diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /nsprpub/pr | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nsprpub/pr')
470 files changed, 154731 insertions, 0 deletions
diff --git a/nsprpub/pr/Makefile.in b/nsprpub/pr/Makefile.in new file mode 100644 index 0000000000..f7af4023f1 --- /dev/null +++ b/nsprpub/pr/Makefile.in @@ -0,0 +1,17 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = .. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +DIRS = include src + +include $(topsrcdir)/config/rules.mk diff --git a/nsprpub/pr/include/Makefile.in b/nsprpub/pr/include/Makefile.in new file mode 100644 index 0000000000..9cc425a84f --- /dev/null +++ b/nsprpub/pr/include/Makefile.in @@ -0,0 +1,27 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +DIRS = md private obsolete + +include $(topsrcdir)/config/config.mk + +HEADERS = $(wildcard $(srcdir)/*.h) + +RELEASE_HEADERS = $(HEADERS) +RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR) + +include $(topsrcdir)/config/rules.mk + +export:: $(RELEASE_HEADERS) + $(INSTALL) -m 444 $(RELEASE_HEADERS) $(dist_includedir) diff --git a/nsprpub/pr/include/gencfg.c b/nsprpub/pr/include/gencfg.c new file mode 100644 index 0000000000..3a67e9113e --- /dev/null +++ b/nsprpub/pr/include/gencfg.c @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdio.h> + +#if defined(__sun) +#ifndef SOLARIS +error - SOLARIS is not defined +#endif +#endif + +#if defined(__hpux) +#ifndef HPUX +error - HPUX is not defined +#endif +#endif + +#if defined(__alpha) +#if !(defined(_WIN32)) && !(defined(__linux)) && !(defined(__FreeBSD__)) +error - None of _WIN32, __linux, or __FreeBSD__ is defined +#endif +#endif + +#if defined(_IBMR2) +#ifndef AIX +error - AIX is not defined +#endif +#endif + +#if defined(linux) +#ifndef LINUX +error - LINUX is not defined +#endif +#endif + +#if defined(bsdi) +#ifndef BSDI +error - BSDI is not defined +#endif +#endif + +#if defined(M_UNIX) +#ifndef SCO +error - SCO is not defined +#endif +#endif +#if !defined(M_UNIX) && defined(_USLC_) +#ifndef UNIXWARE +error - UNIXWARE is not defined +#endif +#endif + +#if defined(__APPLE__) +#ifndef DARWIN +error - DARWIN is not defined +#endif +#endif + +/************************************************************************/ + +/* Generate cpucfg.h */ + +#ifdef XP_PC +#ifdef WIN32 +#define INT64 _PRInt64 +#else +#define INT64 long +#endif +#else +#if defined(HPUX) || defined(SCO) || defined(UNIXWARE) +#define INT64 long +#else +#define INT64 long long +#endif +#endif + +struct align_short { + char c; + short a; +}; +struct align_int { + char c; + int a; +}; +struct align_long { + char c; + long a; +}; +struct align_PRInt64 { + char c; + INT64 a; +}; +struct align_fakelonglong { + char c; + struct { + long hi, lo; + } a; +}; +struct align_float { + char c; + float a; +}; +struct align_double { + char c; + double a; +}; +struct align_pointer { + char c; + void *a; +}; + +#define ALIGN_OF(type) \ + (((char*)&(((struct align_##type *)0)->a)) - ((char*)0)) + +int bpb; + +/* Used if shell doesn't support redirection. By default, assume it does. */ +FILE *stream; + +static int Log2(int n) +{ + int log2 = 0; + + if (n & (n-1)) { + log2++; + } + if (n >> 16) { + log2 += 16, n >>= 16; + } + if (n >> 8) { + log2 += 8, n >>= 8; + } + if (n >> 4) { + log2 += 4, n >>= 4; + } + if (n >> 2) { + log2 += 2, n >>= 2; + } + if (n >> 1) { + log2++; + } + return log2; +} + +/* We assume that int's are 32 bits */ +static void do64(void) +{ + union { + int i; + char c[4]; + } u; + + u.i = 0x01020304; + if (u.c[0] == 0x01) { + fprintf(stream, "#undef IS_LITTLE_ENDIAN\n"); + fprintf(stream, "#define IS_BIG_ENDIAN 1\n\n"); + } else { + fprintf(stream, "#define IS_LITTLE_ENDIAN 1\n"); + fprintf(stream, "#undef IS_BIG_ENDIAN\n\n"); + } +} + +static void do32(void) +{ + union { + long i; + char c[4]; + } u; + + u.i = 0x01020304; + if (u.c[0] == 0x01) { + fprintf(stream, "#undef IS_LITTLE_ENDIAN\n"); + fprintf(stream, "#define IS_BIG_ENDIAN 1\n\n"); + } else { + fprintf(stream, "#define IS_LITTLE_ENDIAN 1\n"); + fprintf(stream, "#undef IS_BIG_ENDIAN\n\n"); + } +} + +/* +** Concievably this could actually be used; but there is lots of code out +** there with and's and shift's in it that assumes a byte is 8 bits, so +** forget about porting THIS code to those non 8 bit byte machines. +*/ +static void BitsPerByte(void) +{ + bpb = 8; +} + +int main(int argc, char **argv) +{ + BitsPerByte(); + + /* If we got a command line argument, try to use it as the stream. */ + ++argv; + if(*argv) { + if(!(stream = fopen ( *argv, "wt" ))) { + fprintf(stderr, "Could not write to output file %s.\n", *argv); + return 1; + } + } else { + stream = stdout; + } + + fprintf(stream, "#ifndef nspr_cpucfg___\n"); + fprintf(stream, "#define nspr_cpucfg___\n\n"); + + fprintf(stream, "/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n"); + + if (sizeof(long) == 8) { + do64(); + } else { + do32(); + } + fprintf(stream, "#define PR_BYTES_PER_BYTE %d\n", sizeof(char)); + fprintf(stream, "#define PR_BYTES_PER_SHORT %d\n", sizeof(short)); + fprintf(stream, "#define PR_BYTES_PER_INT %d\n", sizeof(int)); + fprintf(stream, "#define PR_BYTES_PER_INT64 %d\n", 8); + fprintf(stream, "#define PR_BYTES_PER_LONG %d\n", sizeof(long)); + fprintf(stream, "#define PR_BYTES_PER_FLOAT %d\n", sizeof(float)); + fprintf(stream, "#define PR_BYTES_PER_DOUBLE %d\n\n", sizeof(double)); + + fprintf(stream, "#define PR_BITS_PER_BYTE %d\n", bpb); + fprintf(stream, "#define PR_BITS_PER_SHORT %d\n", bpb * sizeof(short)); + fprintf(stream, "#define PR_BITS_PER_INT %d\n", bpb * sizeof(int)); + fprintf(stream, "#define PR_BITS_PER_INT64 %d\n", bpb * 8); + fprintf(stream, "#define PR_BITS_PER_LONG %d\n", bpb * sizeof(long)); + fprintf(stream, "#define PR_BITS_PER_FLOAT %d\n", bpb * sizeof(float)); + fprintf(stream, "#define PR_BITS_PER_DOUBLE %d\n\n", + bpb * sizeof(double)); + + fprintf(stream, "#define PR_BITS_PER_BYTE_LOG2 %d\n", Log2(bpb)); + fprintf(stream, "#define PR_BITS_PER_SHORT_LOG2 %d\n", + Log2(bpb * sizeof(short))); + fprintf(stream, "#define PR_BITS_PER_INT_LOG2 %d\n", + Log2(bpb * sizeof(int))); + fprintf(stream, "#define PR_BITS_PER_INT64_LOG2 %d\n", 6); + fprintf(stream, "#define PR_BITS_PER_LONG_LOG2 %d\n", + Log2(bpb * sizeof(long))); + fprintf(stream, "#define PR_BITS_PER_FLOAT_LOG2 %d\n", + Log2(bpb * sizeof(float))); + fprintf(stream, "#define PR_BITS_PER_DOUBLE_LOG2 %d\n\n", + Log2(bpb * sizeof(double))); + + fprintf(stream, "#define PR_ALIGN_OF_SHORT %d\n", ALIGN_OF(short)); + fprintf(stream, "#define PR_ALIGN_OF_INT %d\n", ALIGN_OF(int)); + fprintf(stream, "#define PR_ALIGN_OF_LONG %d\n", ALIGN_OF(long)); + if (sizeof(INT64) < 8) { + /* this machine doesn't actually support PRInt64's */ + fprintf(stream, "#define PR_ALIGN_OF_INT64 %d\n", + ALIGN_OF(fakelonglong)); + } else { + fprintf(stream, "#define PR_ALIGN_OF_INT64 %d\n", ALIGN_OF(PRInt64)); + } + fprintf(stream, "#define PR_ALIGN_OF_FLOAT %d\n", ALIGN_OF(float)); + fprintf(stream, "#define PR_ALIGN_OF_DOUBLE %d\n", ALIGN_OF(double)); + fprintf(stream, "#define PR_ALIGN_OF_POINTER %d\n\n", ALIGN_OF(pointer)); + + fprintf(stream, "#endif /* nspr_cpucfg___ */\n"); + fclose(stream); + + return 0; +} diff --git a/nsprpub/pr/include/md/Makefile.in b/nsprpub/pr/include/md/Makefile.in new file mode 100644 index 0000000000..383c092cf8 --- /dev/null +++ b/nsprpub/pr/include/md/Makefile.in @@ -0,0 +1,42 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +# The .cfg files need to be exported and installed to support +# cross-compilation. +CONFIGS = $(wildcard $(srcdir)/*.cfg) + +include $(topsrcdir)/config/rules.mk + +export:: $(MDCPUCFG_H) + $(INSTALL) -m 444 $(CONFIGS) $(dist_includedir)/md + $(INSTALL) -m 444 $(srcdir)/$(MDCPUCFG_H) $(dist_includedir) + mv -f $(dist_includedir)/$(MDCPUCFG_H) $(dist_includedir)/prcpucfg.h + +install:: + $(NSINSTALL) -D $(DESTDIR)$(includedir)/md + $(NSINSTALL) -t -m 644 $(CONFIGS) $(DESTDIR)$(includedir)/md + $(NSINSTALL) -t -m 644 $(srcdir)/$(MDCPUCFG_H) $(DESTDIR)$(includedir) + mv -f $(DESTDIR)$(includedir)/$(MDCPUCFG_H) $(DESTDIR)$(includedir)/prcpucfg.h + +release:: export + @echo "Copying machine-dependent prcpucfg.h" + @if test -z "$(BUILD_NUMBER)"; then \ + echo "BUILD_NUMBER must be defined"; \ + false; \ + fi + @if test ! -d $(RELEASE_INCLUDE_DIR); then \ + rm -rf $(RELEASE_INCLUDE_DIR); \ + $(NSINSTALL) -D $(RELEASE_INCLUDE_DIR);\ + fi + cp $(srcdir)/$(MDCPUCFG_H) $(RELEASE_INCLUDE_DIR)/prcpucfg.h diff --git a/nsprpub/pr/include/md/_aix.h b/nsprpub/pr/include/md/_aix.h new file mode 100644 index 0000000000..c2794d3368 --- /dev/null +++ b/nsprpub/pr/include/md/_aix.h @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_aix_defs_h___ +#define nspr_aix_defs_h___ + +#include <sys/types.h> +#if defined(_PR_PTHREADS) || defined(PTHREADS_USER) +#include <pthread.h> +#endif + +/* + * To pick up fd_set and the poll events. + */ +#include <sys/select.h> +#include <sys/poll.h> + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "aix" +#define _PR_SI_SYSNAME "AIX" +#define _PR_SI_ARCHITECTURE "rs6000" +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE (2*65536L) +#define _MD_MINIMUM_STACK_SIZE (2*65536L) +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#define NEED_TIME_R +#undef HAVE_STACK_GROWING_UP +#undef HAVE_WEAK_IO_SYMBOLS +#undef HAVE_WEAK_MALLOC_SYMBOLS +#define HAVE_DLL +#define USE_DLFCN +#define _PR_HAVE_SOCKADDR_LEN +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_STAT_HAS_ONLY_ST_ATIME +#ifdef _PR_INET6 +#define _PR_HAVE_INET_NTOP +#define _PR_HAVE_GETHOSTBYNAME2 +#ifdef _AIX51 /* AIX 4.3.3 does not have AI_NUMERICHOST. */ +#define _PR_HAVE_GETADDRINFO +#endif +#endif +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY +#define _PR_ACCEPT_INHERIT_NONBLOCK + +/* Timer operations */ +#if defined(AIX_TIMERS) +#define _MD_INTERVAL_INIT() + +extern PRIntervalTime _MD_AixGetInterval(void); +#define _MD_GET_INTERVAL _MD_AixGetInterval + +extern PRIntervalTime _MD_AixIntervalPerSec(void); +#define _MD_INTERVAL_PER_SEC _MD_AixIntervalPerSec + +#else /* defined(AIX_TIMERS) */ +#define _MD_INTERVAL_USE_GTOD +#endif /* defined(AIX_TIMERS) */ + +#ifdef AIX_HAVE_ATOMIC_OP_H +/* 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) +#define _MD_ATOMIC_DECREMENT(val) ((PRInt32)fetch_and_add((atomic_p)val, -1) - 1) +#define _MD_ATOMIC_SET(val, newval) _AIX_AtomicSet(val, newval) +#endif /* AIX_HAVE_ATOMIC_OP_H */ + +#define USE_SETJMP + +#include <setjmp.h> + +#define _MD_GET_SP(_t) (_t)->md.jb[3] +#define _MD_SET_THR_SP(_t, _sp) ((_t)->md.jb[3] = (int) (_sp - 2 * 64)) +#define PR_NUM_GCREGS _JBLEN + +#define CONTEXT(_th) ((_th)->md.jb) +#define SAVE_CONTEXT(_th) _setjmp(CONTEXT(_th)) +#define GOTO_CONTEXT(_th) _longjmp(CONTEXT(_th), 1) + +#ifdef PTHREADS_USER +#include "_nspr_pthread.h" +#else + +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + *status = PR_TRUE; \ + if (setjmp(CONTEXT(_thread))) { \ + (*_main)(); \ + } \ + _MD_GET_SP(_thread) = (int) (_sp - 2 * 64); \ + PR_END_MACRO + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!setjmp(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + longjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + jmp_buf jb; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#if !defined(_PR_PTHREADS) +#define _MD_INIT_LOCKS() +#endif + +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) +#endif /* PTHREADS_USER */ + +#ifdef AIX_RENAME_SELECT +#define _MD_SELECT select +#define _MD_POLL poll +#endif + +extern void _MD_aix_map_sendfile_error(int err); + +#endif /* nspr_aix_defs_h___ */ diff --git a/nsprpub/pr/include/md/_aix32.cfg b/nsprpub/pr/include/md/_aix32.cfg new file mode 100644 index 0000000000..23a3f5125f --- /dev/null +++ b/nsprpub/pr/include/md/_aix32.cfg @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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 + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#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 + +/* used by protypes.h only */ +#define _PR_AIX_HAVE_BSD_INT_TYPES + +#endif /* NO_NSPR_10_SUPPORT */ + +#endif /* nspr_cpucfg___ */ diff --git a/nsprpub/pr/include/md/_aix64.cfg b/nsprpub/pr/include/md/_aix64.cfg new file mode 100644 index 0000000000..7efe896d1e --- /dev/null +++ b/nsprpub/pr/include/md/_aix64.cfg @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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 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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 8 + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#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 + +/* used by protypes.h only */ +#define _PR_AIX_HAVE_BSD_INT_TYPES + +#endif /* NO_NSPR_10_SUPPORT */ + +#endif /* nspr_cpucfg___ */ diff --git a/nsprpub/pr/include/md/_bsdi.cfg b/nsprpub/pr/include/md/_bsdi.cfg new file mode 100644 index 0000000000..130cf820b0 --- /dev/null +++ b/nsprpub/pr/include/md/_bsdi.cfg @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef BSDI +#define BSDI +#endif + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#if defined(__i386__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__sparc__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#error "Unknown CPU architecture" + +#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/nsprpub/pr/include/md/_bsdi.h b/nsprpub/pr/include/md/_bsdi.h new file mode 100644 index 0000000000..99655dc3f2 --- /dev/null +++ b/nsprpub/pr/include/md/_bsdi.h @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_bsdi_defs_h___ +#define nspr_bsdi_defs_h___ + +/* + * Internal configuration macros + */ + +#include <sys/param.h> /* for _BSDI_VERSION */ + +#define PR_LINKER_ARCH "bsdi" +#define _PR_SI_SYSNAME "BSDI" +#if defined(__i386__) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(__sparc__) +#define _PR_SI_ARCHITECTURE "sparc" +#else +#error "Unknown CPU architecture" +#endif +#define PR_DLL_SUFFIX ".so" + +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#define HAVE_BSD_FLOCK +#define NEED_TIME_R +#define _PR_HAVE_SOCKADDR_LEN +#define _PR_NO_LARGE_FILES + +#define USE_SETJMP + +/* BSD/OS 4.3 and newer all have IPv6 support */ +#if _BSDI_VERSION >= 200105 +#define _PR_INET6 +#define _PR_HAVE_INET_NTOP +#define _PR_HAVE_GETIPNODEBYNAME +#define _PR_HAVE_GETIPNODEBYADDR +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#endif + +#ifndef _PR_PTHREADS + +#include <setjmp.h> + +#if defined(_PR_BSDI_JMPBUF_IS_ARRAY) +#define _MD_GET_SP(_t) (_t)->md.context[2] +#elif defined(_PR_BSDI_JMPBUF_IS_STRUCT) +#define _MD_GET_SP(_t) (_t)->md.context[0].jb_esp +#else +#error "Unknown BSDI jmp_buf type" +#endif + +#define PR_NUM_GCREGS _JBLEN +#define PR_CONTEXT_TYPE jmp_buf + +#define CONTEXT(_th) ((_th)->md.context) + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (setjmp(CONTEXT(_thread))) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (int) (_sp - 64); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!setjmp(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + longjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +#endif /* ! _PR_PTHREADS */ + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit + +#include <sys/syscall.h> +#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv) + +#define _MD_INTERVAL_USE_GTOD + +#endif /* nspr_bsdi_defs_h___ */ diff --git a/nsprpub/pr/include/md/_darwin.cfg b/nsprpub/pr/include/md/_darwin.cfg new file mode 100644 index 0000000000..5e11893315 --- /dev/null +++ b/nsprpub/pr/include/md/_darwin.cfg @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#define PR_AF_INET6 30 /* same as AF_INET6 */ + +#ifdef __LITTLE_ENDIAN__ +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#else +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#endif + +#ifdef __LP64__ +#define IS_64 +#endif + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#undef HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS 1 + +#ifdef 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_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_DWORD 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_BITS_PER_DWORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 +#define PR_ALIGN_OF_DWORD 8 + +#else /* 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 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_DWORD 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 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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#endif /* IS_64 */ + +#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/nsprpub/pr/include/md/_darwin.h b/nsprpub/pr/include/md/_darwin.h new file mode 100644 index 0000000000..b3f89fbf8d --- /dev/null +++ b/nsprpub/pr/include/md/_darwin.h @@ -0,0 +1,308 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_darwin_defs_h___ +#define nspr_darwin_defs_h___ + +#include "prthread.h" + +#include <libkern/OSAtomic.h> +#include <sys/syscall.h> + +#ifdef __APPLE__ +#include <AvailabilityMacros.h> +#include <TargetConditionals.h> +#endif + +#define PR_LINKER_ARCH "darwin" +#define _PR_SI_SYSNAME "DARWIN" +#ifdef __i386__ +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(__x86_64__) +#define _PR_SI_ARCHITECTURE "x86-64" +#elif defined(__ppc__) +#define _PR_SI_ARCHITECTURE "ppc" +#elif defined(__arm__) +#define _PR_SI_ARCHITECTURE "arm" +#elif defined(__aarch64__) +#define _PR_SI_ARCHITECTURE "aarch64" +#else +#error "Unknown CPU architecture" +#endif +#define PR_DLL_SUFFIX ".dylib" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN +#define _PR_HAVE_SOCKADDR_LEN +#define _PR_STAT_HAS_ST_ATIMESPEC +#define _PR_HAVE_LARGE_OFF_T +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY + +#define _PR_INET6 +/* + * I'd prefer to use getipnodebyname and getipnodebyaddr but the + * getipnodebyname(3) man page on Mac OS X 10.2 says they are not + * thread-safe. AI_V4MAPPED|AI_ADDRCONFIG doesn't work either. + */ +#define _PR_HAVE_GETHOSTBYNAME2 +#define _PR_HAVE_GETADDRINFO +/* + * On Mac OS X 10.2, gethostbyaddr fails with h_errno=NO_RECOVERY + * if you pass an IPv4-mapped IPv6 address to it. + */ +#define _PR_GHBA_DISALLOW_V4MAPPED +#ifdef __APPLE__ +#if !defined(MAC_OS_X_VERSION_10_3) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3 +/* + * socket(AF_INET6) fails with EPROTONOSUPPORT on Mac OS X 10.1. + * IPv6 under OS X 10.2 and below is not complete (see bug 222031). + */ +#define _PR_INET6_PROBE +#endif /* DT < 10.3 */ +#if defined(MAC_OS_X_VERSION_10_2) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_2 +/* Mac OS X 10.2 has inet_ntop and inet_pton. */ +#define _PR_HAVE_INET_NTOP +#endif /* DT >= 10.2 */ +#endif /* __APPLE__ */ +#define _PR_IPV6_V6ONLY_PROBE +/* The IPV6_V6ONLY socket option is not defined on Mac OS X 10.1. */ +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif + +#ifdef __ppc__ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_DarwinPPC_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT(val) _PR_DarwinPPC_AtomicIncrement(val) +extern PRInt32 _PR_DarwinPPC_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT(val) _PR_DarwinPPC_AtomicDecrement(val) +extern PRInt32 _PR_DarwinPPC_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET(val, newval) _PR_DarwinPPC_AtomicSet(val, newval) +extern PRInt32 _PR_DarwinPPC_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD(ptr, val) _PR_DarwinPPC_AtomicAdd(ptr, val) +#endif /* __ppc__ */ + +#ifdef __i386__ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_Darwin_x86_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT(val) _PR_Darwin_x86_AtomicIncrement(val) +extern PRInt32 _PR_Darwin_x86_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT(val) _PR_Darwin_x86_AtomicDecrement(val) +extern PRInt32 _PR_Darwin_x86_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET(val, newval) _PR_Darwin_x86_AtomicSet(val, newval) +extern PRInt32 _PR_Darwin_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD(ptr, val) _PR_Darwin_x86_AtomicAdd(ptr, val) +#endif /* __i386__ */ + +#ifdef __x86_64__ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_Darwin_x86_64_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT(val) _PR_Darwin_x86_64_AtomicIncrement(val) +extern PRInt32 _PR_Darwin_x86_64_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT(val) _PR_Darwin_x86_64_AtomicDecrement(val) +extern PRInt32 _PR_Darwin_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET(val, newval) _PR_Darwin_x86_64_AtomicSet(val, newval) +extern PRInt32 _PR_Darwin_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD(ptr, val) _PR_Darwin_x86_64_AtomicAdd(ptr, val) +#endif /* __x86_64__ */ + +#ifdef __aarch64__ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT(val) __sync_add_and_fetch(val, 1) +#define _MD_ATOMIC_DECREMENT(val) __sync_sub_and_fetch(val, 1) +#define _MD_ATOMIC_SET(val, newval) __sync_lock_test_and_set(val, newval) +#define _MD_ATOMIC_ADD(ptr, val) __sync_add_and_fetch(ptr, val) +#endif /* __aarch64__ */ + +#if defined(__arm__) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT(val) OSAtomicIncrement32(val) +#define _MD_ATOMIC_DECREMENT(val) OSAtomicDecrement32(val) +static inline PRInt32 _MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + PRInt32 oldval; + do { + oldval = *val; + } while (!OSAtomicCompareAndSwap32(oldval, newval, val)); + return oldval; +} +#define _MD_ATOMIC_ADD(ptr, val) OSAtomicAdd32(val, ptr) +#endif /* __arm__ */ + +#define USE_SETJMP + +#if !defined(_PR_PTHREADS) + +#include <setjmp.h> + +#define PR_CONTEXT_TYPE jmp_buf + +#define CONTEXT(_th) ((_th)->md.context) +#define _MD_GET_SP(_th) (((struct sigcontext *) (_th)->md.context)->sc_onstack) +#define PR_NUM_GCREGS _JBLEN + +/* +** Initialize a thread context to run "_main()" when started +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (setjmp(CONTEXT(_thread))) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!setjmp(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + longjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +extern PRStatus _MD_InitializeThread(PRThread *thread); + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread +#define _MD_RESUME_THREAD(thread) _MD_resume_thread +#define _MD_CLEAN_THREAD(_thread) + +extern PRStatus _MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); +extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri); +extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout); +extern PRStatus _MD_WAKEUP_WAITER(PRThread *); +extern void _MD_YIELD(void); + +#endif /* ! _PR_PTHREADS */ + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INTERVAL_INIT _PR_Mach_IntervalInit +#define _MD_GET_INTERVAL _PR_Mach_GetInterval +#define _MD_INTERVAL_PER_SEC _PR_Mach_TicksPerSecond + +extern void _MD_EarlyInit(void); +extern void _PR_Mach_IntervalInit(void); +extern PRIntervalTime _PR_Mach_GetInterval(void); +extern PRIntervalTime _PR_Mach_TicksPerSecond(void); + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv) + +/* For writev() */ +#include <sys/uio.h> + +#endif /* nspr_darwin_defs_h___ */ diff --git a/nsprpub/pr/include/md/_freebsd.cfg b/nsprpub/pr/include/md/_freebsd.cfg new file mode 100644 index 0000000000..1d1039a5b8 --- /dev/null +++ b/nsprpub/pr/include/md/_freebsd.cfg @@ -0,0 +1,594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef FREEBSD +#define FREEBSD +#endif + +#define PR_AF_INET6 28 /* same as AF_INET6 */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif + +#if defined(__i386__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#elif defined(__alpha__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#elif defined(__sparc__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#elif defined(__ia64__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#elif defined(__amd64__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#elif defined(__powerpc64__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__powerpc__) + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__aarch64__) + +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__arm__) + +#if defined(__ARMEB__) || defined(__ARM_BIG_ENDIAN__) +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#else +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#endif + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__mips64__) + +#if defined(__MIPSEB__) || defined(_MIPSEB) +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#else +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__mips__) + +#if defined(__MIPSEB__) || defined(_MIPSEB) +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#else +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#endif + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#error "Unknown CPU architecture" + +#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/nsprpub/pr/include/md/_freebsd.h b/nsprpub/pr/include/md/_freebsd.h new file mode 100644 index 0000000000..118cb9f933 --- /dev/null +++ b/nsprpub/pr/include/md/_freebsd.h @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_freebsd_defs_h___ +#define nspr_freebsd_defs_h___ + +#include "prthread.h" + +#if __FreeBSD__ >= 2 +#include <osreldate.h> /* for __FreeBSD_version */ +#endif +#include <sys/syscall.h> + +#define PR_LINKER_ARCH "freebsd" +#define _PR_SI_SYSNAME "FREEBSD" +#if defined(__i386__) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(__alpha__) +#define _PR_SI_ARCHITECTURE "alpha" +#elif defined(__sparc__) +#define _PR_SI_ARCHITECTURE "sparc" +#elif defined(__ia64__) +#define _PR_SI_ARCHITECTURE "ia64" +#elif defined(__amd64__) +#define _PR_SI_ARCHITECTURE "amd64" +#elif defined(__powerpc64__) +#define _PR_SI_ARCHITECTURE "powerpc64" +#elif defined(__powerpc__) +#define _PR_SI_ARCHITECTURE "powerpc" +#elif defined(__aarch64__) +#define _PR_SI_ARCHITECTURE "aarch64" +#elif defined(__arm__) +#define _PR_SI_ARCHITECTURE "arm" +#elif defined(__mips64__) +#define _PR_SI_ARCHITECTURE "mips64" +#elif defined(__mips__) +#define _PR_SI_ARCHITECTURE "mips" +#else +#error "Unknown CPU architecture" +#endif +#if defined(__ELF__) +#define PR_DLL_SUFFIX ".so" +#else +#define PR_DLL_SUFFIX ".so.1.0" +#endif + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN +#define _PR_HAVE_SOCKADDR_LEN +#define _PR_STAT_HAS_ST_ATIMESPEC +#define _PR_HAVE_LARGE_OFF_T + +#if defined(_PR_PTHREADS) +#if __FreeBSD_version >= 400008 +/* + * libc_r before this version of FreeBSD doesn't have poll(). + * Although libc has poll(), it is not thread-safe so we can't + * use it in the pthreads version. + */ +#define _PR_POLL_AVAILABLE +#endif +#else +#if __FreeBSD_version >= 300000 +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#endif +#endif + +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY + +#if __FreeBSD_version >= 400014 +#define _PR_INET6 +#define _PR_HAVE_INET_NTOP +#define _PR_HAVE_GETHOSTBYNAME2 +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#define _PR_IPV6_V6ONLY_PROBE +#endif + +#define USE_SETJMP + +#ifndef _PR_PTHREADS +#include <setjmp.h> + +#define PR_CONTEXT_TYPE sigjmp_buf + +#define CONTEXT(_th) ((_th)->md.context) + +#define _MD_GET_SP(_th) (_th)->md.context[0]._sjb[2] +#define PR_NUM_GCREGS _JBLEN + +/* +** Initialize a thread context to run "_main()" when started +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (sigsetjmp(CONTEXT(_thread), 1)) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!sigsetjmp(CONTEXT(_thread), 1)) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + siglongjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +extern PRStatus _MD_InitializeThread(PRThread *thread); + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread +#define _MD_RESUME_THREAD(thread) _MD_resume_thread +#define _MD_CLEAN_THREAD(_thread) + +extern PRStatus _MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); +extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri); +extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout); +extern PRStatus _MD_WAKEUP_WAITER(PRThread *); +extern void _MD_YIELD(void); + +#endif /* ! _PR_PTHREADS */ + +extern void _MD_EarlyInit(void); + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _PR_HAVE_CLOCK_MONOTONIC + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv) + +#if defined(_PR_POLL_AVAILABLE) +#include <poll.h> +#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout) +#endif + +/* freebsd has INADDR_LOOPBACK defined, but in /usr/include/rpc/types.h, and I didn't + want to be including that.. */ +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (u_long)0x7F000001 +#endif + +/* For writev() */ +#include <sys/uio.h> + +#endif /* nspr_freebsd_defs_h___ */ diff --git a/nsprpub/pr/include/md/_hpux.h b/nsprpub/pr/include/md/_hpux.h new file mode 100644 index 0000000000..2d8017e972 --- /dev/null +++ b/nsprpub/pr/include/md/_hpux.h @@ -0,0 +1,288 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_xhppa_defs_h___ +#define nspr_xhppa_defs_h___ + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "hpux" +#define _PR_SI_SYSNAME "HPUX" +#ifdef __ia64 +#define _PR_SI_ARCHITECTURE "ia64" +#define PR_DLL_SUFFIX ".so" +#else +/* + * _PR_SI_ARCHITECTURE must be "hppa1.1" for backward compatibility. + * It was changed to "hppa" in NSPR 4.6.2, but was changed back in + * NSPR 4.6.4. + */ +#define _PR_SI_ARCHITECTURE "hppa1.1" +#define PR_DLL_SUFFIX ".sl" +#endif + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +/* + * _USE_BIG_FDS increases the size of fd_set from 256 bytes to + * about 7500 bytes. PR_Poll allocates three fd_sets on the + * stack, so it is safer to also increase the default thread + * stack size. + */ +#define _MD_DEFAULT_STACK_SIZE (2*65536L) +#define _MD_MINIMUM_STACK_SIZE (2*65536L) +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#define NEED_TIME_R + +#define HAVE_STACK_GROWING_UP +#undef HAVE_WEAK_IO_SYMBOLS +#undef HAVE_WEAK_MALLOC_SYMBOLS +#define HAVE_DLL +#ifdef IS_64 +#define USE_DLFCN +#else +#define USE_HPSHL +#endif +#ifndef HAVE_STRERROR +#define HAVE_STRERROR +#endif +#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 +#define _PR_ACCEPT_INHERIT_NONBLOCK + +#if defined(__ia64) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT _PR_ia64_AtomicIncrement +extern PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT _PR_ia64_AtomicDecrement +extern PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD _PR_ia64_AtomicAdd +extern PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET _PR_ia64_AtomicSet +#endif + +#define _PR_HAVE_GETIPNODEBYNAME +#define _PR_HAVE_GETIPNODEBYADDR +#define _PR_HAVE_GETADDRINFO +#ifdef _PR_INET6 +#define _PR_HAVE_INET_NTOP +#else +#define _PR_INET6_PROBE + +/* for HP-UX 11.11 without IPv6 */ +#ifndef AF_INET6 +#define AF_INET6 22 +#define AI_CANONNAME 2 +#define AI_NUMERICHOST 4 +#define AI_NUMERICSERV 8 +#define AI_V4MAPPED 0x00000010 +#define AI_ADDRCONFIG 0x00000040 +#define AI_ALL 0x00000020 +#define AI_DEFAULT (AI_V4MAPPED|AI_ADDRCONFIG) +#define NI_NUMERICHOST 2 +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* IPPROTO_xxx for IPv4 and IPv6 */ + socklen_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for host */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif /* for HP-UX 11.11 without IPv6 */ + +#define _PR_HAVE_MD_SOCKADDR_IN6 +/* isomorphic to struct in6_addr on HP-UX B.11.23 */ +struct _md_in6_addr { + union { + PRUint8 _S6_u8[16]; + PRUint16 _S6_u16[8]; + PRUint32 _S6_u32[4]; + PRUint32 __S6_align; + } _s6_un; +}; +/* isomorphic to struct sockaddr_in6 on HP-UX B.11.23 */ +struct _md_sockaddr_in6 { + PRUint16 sin6_family; + PRUint16 sin6_port; + PRUint32 sin6_flowinfo; + struct _md_in6_addr sin6_addr; + PRUint32 sin6_scope_id; +}; +#endif + +#if !defined(_PR_PTHREADS) + +#include <syscall.h> +#include <setjmp.h> + +#define USE_SETJMP + +#define _MD_GET_SP(_t) (*((int *)((_t)->md.jb) + 1)) +#define PR_NUM_GCREGS _JBLEN +/* Caveat: This makes jmp_buf full of doubles. */ +#define CONTEXT(_th) ((_th)->md.jb) + +/* Stack needs two frames (64 bytes) at the bottom */ \ +#define _MD_SET_THR_SP(_t, _sp) ((_MD_GET_SP(_t)) = (int) (_sp + 64 *2)) +#define SAVE_CONTEXT(_th) _setjmp(CONTEXT(_th)) +#define GOTO_CONTEXT(_th) _longjmp(CONTEXT(_th), 1) + +#if !defined(PTHREADS_USER) + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *(status) = PR_TRUE; \ + if (_setjmp(CONTEXT(_thread))) (*_main)(); \ + /* Stack needs two frames (64 bytes) at the bottom */ \ + (_MD_GET_SP(_thread)) = (int) ((_sp) + 64*2); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!_setjmp(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + _longjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures. HP-UX has no native threads. */ + +struct _MDThread { + jmp_buf jb; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread +#define _MD_RESUME_THREAD(thread) _MD_resume_thread +#define _MD_CLEAN_THREAD(_thread) + +#else /* PTHREADS_USER */ + +#include "_nspr_pthread.h" + +#endif /* PTHREADS_USER */ + +#endif /* !defined(_PR_PTHREADS) */ + +#if !defined(PTHREADS_USER) +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#endif + +#if defined(HPUX_LW_TIMER) +extern void _PR_HPUX_LW_IntervalInit(void); +extern PRIntervalTime _PR_HPUX_LW_GetInterval(void); +#define _MD_INTERVAL_INIT _PR_HPUX_LW_IntervalInit +#define _MD_GET_INTERVAL _PR_HPUX_LW_GetInterval +#define _MD_INTERVAL_PER_SEC() 1000 +#else +#define _MD_INTERVAL_USE_GTOD +#endif + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv) + +#include <poll.h> +#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout) + +#ifdef HPUX11 +extern void _MD_hpux_map_sendfile_error(int err); +#endif /* HPUX11 */ + +#endif /* nspr_xhppa_defs_h___ */ diff --git a/nsprpub/pr/include/md/_hpux32.cfg b/nsprpub/pr/include/md/_hpux32.cfg new file mode 100644 index 0000000000..08950cbf13 --- /dev/null +++ b/nsprpub/pr/include/md/_hpux32.cfg @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef HPUX +#define HPUX +#endif + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_AF_INET6 22 /* same as AF_INET6 */ + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#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 /* nspr_cpucfg___ */ diff --git a/nsprpub/pr/include/md/_hpux64.cfg b/nsprpub/pr/include/md/_hpux64.cfg new file mode 100644 index 0000000000..028a034f68 --- /dev/null +++ b/nsprpub/pr/include/md/_hpux64.cfg @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef HPUX +#define HPUX +#endif + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define IS_64 + +#define PR_AF_INET6 22 /* same as AF_INET6 */ + +#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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#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 /* nspr_cpucfg___ */ diff --git a/nsprpub/pr/include/md/_linux.cfg b/nsprpub/pr/include/md/_linux.cfg new file mode 100644 index 0000000000..2232820afe --- /dev/null +++ b/nsprpub/pr/include/md/_linux.cfg @@ -0,0 +1,1365 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file is used by not only Linux but also other glibc systems + * such as GNU/Hurd and GNU/k*BSD. + */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#if !defined(LINUX) && defined(__linux__) +#define LINUX +#endif + +#ifdef __FreeBSD_kernel__ +#define PR_AF_INET6 28 /* same as AF_INET6 */ +#elif defined(__GNU__) +#define PR_AF_INET6 26 /* same as AF_INET6 */ +#else +#define PR_AF_INET6 10 /* same as AF_INET6 */ +#endif + +#ifdef __powerpc64__ + +#ifdef __LITTLE_ENDIAN__ +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#else +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__powerpc__) + +#ifdef __LITTLE_ENDIAN__ +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#else +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#endif + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__alpha) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__ia64__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__x86_64__) + +#ifdef __ILP32__ + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#endif + +#elif defined(__mc68000__) + +#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_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 2 +#define PR_ALIGN_OF_LONG 2 +#define PR_ALIGN_OF_INT64 2 +#define PR_ALIGN_OF_FLOAT 2 +#define PR_ALIGN_OF_DOUBLE 2 +#define PR_ALIGN_OF_POINTER 2 +#define PR_ALIGN_OF_WORD 2 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__sparc__) && defined (__arch64__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__sparc__) + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__i386__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__mips__) + +/* For _ABI64 */ +#include <asm/sgidefs.h> + +#ifdef __MIPSEB__ +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN +#elif defined(__MIPSEL__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#else +#error "Unknown MIPS endianness." +#endif + +#if _MIPS_SIM == _ABI64 + +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else /* _ABI64 */ + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#endif /* _ABI64 */ + +#elif defined(__arm__) + +#ifdef __ARMEB__ +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#elif defined(__ARMEL__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#else +#error "Unknown ARM endianness." +#endif + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__aarch64__) + +#ifdef __AARCH64EB__ +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#elif defined(__AARCH64EL__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#else +#error "Unknown Aarch64 endianness." +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__hppa__) + +#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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__s390x__) + +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__s390__) + +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__sh__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__avr32__) + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__m32r__) + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__or1k__) + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__riscv) && (__riscv_xlen == 32) + +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#undef 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 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_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 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__riscv) && (__riscv_xlen == 64) + +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__arc__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__nios2__) || defined(__microblaze__) || defined(__nds32__) || \ + defined(__xtensa__) + +#if defined(__microblaze__) && defined(__BIG_ENDIAN__) +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN +#else +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#endif + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__e2k__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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 4 +#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_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 32 +#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 5 +#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_INT64 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__loongarch64) + +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#error "Unknown CPU architecture" + +#endif + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#if PR_ALIGN_OF_DOUBLE == 8 +#define HAVE_ALIGNED_DOUBLES +#endif +#if PR_ALIGN_OF_INT64 == 8 +#define HAVE_ALIGNED_LONGLONGS +#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/nsprpub/pr/include/md/_linux.h b/nsprpub/pr/include/md/_linux.h new file mode 100644 index 0000000000..ad57d08330 --- /dev/null +++ b/nsprpub/pr/include/md/_linux.h @@ -0,0 +1,724 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file is used by not only Linux but also other glibc systems + * such as GNU/Hurd and GNU/k*BSD. + */ + +#ifndef nspr_linux_defs_h___ +#define nspr_linux_defs_h___ + +#include "prthread.h" + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "linux" +#define _PR_SI_SYSNAME "LINUX" +#ifdef __powerpc64__ +#define _PR_SI_ARCHITECTURE "ppc64" +#elif defined(__powerpc__) +#define _PR_SI_ARCHITECTURE "ppc" +#elif defined(__alpha) +#define _PR_SI_ARCHITECTURE "alpha" +#elif defined(__ia64__) +#define _PR_SI_ARCHITECTURE "ia64" +#elif defined(__x86_64__) +#define _PR_SI_ARCHITECTURE "x86-64" +#elif defined(__mc68000__) +#define _PR_SI_ARCHITECTURE "m68k" +#elif defined(__sparc__) && defined(__arch64__) +#define _PR_SI_ARCHITECTURE "sparc64" +#elif defined(__sparc__) +#define _PR_SI_ARCHITECTURE "sparc" +#elif defined(__i386__) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(__mips__) +#define _PR_SI_ARCHITECTURE "mips" +#elif defined(__arm__) +#define _PR_SI_ARCHITECTURE "arm" +#elif defined(__aarch64__) +#define _PR_SI_ARCHITECTURE "aarch64" +#elif defined(__hppa__) +#define _PR_SI_ARCHITECTURE "hppa" +#elif defined(__s390x__) +#define _PR_SI_ARCHITECTURE "s390x" +#elif defined(__s390__) +#define _PR_SI_ARCHITECTURE "s390" +#elif defined(__sh__) +#define _PR_SI_ARCHITECTURE "sh" +#elif defined(__avr32__) +#define _PR_SI_ARCHITECTURE "avr32" +#elif defined(__m32r__) +#define _PR_SI_ARCHITECTURE "m32r" +#elif defined(__or1k__) +#define _PR_SI_ARCHITECTURE "or1k" +#elif defined(__riscv) && (__riscv_xlen == 32) +#define _PR_SI_ARCHITECTURE "riscv32" +#elif defined(__riscv) && (__riscv_xlen == 64) +#define _PR_SI_ARCHITECTURE "riscv64" +#elif defined(__e2k__) +#define _PR_SI_ARCHITECTURE "e2k" +#elif defined(__arc__) +#define _PR_SI_ARCHITECTURE "arc" +#elif defined(__nios2__) +#define _PR_SI_ARCHITECTURE "nios2" +#elif defined(__microblaze__) +#define _PR_SI_ARCHITECTURE "microblaze" +#elif defined(__nds32__) +#define _PR_SI_ARCHITECTURE "nds32" +#elif defined(__xtensa__) +#define _PR_SI_ARCHITECTURE "xtensa" +#elif defined(__loongarch64) +#define _PR_SI_ARCHITECTURE "loongarch64" +#else +#error "Unknown CPU architecture" +#endif +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#if defined(__aarch64__) || defined(__mips__) +#define _MD_MINIMUM_STACK_SIZE 0x20000 +#endif + +#undef HAVE_STACK_GROWING_UP + +/* + * Elf linux supports dl* functions + */ +#define HAVE_DLL +#define USE_DLFCN +#if defined(ANDROID) +#define NO_DLOPEN_NULL +#endif + +#if defined(__FreeBSD_kernel__) || defined(__GNU__) +#define _PR_HAVE_SOCKADDR_LEN +#endif + +#if defined(__i386__) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_x86_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT _PR_x86_AtomicIncrement +extern PRInt32 _PR_x86_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT _PR_x86_AtomicDecrement +extern PRInt32 _PR_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD _PR_x86_AtomicAdd +extern PRInt32 _PR_x86_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET _PR_x86_AtomicSet +#endif + +#if defined(__ia64__) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT _PR_ia64_AtomicIncrement +extern PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT _PR_ia64_AtomicDecrement +extern PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD _PR_ia64_AtomicAdd +extern PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET _PR_ia64_AtomicSet +#endif + +#if defined(__x86_64__) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_x86_64_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT _PR_x86_64_AtomicIncrement +extern PRInt32 _PR_x86_64_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT _PR_x86_64_AtomicDecrement +extern PRInt32 _PR_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD _PR_x86_64_AtomicAdd +extern PRInt32 _PR_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET _PR_x86_64_AtomicSet +#endif + +#if defined(__loongarch__) +#if defined(__GNUC__) +/* Use GCC built-in functions */ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1) +#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i) +#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv) +#endif +#endif + +#if defined(__or1k__) +#if defined(__GNUC__) +/* Use GCC built-in functions */ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1) +#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i) +#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv) +#endif +#endif + +#if defined(__powerpc__) && !defined(__powerpc64__) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +extern PRInt32 _PR_ppc_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT _PR_ppc_AtomicIncrement +extern PRInt32 _PR_ppc_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT _PR_ppc_AtomicDecrement +extern PRInt32 _PR_ppc_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD _PR_ppc_AtomicAdd +extern PRInt32 _PR_ppc_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET _PR_ppc_AtomicSet +#endif + +#if defined(__powerpc64__) +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +/* Use GCC built-in functions */ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1) +#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i) +#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv) +#endif +#endif + +#if defined(__mips__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) +/* Use GCC built-in functions */ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1) +#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i) +#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv) +#endif + +#if defined(__alpha) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_ADD(ptr, i) ({ \ + PRInt32 __atomic_tmp, __atomic_ret; \ + __asm__ __volatile__( \ + "1: ldl_l %[ret], %[val] \n" \ + " addl %[ret], %[inc], %[tmp] \n" \ + " addl %[ret], %[inc], %[ret] \n" \ + " stl_c %[tmp], %[val] \n" \ + " beq %[tmp], 2f \n" \ + ".subsection 2 \n" \ + "2: br 1b \n" \ + ".previous" \ + : [ret] "=&r" (__atomic_ret), \ + [tmp] "=&r" (__atomic_tmp), \ + [val] "=m" (*ptr) \ + : [inc] "Ir" (i), "m" (*ptr)); \ + __atomic_ret; \ +}) +#define _MD_ATOMIC_INCREMENT(ptr) _MD_ATOMIC_ADD(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) ({ \ + PRInt32 __atomic_tmp, __atomic_ret; \ + __asm__ __volatile__( \ + "1: ldl_l %[ret], %[val] \n" \ + " subl %[ret], 1, %[tmp] \n" \ + " subl %[ret], 1, %[ret] \n" \ + " stl_c %[tmp], %[val] \n" \ + " beq %[tmp], 2f \n" \ + ".subsection 2 \n" \ + "2: br 1b \n" \ + ".previous" \ + : [ret] "=&r" (__atomic_ret), \ + [tmp] "=&r" (__atomic_tmp), \ + [val] "=m" (*ptr) \ + : "m" (*ptr)); \ + __atomic_ret; \ +}) +#define _MD_ATOMIC_SET(ptr, n) ({ \ + PRInt32 __atomic_tmp, __atomic_ret; \ + __asm__ __volatile__( \ + "1: ldl_l %[ret], %[val] \n" \ + " mov %[newval], %[tmp] \n" \ + " stl_c %[tmp], %[val] \n" \ + " beq %[tmp], 2f \n" \ + ".subsection 2 \n" \ + "2: br 1b \n" \ + ".previous" \ + : [ret] "=&r" (__atomic_ret), \ + [tmp] "=&r"(__atomic_tmp), \ + [val] "=m" (*ptr) \ + : [newval] "Ir" (n), "m" (*ptr)); \ + __atomic_ret; \ +}) +#endif + +#if defined(__arm__) || defined(__aarch64__) +#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) +/* Use GCC built-in functions */ +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() + +#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1) +#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv) +#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i) + +#elif defined(_PR_ARM_KUSER) +#define _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() + +/* + * The kernel provides this helper function at a fixed address with a fixed + * ABI signature, directly callable from user space. + * + * Definition: + * Atomically store newval in *ptr if *ptr is equal to oldval. + * Return zero if *ptr was changed or non-zero if no exchange happened. + */ +typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); +#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) + +#define _MD_ATOMIC_INCREMENT(ptr) _MD_ATOMIC_ADD(ptr, 1) +#define _MD_ATOMIC_DECREMENT(ptr) _MD_ATOMIC_ADD(ptr, -1) + +static inline PRInt32 _MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 n) +{ + PRInt32 ov, nv; + volatile PRInt32 *vp = ptr; + + do { + ov = *vp; + nv = ov + n; + } while (__kernel_cmpxchg(ov, nv, vp)); + + return nv; +} + +static inline PRInt32 _MD_ATOMIC_SET(PRInt32 *ptr, PRInt32 nv) +{ + PRInt32 ov; + volatile PRInt32 *vp = ptr; + + do { + ov = *vp; + } while (__kernel_cmpxchg(ov, nv, vp)); + + return ov; +} +#endif +#endif /* __arm__ */ + +#define USE_SETJMP +#if (defined(__GLIBC__) && __GLIBC__ >= 2) || defined(ANDROID) +#define _PR_POLL_AVAILABLE +#endif +#undef _PR_USE_POLL +#define _PR_STAT_HAS_ONLY_ST_ATIME +#if defined(__alpha) || defined(__ia64__) +#define _PR_HAVE_LARGE_OFF_T +#elif (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) \ + || defined(ANDROID) +#define _PR_HAVE_OFF64_T +#else +#define _PR_NO_LARGE_FILES +#endif +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) \ + || defined(ANDROID) +#define _PR_INET6 +#define _PR_HAVE_INET_NTOP +#define _PR_HAVE_GETHOSTBYNAME2 +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#endif +#ifndef ANDROID +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY +#endif +/* Android has gethostbyname_r but not gethostbyaddr_r or gethostbyname2_r. */ +#if (__GLIBC__ >= 2) && defined(_PR_PTHREADS) +#define _PR_HAVE_GETHOST_R +#define _PR_HAVE_GETHOST_R_INT +#endif + +#ifdef _PR_PTHREADS + +extern void _MD_CleanupBeforeExit(void); +#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit + +#else /* ! _PR_PTHREADS */ + +#include <setjmp.h> + +#define PR_CONTEXT_TYPE sigjmp_buf + +#define CONTEXT(_th) ((_th)->md.context) + +#ifdef __powerpc__ +/* + * PowerPC based MkLinux + * + * On the PowerPC, the new style jmp_buf isn't used until glibc + * 2.1. + */ +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_GPR1] +#else +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__misc[0] +#endif /* glibc 2.1 or later */ +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +/* aix = 64, macos = 70 */ +#define PR_NUM_GCREGS 64 + +#elif defined(__alpha) +/* Alpha based Linux */ + +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP] +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +#define _MD_SP_TYPE long int +#else +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +#define _MD_SP_TYPE __ptr_t +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +/* XXX not sure if this is correct, or maybe it should be 17? */ +#define PR_NUM_GCREGS 9 + +#elif defined(__ia64__) + +#define _MD_GET_SP(_t) ((long *)((_t)->md.context[0].__jmpbuf)[0]) +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +#define _MD_SP_TYPE long int + +#define PR_NUM_GCREGS _JBLEN + +#elif defined(__mc68000__) +/* m68k based Linux */ + +/* + * On the m68k, glibc still uses the old style sigjmp_buf, even + * in glibc 2.0.7. + */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +#define _MD_SP_TYPE int +#else +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +#define _MD_SP_TYPE __ptr_t +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +/* XXX not sure if this is correct, or maybe it should be 17? */ +#define PR_NUM_GCREGS 9 + +#elif defined(__sparc__) +/* Sparc */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +/* + * You need glibc2-2.0.7-25 or later. The libraries that came with + * Red Hat 5.1 are not new enough, but they are in 5.2. + */ +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP] +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[JB_FP] = val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[JB_FP]) +#define _MD_SP_TYPE int +#else +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__fp +#define _MD_SET_FP(_t, val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) ((void *) 0) +#define _MD_SP_TYPE __ptr_t +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +#elif defined(__i386__) +/* Intel based Linux */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[JB_SP] +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[JB_BP] = val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[JB_BP]) +#define _MD_SP_TYPE int +#else +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__bp = val) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) &((_t)->md.context[0].__jmpbuf[0].__bp) +#define _MD_SP_TYPE __ptr_t +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ +#define PR_NUM_GCREGS 6 + +#elif defined(__mips__) +/* Linux/MIPS */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__sp +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__fp = (val)) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[0].__fp) +#define _MD_SP_TYPE __ptr_t +#else +#error "Linux/MIPS pre-glibc2 not supported yet" +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +#elif defined(__arm__) +/* ARM/Linux */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#ifdef __ARM_EABI__ +/* EABI */ +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[8] +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[7] = (val)) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[7]) +#define _MD_SP_TYPE __ptr_t +#else /* __ARM_EABI__ */ +/* old ABI */ +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[20] +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[19] = (val)) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[19]) +#define _MD_SP_TYPE __ptr_t +#endif /* __ARM_EABI__ */ +#else +#error "ARM/Linux pre-glibc2 not supported yet" +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +#elif defined(__sh__) +/* SH/Linux */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[7] +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[6] = (val)) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[6]) +#define _MD_SP_TYPE __ptr_t +#else +#error "SH/Linux pre-glibc2 not supported yet" +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +#elif defined(__m32r__) +/* Linux/M32R */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 +#define _MD_GET_SP(_t) (_t)->md.context[0].__jmpbuf[0].__regs[JB_SP] +#define _MD_SET_FP(_t, val) ((_t)->md.context[0].__jmpbuf[0].__regs[JB_FP] = (val)) +#define _MD_GET_SP_PTR(_t) &(_MD_GET_SP(_t)) +#define _MD_GET_FP_PTR(_t) (&(_t)->md.context[0].__jmpbuf[0].__regs[JB_FP]) +#define _MD_SP_TYPE __ptr_t +#else +#error "Linux/M32R pre-glibc2 not supported yet" +#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */ + +#else + +#error "Unknown CPU architecture" + +#endif /*__powerpc__*/ + +/* +** Initialize a thread context to run "_main()" when started +*/ +#ifdef __powerpc__ + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (sigsetjmp(CONTEXT(_thread), 1)) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 128); \ + _thread->md.sp = _MD_GET_SP_PTR(_thread); \ + _thread->md.fp = _MD_GET_FP_PTR(_thread); \ + _MD_SET_FP(_thread, 0); \ +} + +#elif defined(__mips__) + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + (void) sigsetjmp(CONTEXT(_thread), 1); \ + _thread->md.context[0].__jmpbuf[0].__pc = (__ptr_t) _main; \ + _MD_GET_SP(_thread) = (_MD_SP_TYPE) ((_sp) - 64); \ + _thread->md.sp = _MD_GET_SP_PTR(_thread); \ + _thread->md.fp = _MD_GET_FP_PTR(_thread); \ + _MD_SET_FP(_thread, 0); \ +} + +#else + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (sigsetjmp(CONTEXT(_thread), 1)) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (_MD_SP_TYPE) ((_sp) - 64); \ + _thread->md.sp = _MD_GET_SP_PTR(_thread); \ + _thread->md.fp = _MD_GET_FP_PTR(_thread); \ + _MD_SET_FP(_thread, 0); \ +} + +#endif /*__powerpc__*/ + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!sigsetjmp(CONTEXT(_thread), 1)) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + siglongjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + PR_CONTEXT_TYPE context; + void *sp; + void *fp; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#include <sys/time.h> /* for FD_SETSIZE */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +extern PRStatus _MD_InitializeThread(PRThread *thread); + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread +#define _MD_RESUME_THREAD(thread) _MD_resume_thread +#define _MD_CLEAN_THREAD(_thread) + +extern PRStatus _MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); +extern void _MD_SET_PRIORITY(struct _MDThread *thread, PRUintn newPri); +extern PRStatus _MD_WAIT(PRThread *, PRIntervalTime timeout); +extern PRStatus _MD_WAKEUP_WAITER(PRThread *); +extern void _MD_YIELD(void); + +#endif /* ! _PR_PTHREADS */ + +extern void _MD_EarlyInit(void); + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _PR_HAVE_CLOCK_MONOTONIC + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#define _MD_SELECT __select + +#ifdef _PR_POLL_AVAILABLE +#include <sys/poll.h> +extern int __syscall_poll(struct pollfd *ufds, unsigned long int nfds, + int timeout); +#define _MD_POLL __syscall_poll +#endif + +/* For writev() */ +#include <sys/uio.h> + +extern void _MD_linux_map_sendfile_error(int err); + +#endif /* nspr_linux_defs_h___ */ diff --git a/nsprpub/pr/include/md/_netbsd.cfg b/nsprpub/pr/include/md/_netbsd.cfg new file mode 100644 index 0000000000..1326556e0a --- /dev/null +++ b/nsprpub/pr/include/md/_netbsd.cfg @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef NETBSD +#define NETBSD +#endif + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif + +#if defined(__i386__) || defined(__arm32__) || defined(__ARMEL__) || \ + defined(__MIPSEL__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#elif defined(__sparc_v9__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#elif defined(__sparc__) || defined(__MIPSEB__) || defined(__ARMEB__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS + +#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 8 +#define PR_ALIGN_OF_POINTER 4 + +#elif defined(__alpha__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__amd64__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__powerpc__) || defined(__m68k__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#error Must define constants for type sizes here. + +#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/nsprpub/pr/include/md/_netbsd.h b/nsprpub/pr/include/md/_netbsd.h new file mode 100644 index 0000000000..1ec0fe6ec8 --- /dev/null +++ b/nsprpub/pr/include/md/_netbsd.h @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_netbsd_defs_h___ +#define nspr_netbsd_defs_h___ + +#include <sys/syscall.h> +#include <sys/param.h> /* for __NetBSD_Version__ */ + +#define PR_LINKER_ARCH "netbsd" +#define _PR_SI_SYSNAME "NetBSD" +#if defined(__i386__) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(__alpha__) +#define _PR_SI_ARCHITECTURE "alpha" +#elif defined(__amd64__) +#define _PR_SI_ARCHITECTURE "amd64" +#elif defined(__m68k__) +#define _PR_SI_ARCHITECTURE "m68k" +#elif defined(__powerpc__) +#define _PR_SI_ARCHITECTURE "powerpc" +#elif defined(__sparc_v9__) +#define _PR_SI_ARCHITECTURE "sparc64" +#elif defined(__sparc__) +#define _PR_SI_ARCHITECTURE "sparc" +#elif defined(__mips__) +#define _PR_SI_ARCHITECTURE "mips" +#elif defined(__arm32__) || defined(__arm__) || defined(__armel__) \ + || defined(__armeb__) +#define _PR_SI_ARCHITECTURE "arm" +#endif + +#if defined(__ELF__) +#define PR_DLL_SUFFIX ".so" +#else +#define PR_DLL_SUFFIX ".so.1.0" +#endif + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN +#define _PR_HAVE_SOCKADDR_LEN +#define _PR_NO_LARGE_FILES +#define _PR_STAT_HAS_ST_ATIMESPEC +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY + +#if __NetBSD_Version__ >= 105000000 +#define _PR_INET6 +#define _PR_HAVE_INET_NTOP +#define _PR_HAVE_GETHOSTBYNAME2 +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#endif + +#define USE_SETJMP + +#ifndef _PR_PTHREADS +#include <setjmp.h> + +#define PR_CONTEXT_TYPE sigjmp_buf + +#define CONTEXT(_th) ((_th)->md.context) + +#if defined(__i386__) || defined(__sparc__) || defined(__m68k__) || defined(__powerpc__) +#define JB_SP_INDEX 2 +#elif defined(__mips__) +#define JB_SP_INDEX 4 +#elif defined(__alpha__) +#define JB_SP_INDEX 34 +#elif defined(__arm32__) +/* + * On the arm32, the jmpbuf regs underwent a name change after NetBSD 1.3. + */ +#ifdef JMPBUF_REG_R13 +#define JB_SP_INDEX JMPBUF_REG_R13 +#else +#define JB_SP_INDEX _JB_REG_R13 +#endif +#else +#error "Need to define SP index in jmp_buf here" +#endif +#define _MD_GET_SP(_th) (_th)->md.context[JB_SP_INDEX] + +#define PR_NUM_GCREGS _JBLEN + +/* +** Initialize a thread context to run "_main()" when started +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (sigsetjmp(CONTEXT(_thread), 1)) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!sigsetjmp(CONTEXT(_thread), 1)) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + siglongjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread +#define _MD_RESUME_THREAD(thread) _MD_resume_thread +#define _MD_CLEAN_THREAD(_thread) + +#endif /* ! _PR_PTHREADS */ + +extern void _MD_EarlyInit(void); + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _PR_HAVE_CLOCK_MONOTONIC + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv) +#if defined(_PR_POLL_AVAILABLE) +#include <poll.h> +#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout) +#endif + +#if NetBSD1_3 == 1L +typedef unsigned int nfds_t; +#endif + +#endif /* nspr_netbsd_defs_h___ */ diff --git a/nsprpub/pr/include/md/_nspr_pthread.h b/nsprpub/pr/include/md/_nspr_pthread.h new file mode 100644 index 0000000000..01796e7721 --- /dev/null +++ b/nsprpub/pr/include/md/_nspr_pthread.h @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_pthread_defs_h___ +#define nspr_pthread_defs_h___ + +#include <pthread.h> +#include "prthread.h" + +#if defined(PTHREADS_USER) +/* +** Thread Local Storage +*/ +extern pthread_key_t current_thread_key; +extern pthread_key_t current_cpu_key; +extern pthread_key_t last_thread_key; +extern pthread_key_t intsoff_key; + +#define _MD_CURRENT_THREAD() \ + ((struct PRThread *) pthread_getspecific(current_thread_key)) +#define _MD_CURRENT_CPU() \ + ((struct _PRCPU *) pthread_getspecific(current_cpu_key)) +#define _MD_LAST_THREAD() \ + ((struct PRThread *) pthread_getspecific(last_thread_key)) + +#define _MD_SET_CURRENT_THREAD(newval) \ + pthread_setspecific(current_thread_key, (void *)newval) + +#define _MD_SET_CURRENT_CPU(newval) \ + pthread_setspecific(current_cpu_key, (void *)newval) + +#define _MD_SET_LAST_THREAD(newval) \ + pthread_setspecific(last_thread_key, (void *)newval) + +#define _MD_SET_INTSOFF(_val) +#define _MD_GET_INTSOFF() 1 + +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + *status = PR_TRUE; \ + if (SAVE_CONTEXT(_thread)) { \ + (*_main)(); \ + } \ + _MD_SET_THR_SP(_thread, _sp); \ + _thread->no_sched = 0; \ + PR_END_MACRO + +#define _MD_SWITCH_CONTEXT(_thread) \ + PR_BEGIN_MACRO \ + PR_ASSERT(_thread->no_sched); \ + if (!SAVE_CONTEXT(_thread)) { \ + (_thread)->md.errcode = errno; \ + _MD_SET_LAST_THREAD(_thread); \ + _PR_Schedule(); \ + } else { \ + (_MD_LAST_THREAD())->no_sched = 0; \ + } \ + PR_END_MACRO + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ + PR_BEGIN_MACRO \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + _thread->no_sched = 1; \ + GOTO_CONTEXT(_thread); \ + PR_END_MACRO + + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + jmp_buf jb; + int id; + int errcode; + pthread_t pthread; + pthread_mutex_t pthread_mutex; + pthread_cond_t pthread_cond; + int wait; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + pthread_mutex_t mutex; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + pthread_mutex_t mutex; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + jmp_buf jb; + pthread_t pthread; + struct _MDCPU_Unix md_unix; +}; + +/* +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +*/ + +extern pthread_mutex_t _pr_heapLock; + +#define _PR_LOCK(lock) pthread_mutex_lock(lock) + +#define _PR_UNLOCK(lock) pthread_mutex_unlock(lock) + + +#define _PR_LOCK_HEAP() { \ + if (_pr_primordialCPU) { \ + _PR_LOCK(_pr_heapLock); \ + } + +#define _PR_UNLOCK_HEAP() if (_pr_primordialCPU) { \ + _PR_UNLOCK(_pr_heapLock); \ + } \ + } + +NSPR_API(PRStatus) _MD_NEW_LOCK(struct _MDLock *md); +NSPR_API(void) _MD_FREE_LOCK(struct _MDLock *lockp); + +#define _MD_LOCK(_lockp) _PR_LOCK(&(_lockp)->mutex) +#define _MD_UNLOCK(_lockp) _PR_UNLOCK(&(_lockp)->mutex) + +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() +#define _MD_CHECK_FOR_EXIT() + +NSPR_API(PRStatus) _MD_InitThread(struct PRThread *thread); +#define _MD_INIT_THREAD _MD_InitThread +#define _MD_INIT_ATTACHED_THREAD _MD_InitThread + +NSPR_API(void) _MD_ExitThread(struct PRThread *thread); +#define _MD_EXIT_THREAD _MD_ExitThread + +NSPR_API(void) _MD_SuspendThread(struct PRThread *thread); +#define _MD_SUSPEND_THREAD _MD_SuspendThread + +NSPR_API(void) _MD_ResumeThread(struct PRThread *thread); +#define _MD_RESUME_THREAD _MD_ResumeThread + +NSPR_API(void) _MD_SuspendCPU(struct _PRCPU *thread); +#define _MD_SUSPEND_CPU _MD_SuspendCPU + +NSPR_API(void) _MD_ResumeCPU(struct _PRCPU *thread); +#define _MD_RESUME_CPU _MD_ResumeCPU + +#define _MD_BEGIN_SUSPEND_ALL() +#define _MD_END_SUSPEND_ALL() +#define _MD_BEGIN_RESUME_ALL() +#define _MD_END_RESUME_ALL() + +NSPR_API(void) _MD_EarlyInit(void); +#define _MD_EARLY_INIT _MD_EarlyInit + +#define _MD_FINAL_INIT _PR_UnixInit + +NSPR_API(void) _MD_InitLocks(void); +#define _MD_INIT_LOCKS _MD_InitLocks + +NSPR_API(void) _MD_CleanThread(struct PRThread *thread); +#define _MD_CLEAN_THREAD _MD_CleanThread + +NSPR_API(PRStatus) _MD_CreateThread( + struct PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); +#define _MD_CREATE_THREAD _MD_CreateThread + +extern void _MD_CleanupBeforeExit(void); +#define _MD_CLEANUP_BEFORE_EXIT _MD_CleanupBeforeExit + +NSPR_API(void) _MD_InitRunningCPU(struct _PRCPU *cpu); +#define _MD_INIT_RUNNING_CPU _MD_InitRunningCPU + +/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and + * awaken a thread which is waiting on a lock or cvar. + */ +NSPR_API(PRStatus) _MD_wait(struct PRThread *, PRIntervalTime timeout); +#define _MD_WAIT _MD_wait + +NSPR_API(PRStatus) _MD_WakeupWaiter(struct PRThread *); +#define _MD_WAKEUP_WAITER _MD_WakeupWaiter + +NSPR_API(void) _MD_SetPriority(struct _MDThread *thread, + PRThreadPriority newPri); +#define _MD_SET_PRIORITY _MD_SetPriority + +#endif /* PTHREADS_USER */ + +#endif /* nspr_pthread_defs_h___ */ diff --git a/nsprpub/pr/include/md/_nto.cfg b/nsprpub/pr/include/md/_nto.cfg new file mode 100644 index 0000000000..b76a56057e --- /dev/null +++ b/nsprpub/pr/include/md/_nto.cfg @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef NTO +#define NTO +#endif + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#ifdef __i386__ + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define PR_BYTES_PER_BYTE 1L +#define PR_BYTES_PER_SHORT 2L +#define PR_BYTES_PER_INT 4L +#define PR_BYTES_PER_INT64 8L +#define PR_BYTES_PER_LONG 4L +#define PR_BYTES_PER_FLOAT 4L +#define PR_BYTES_PER_DOUBLE 8L +#define PR_BYTES_PER_WORD 4L +#define PR_BYTES_PER_DWORD 8L + +#define PR_BITS_PER_BYTE 8L +#define PR_BITS_PER_SHORT 16L +#define PR_BITS_PER_INT 32L +#define PR_BITS_PER_INT64 64L +#define PR_BITS_PER_LONG 32L +#define PR_BITS_PER_FLOAT 32L +#define PR_BITS_PER_DOUBLE 64L +#define PR_BITS_PER_WORD 32L + +#define PR_BITS_PER_BYTE_LOG2 3L +#define PR_BITS_PER_SHORT_LOG2 4L +#define PR_BITS_PER_INT_LOG2 5L +#define PR_BITS_PER_INT64_LOG2 6L +#define PR_BITS_PER_LONG_LOG2 5L +#define PR_BITS_PER_FLOAT_LOG2 5L +#define PR_BITS_PER_DOUBLE_LOG2 6L +#define PR_BITS_PER_WORD_LOG2 5L + +#define PR_ALIGN_OF_SHORT 2L +#define PR_ALIGN_OF_INT 4L +#define PR_ALIGN_OF_LONG 4L +#define PR_ALIGN_OF_INT64 4L +#define PR_ALIGN_OF_FLOAT 4L +#define PR_ALIGN_OF_DOUBLE 4L +#define PR_ALIGN_OF_POINTER 4L +#define PR_ALIGN_OF_WORD 4L + +#define PR_BYTES_PER_WORD_LOG2 2L +#define PR_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L + +#else + +#error Undefined CPU Architecture + +#endif + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#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/nsprpub/pr/include/md/_nto.h b/nsprpub/pr/include/md/_nto.h new file mode 100644 index 0000000000..1094dd183f --- /dev/null +++ b/nsprpub/pr/include/md/_nto.h @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_nto_defs_h___ +#define nspr_nto_defs_h___ + +/* +** Internal configuration macros +*/ +#define PR_LINKER_ARCH "nto" +#define _PR_SI_SYSNAME "NTO" +#define _PR_SI_ARCHITECTURE "x86" +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MINIMUM_STACK_SIZE 131072L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#ifndef HAVE_WEAK_IO_SYMBOLS +#define HAVE_WEAK_IO_SYMBOLS +#endif + +#undef _PR_POLL_AVAILABLE +#undef _PR_USE_POLL +#define _PR_HAVE_SOCKADDR_LEN +#undef HAVE_BSD_FLOCK +#define HAVE_FCNTL_FILE_LOCKING +#define _PR_NO_LARGE_FILES +#define _PR_STAT_HAS_ONLY_ST_ATIME +#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY +#define _PR_HAVE_POSIX_SEMAPHORES + +#undef FD_SETSIZE +#define FD_SETSIZE 4096 +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN +#define NEED_STRFTIME_LOCK +#define NEED_TIME_R +#define _PR_NEED_STRCASECMP + +#ifndef HAVE_STRERROR +#define HAVE_STRERROR +#endif + +#define USE_SETJMP + +#include <setjmp.h> + +#define _SETJMP setjmp +#define _LONGJMP longjmp +#define _PR_CONTEXT_TYPE jmp_buf +#define _PR_NUM_GCREGS _JBLEN +#define _MD_GET_SP(_t) (_t)->md.context[7] + +#define CONTEXT(_th) ((_th)->md.context) + + +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if(_SETJMP(CONTEXT(_thread))) (*_main)(); \ + _MD_GET_SP(_thread) = (int) ((_sp) - 128); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!_SETJMP(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + _LONGJMP(CONTEXT(_thread), 1); \ +} + +/* +** Machine-dependent (MD) data structures. +*/ +struct _MDThread { + _PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* +** md-specific cpu structure field +*/ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD], fd_write_cnt[_PR_MD_MAX_OSFD], fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INTERVAL_USE_GTOD +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +/* +** We wrapped the select() call. _MD_SELECT refers to the built-in, +** unwrapped version. +*/ +#define _MD_SELECT select + +#define SA_RESTART 0 + +#endif /* nspr_nto_defs_h___ */ diff --git a/nsprpub/pr/include/md/_openbsd.cfg b/nsprpub/pr/include/md/_openbsd.cfg new file mode 100644 index 0000000000..b68d6e9cd6 --- /dev/null +++ b/nsprpub/pr/include/md/_openbsd.cfg @@ -0,0 +1,353 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef OPENBSD +#define OPENBSD +#endif + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif + +#if defined(__i386__) || defined(__arm__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#elif defined(__amd64__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS + +#elif defined(__sparc_v9__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__sparc__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS + +#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 8 +#define PR_ALIGN_OF_POINTER 4 + +#elif defined(__alpha__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS +#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_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_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__powerpc__) || defined(__m68k__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#error Must define constants for type sizes here. + +#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/nsprpub/pr/include/md/_openbsd.h b/nsprpub/pr/include/md/_openbsd.h new file mode 100644 index 0000000000..1949631a96 --- /dev/null +++ b/nsprpub/pr/include/md/_openbsd.h @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_openbsd_defs_h___ +#define nspr_openbsd_defs_h___ + +#include <sys/syscall.h> + +#define PR_LINKER_ARCH "openbsd" +#define _PR_SI_SYSNAME "OPENBSD" +#if defined(__i386__) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(__alpha__) +#define _PR_SI_ARCHITECTURE "alpha" +#elif defined(__amd64__) +#define _PR_SI_ARCHITECTURE "amd64" +#elif defined(__powerpc64__) +#define _PR_SI_ARCHITECTURE "powerpc64" +#elif defined(__powerpc__) +#define _PR_SI_ARCHITECTURE "powerpc" +#elif defined(__aarch64__) +#define _PR_SI_ARCHITECTURE "aarch64" +#elif defined(__sparc__) +#define _PR_SI_ARCHITECTURE "sparc" +#elif defined(__arm__) +#define _PR_SI_ARCHITECTURE "arm" +#endif + +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN +#define _PR_HAVE_SOCKADDR_LEN +#define _PR_HAVE_LARGE_OFF_T +#define _PR_STAT_HAS_ST_ATIMESPEC +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY + +#define _PR_INET6 +#define _PR_HAVE_INET_NTOP +#define _PR_HAVE_GETHOSTBYNAME2 +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE + +#define USE_SETJMP + +#ifndef _PR_PTHREADS +#include <setjmp.h> + +#define PR_CONTEXT_TYPE sigjmp_buf + +#define CONTEXT(_th) ((_th)->md.context) + +#if defined(__i386__) || defined(__sparc__) || defined(__m68k__) +#define JB_SP_INDEX 2 +#elif defined(__powerpc__) +#define JB_SP_INDEX 1 +#elif defined(__alpha__) +#define JB_SP_INDEX 34 +#elif defined(__amd64__) +#define JB_SP_INDEX 6 +#elif defined(__arm__) +#define JB_SP_INDEX 23 +#else +#error "Need to define SP index in jmp_buf here" +#endif +#define _MD_GET_SP(_th) (_th)->md.context[JB_SP_INDEX] + +#define PR_NUM_GCREGS _JBLEN + +/* +** Initialize a thread context to run "_main()" when started +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (sigsetjmp(CONTEXT(_thread), 1)) { \ + _main(); \ + } \ + _MD_GET_SP(_thread) = (unsigned char*) ((_sp) - 64); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!sigsetjmp(CONTEXT(_thread), 1)) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + siglongjmp(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures */ + +struct _MDThread { + PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) _MD_suspend_thread +#define _MD_RESUME_THREAD(thread) _MD_resume_thread +#define _MD_CLEAN_THREAD(_thread) + +#endif /* ! _PR_PTHREADS */ + +extern void _MD_EarlyInit(void); + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _PR_HAVE_CLOCK_MONOTONIC + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#define _MD_SELECT(nfds,r,w,e,tv) syscall(SYS_select,nfds,r,w,e,tv) +#include <poll.h> +#define _MD_POLL(fds,nfds,timeout) syscall(SYS_poll,fds,nfds,timeout) + +#if OpenBSD1_3 == 1L +typedef unsigned int nfds_t; +#endif + +#endif /* nspr_openbsd_defs_h___ */ diff --git a/nsprpub/pr/include/md/_os2.cfg b/nsprpub/pr/include/md/_os2.cfg new file mode 100644 index 0000000000..67c2dedf54 --- /dev/null +++ b/nsprpub/pr/include/md/_os2.cfg @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_PC +#define XP_PC +#endif + +#ifndef XP_OS2 +#define XP_OS2 +#endif + +#ifndef OS2 +#define OS2 +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#ifdef NO_LONG_LONG +#undef HAVE_LONG_LONG +#else +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG 1 +#endif +#endif + +#define PR_AF_INET6 24 /* same as AF_INET6 */ + +#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_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_WORD 32 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 5 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_WORD 4 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 2 + +#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/nsprpub/pr/include/md/_os2.h b/nsprpub/pr/include/md/_os2.h new file mode 100644 index 0000000000..2c3165a146 --- /dev/null +++ b/nsprpub/pr/include/md/_os2.h @@ -0,0 +1,504 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_os2_defs_h___ +#define nspr_os2_defs_h___ + +#ifndef NO_LONG_LONG +#define INCL_LONGLONG +#endif +#define INCL_DOS +#define INCL_DOSPROCESS +#define INCL_DOSERRORS +#define INCL_WIN +#define INCL_WPS +#include <os2.h> +#include <sys/select.h> + +#include "prio.h" + +#include <errno.h> + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "os2" +#define _PR_SI_SYSNAME "OS2" +#define _PR_SI_ARCHITECTURE "x86" /* XXXMB hardcode for now */ + +#define HAVE_DLL +#define _PR_GLOBAL_THREADS_ONLY +#undef HAVE_THREAD_AFFINITY +#define _PR_HAVE_THREADSAFE_GETHOST +#define _PR_HAVE_ATOMIC_OPS +#define HAVE_NETINET_TCP_H + +#define HANDLE unsigned long +#define HINSTANCE HMODULE + +/* --- Common User-Thread/Native-Thread Definitions --------------------- */ + +/* --- Globals --- */ +extern struct PRLock *_pr_schedLock; + +/* --- Typedefs --- */ +typedef void (*FiberFunc)(void *); + +#define PR_NUM_GCREGS 8 +typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS]; +#define GC_VMBASE 0x40000000 +#define GC_VMLIMIT 0x00FFFFFF +typedef int (*FARPROC)(); + +#define _MD_MAGIC_THREAD 0x22222222 +#define _MD_MAGIC_THREADSTACK 0x33333333 +#define _MD_MAGIC_SEGMENT 0x44444444 +#define _MD_MAGIC_DIR 0x55555555 +#define _MD_MAGIC_CV 0x66666666 + +struct _MDSemaphore { + HEV sem; +}; + +struct _MDCPU { + int unused; +}; + +struct _MDThread { + HEV blocked_sema; /* Threads block on this when waiting + * for IO or CondVar. + */ + PRBool inCVWaitQueue; /* PR_TRUE if the thread is in the + * wait queue of some cond var. + * PR_FALSE otherwise. */ + TID handle; /* OS/2 thread handle */ + void *sp; /* only valid when suspended */ + PRUint32 magic; /* for debugging */ + PR_CONTEXT_TYPE gcContext; /* Thread context for GC */ + struct PRThread *prev, *next; /* used by the cvar wait queue to + * chain the PRThread structures + * together */ +}; + +struct _MDThreadStack { + PRUint32 magic; /* for debugging */ +}; + +struct _MDSegment { + PRUint32 magic; /* for debugging */ +}; + +#undef PROFILE_LOCKS + +struct _MDDir { + HDIR d_hdl; + union { + FILEFINDBUF3 small; + FILEFINDBUF3L large; + } d_entry; + PRBool firstEntry; /* Is this the entry returned + * by FindFirstFile()? */ + PRUint32 magic; /* for debugging */ +}; + +struct _MDCVar { + PRUint32 magic; + struct PRThread *waitHead, *waitTail; /* the wait queue: a doubly- + * linked list of threads + * waiting on this condition + * variable */ + PRIntn nwait; /* number of threads in the + * wait queue */ +}; + +#define _MD_CV_NOTIFIED_LENGTH 6 +typedef struct _MDNotified _MDNotified; +struct _MDNotified { + PRIntn length; /* # of used entries in this + * structure */ + struct { + struct _MDCVar *cv; /* the condition variable notified */ + PRIntn times; /* and the number of times notified */ + struct PRThread *notifyHead; /* list of threads to wake up */ + } cv[_MD_CV_NOTIFIED_LENGTH]; + _MDNotified *link; /* link to another of these, or NULL */ +}; + +struct _MDLock { + HMTX mutex; /* this is recursive on OS/2 */ + + /* + * When notifying cvars, there is no point in actually + * waking up the threads waiting on the cvars until we've + * released the lock. So, we temporarily record the cvars. + * When doing an unlock, we'll then wake up the waiting threads. + */ + struct _MDNotified notified; /* array of conditions notified */ +#ifdef PROFILE_LOCKS + PRInt32 hitcount; + PRInt32 misscount; +#endif +}; + +struct _MDFileDesc { + PRInt32 osfd; /* The osfd can come from one of three spaces: + * - For stdin, stdout, and stderr, we are using + * the libc file handle (0, 1, 2), which is an int. + * - For files and pipes, we are using OS/2 handles, + * which is a void*. + * - For sockets, we are using int + */ +}; + +struct _MDProcess { + PID pid; +}; + +/* --- Misc stuff --- */ +#define _MD_GET_SP(thread) (thread)->md.gcContext[6] + +/* --- IO stuff --- */ + +#define _MD_OPEN (_PR_MD_OPEN) +#define _MD_OPEN_FILE (_PR_MD_OPEN) +#define _MD_READ (_PR_MD_READ) +#define _MD_WRITE (_PR_MD_WRITE) +#define _MD_WRITEV (_PR_MD_WRITEV) +#define _MD_LSEEK (_PR_MD_LSEEK) +#define _MD_LSEEK64 (_PR_MD_LSEEK64) +extern PRInt32 _MD_CloseFile(PRInt32 osfd); +#define _MD_CLOSE_FILE _MD_CloseFile +#define _MD_GETFILEINFO (_PR_MD_GETFILEINFO) +#define _MD_GETFILEINFO64 (_PR_MD_GETFILEINFO64) +#define _MD_GETOPENFILEINFO (_PR_MD_GETOPENFILEINFO) +#define _MD_GETOPENFILEINFO64 (_PR_MD_GETOPENFILEINFO64) +#define _MD_STAT (_PR_MD_STAT) +#define _MD_RENAME (_PR_MD_RENAME) +#define _MD_ACCESS (_PR_MD_ACCESS) +#define _MD_DELETE (_PR_MD_DELETE) +#define _MD_MKDIR (_PR_MD_MKDIR) +#define _MD_MAKE_DIR (_PR_MD_MKDIR) +#define _MD_RMDIR (_PR_MD_RMDIR) +#define _MD_LOCKFILE (_PR_MD_LOCKFILE) +#define _MD_TLOCKFILE (_PR_MD_TLOCKFILE) +#define _MD_UNLOCKFILE (_PR_MD_UNLOCKFILE) + +/* --- Socket IO stuff --- */ + +/* The ones that don't map directly may need to be re-visited... */ +#define _MD_EACCES EACCES +#define _MD_EADDRINUSE EADDRINUSE +#define _MD_EADDRNOTAVAIL EADDRNOTAVAIL +#define _MD_EAFNOSUPPORT EAFNOSUPPORT +#define _MD_EAGAIN EWOULDBLOCK +#define _MD_EALREADY EALREADY +#define _MD_EBADF EBADF +#define _MD_ECONNREFUSED ECONNREFUSED +#define _MD_ECONNRESET ECONNRESET +#define _MD_EFAULT SOCEFAULT +#define _MD_EINPROGRESS EINPROGRESS +#define _MD_EINTR EINTR +#define _MD_EINVAL EINVAL +#define _MD_EISCONN EISCONN +#define _MD_ENETUNREACH ENETUNREACH +#define _MD_ENOENT ENOENT +#define _MD_ENOTCONN ENOTCONN +#define _MD_ENOTSOCK ENOTSOCK +#define _MD_EOPNOTSUPP EOPNOTSUPP +#define _MD_EWOULDBLOCK EWOULDBLOCK +#define _MD_GET_SOCKET_ERROR() sock_errno() +#ifndef INADDR_LOOPBACK /* For some reason this is not defined in OS2 tcpip */ +/* #define INADDR_LOOPBACK INADDR_ANY */ +#endif + +#define _MD_INIT_FILEDESC(fd) +extern void _MD_MakeNonblock(PRFileDesc *f); +#define _MD_MAKE_NONBLOCK _MD_MakeNonblock +#define _MD_INIT_FD_INHERITABLE (_PR_MD_INIT_FD_INHERITABLE) +#define _MD_QUERY_FD_INHERITABLE (_PR_MD_QUERY_FD_INHERITABLE) +#define _MD_SHUTDOWN (_PR_MD_SHUTDOWN) +#define _MD_LISTEN _PR_MD_LISTEN +extern PRInt32 _MD_CloseSocket(PRInt32 osfd); +#define _MD_CLOSE_SOCKET _MD_CloseSocket +#define _MD_SENDTO (_PR_MD_SENDTO) +#define _MD_RECVFROM (_PR_MD_RECVFROM) +#define _MD_SOCKETPAIR (_PR_MD_SOCKETPAIR) +#define _MD_GETSOCKNAME (_PR_MD_GETSOCKNAME) +#define _MD_GETPEERNAME (_PR_MD_GETPEERNAME) +#define _MD_GETSOCKOPT (_PR_MD_GETSOCKOPT) +#define _MD_SETSOCKOPT (_PR_MD_SETSOCKOPT) + +#define _MD_FSYNC _PR_MD_FSYNC +#define _MD_SET_FD_INHERITABLE (_PR_MD_SET_FD_INHERITABLE) + +#ifdef _PR_HAVE_ATOMIC_OPS +#define _MD_INIT_ATOMIC() +#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT +#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD +#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT +#define _MD_ATOMIC_SET _PR_MD_ATOMIC_SET +#endif + +#define _MD_INIT_IO (_PR_MD_INIT_IO) +#define _MD_PR_POLL (_PR_MD_PR_POLL) + +#define _MD_SOCKET (_PR_MD_SOCKET) +extern PRInt32 _MD_SocketAvailable(PRFileDesc *fd); +#define _MD_SOCKETAVAILABLE _MD_SocketAvailable +#define _MD_PIPEAVAILABLE _MD_SocketAvailable +#define _MD_CONNECT (_PR_MD_CONNECT) +extern PRInt32 _MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen, + PRIntervalTime timeout); +#define _MD_ACCEPT _MD_Accept +#define _MD_BIND (_PR_MD_BIND) +#define _MD_RECV (_PR_MD_RECV) +#define _MD_SEND (_PR_MD_SEND) + +/* --- Scheduler stuff --- */ +/* #define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU */ +#define _MD_PAUSE_CPU + +/* --- DIR stuff --- */ +#define PR_DIRECTORY_SEPARATOR '\\' +#define PR_DIRECTORY_SEPARATOR_STR "\\" +#define PR_PATH_SEPARATOR ';' +#define PR_PATH_SEPARATOR_STR ";" +#define _MD_ERRNO() errno +#define _MD_OPEN_DIR (_PR_MD_OPEN_DIR) +#define _MD_CLOSE_DIR (_PR_MD_CLOSE_DIR) +#define _MD_READ_DIR (_PR_MD_READ_DIR) + +/* --- Segment stuff --- */ +#define _MD_INIT_SEGS() +#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0 +#define _MD_FREE_SEGMENT(seg) + +/* --- Environment Stuff --- */ +#define _MD_GET_ENV (_PR_MD_GET_ENV) +#define _MD_PUT_ENV (_PR_MD_PUT_ENV) + +/* --- Threading Stuff --- */ +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_INIT_THREAD (_PR_MD_INIT_THREAD) +#define _MD_INIT_ATTACHED_THREAD (_PR_MD_INIT_THREAD) +#define _MD_CREATE_THREAD (_PR_MD_CREATE_THREAD) +#define _MD_YIELD (_PR_MD_YIELD) +#define _MD_SET_PRIORITY (_PR_MD_SET_PRIORITY) +#define _MD_CLEAN_THREAD (_PR_MD_CLEAN_THREAD) +#define _MD_SETTHREADAFFINITYMASK (_PR_MD_SETTHREADAFFINITYMASK) +#define _MD_GETTHREADAFFINITYMASK (_PR_MD_GETTHREADAFFINITYMASK) +#define _MD_EXIT_THREAD (_PR_MD_EXIT_THREAD) +#define _MD_SUSPEND_THREAD (_PR_MD_SUSPEND_THREAD) +#define _MD_RESUME_THREAD (_PR_MD_RESUME_THREAD) +#define _MD_SUSPEND_CPU (_PR_MD_SUSPEND_CPU) +#define _MD_RESUME_CPU (_PR_MD_RESUME_CPU) +#define _MD_WAKEUP_CPUS (_PR_MD_WAKEUP_CPUS) +#define _MD_BEGIN_SUSPEND_ALL() +#define _MD_BEGIN_RESUME_ALL() +#define _MD_END_SUSPEND_ALL() +#define _MD_END_RESUME_ALL() + +/* --- Lock stuff --- */ +#define _PR_LOCK _MD_LOCK +#define _PR_UNLOCK _MD_UNLOCK + +#define _MD_NEW_LOCK (_PR_MD_NEW_LOCK) +#define _MD_FREE_LOCK(lock) (DosCloseMutexSem((lock)->mutex)) +#define _MD_LOCK(lock) (DosRequestMutexSem((lock)->mutex, SEM_INDEFINITE_WAIT)) +#define _MD_TEST_AND_LOCK(lock) (DosRequestMutexSem((lock)->mutex, SEM_INDEFINITE_WAIT),0) +#define _MD_UNLOCK (_PR_MD_UNLOCK) + +/* --- lock and cv waiting --- */ +#define _MD_WAIT (_PR_MD_WAIT) +#define _MD_WAKEUP_WAITER (_PR_MD_WAKEUP_WAITER) + +/* --- CVar ------------------- */ +#define _MD_WAIT_CV (_PR_MD_WAIT_CV) +#define _MD_NEW_CV (_PR_MD_NEW_CV) +#define _MD_FREE_CV (_PR_MD_FREE_CV) +#define _MD_NOTIFY_CV (_PR_MD_NOTIFY_CV ) +#define _MD_NOTIFYALL_CV (_PR_MD_NOTIFYALL_CV) + +/* XXXMB- the IOQ stuff is certainly not working correctly yet. */ +/* extern struct _MDLock _pr_ioq_lock; */ +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + + +/* --- Initialization stuff --- */ +#define _MD_START_INTERRUPTS() +#define _MD_STOP_INTERRUPTS() +#define _MD_DISABLE_CLOCK_INTERRUPTS() +#define _MD_ENABLE_CLOCK_INTERRUPTS() +#define _MD_BLOCK_CLOCK_INTERRUPTS() +#define _MD_UNBLOCK_CLOCK_INTERRUPTS() +#define _MD_EARLY_INIT (_PR_MD_EARLY_INIT) +#define _MD_FINAL_INIT() +#define _MD_EARLY_CLEANUP() +#define _MD_INIT_CPUS() +#define _MD_INIT_RUNNING_CPU(cpu) + +struct PRProcess; +struct PRProcessAttr; + +#define _MD_CREATE_PROCESS _PR_CreateOS2Process +extern struct PRProcess * _PR_CreateOS2Process( + const char *path, + char *const *argv, + char *const *envp, + const struct PRProcessAttr *attr +); + +#define _MD_DETACH_PROCESS _PR_DetachOS2Process +extern PRStatus _PR_DetachOS2Process(struct PRProcess *process); + +/* --- Wait for a child process to terminate --- */ +#define _MD_WAIT_PROCESS _PR_WaitOS2Process +extern PRStatus _PR_WaitOS2Process(struct PRProcess *process, + PRInt32 *exitCode); + +#define _MD_KILL_PROCESS _PR_KillOS2Process +extern PRStatus _PR_KillOS2Process(struct PRProcess *process); + +#define _MD_CLEANUP_BEFORE_EXIT() +#define _MD_EXIT (_PR_MD_EXIT) +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + *status = PR_TRUE; \ + PR_END_MACRO +#define _MD_SWITCH_CONTEXT +#define _MD_RESTORE_CONTEXT + +/* --- Intervals --- */ +#define _MD_INTERVAL_INIT (_PR_MD_INTERVAL_INIT) +#define _MD_GET_INTERVAL (_PR_MD_GET_INTERVAL) +#define _MD_INTERVAL_PER_SEC (_PR_MD_INTERVAL_PER_SEC) +#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000) +#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000) + +/* --- Native-Thread Specific Definitions ------------------------------- */ + +typedef struct __NSPR_TLS +{ + struct PRThread *_pr_thread_last_run; + struct PRThread *_pr_currentThread; + struct _PRCPU *_pr_currentCPU; +} _NSPR_TLS; + +extern _NSPR_TLS* pThreadLocalStorage; +NSPR_API(void) _PR_MD_ENSURE_TLS(void); + +#define _MD_GET_ATTACHED_THREAD() pThreadLocalStorage->_pr_currentThread +extern struct PRThread * _MD_CURRENT_THREAD(void); +#define _MD_SET_CURRENT_THREAD(_thread) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_currentThread = (_thread) + +#define _MD_LAST_THREAD() pThreadLocalStorage->_pr_thread_last_run +#define _MD_SET_LAST_THREAD(_thread) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_thread_last_run = (_thread) + +#define _MD_CURRENT_CPU() pThreadLocalStorage->_pr_currentCPU +#define _MD_SET_CURRENT_CPU(_cpu) _PR_MD_ENSURE_TLS(); pThreadLocalStorage->_pr_currentCPU = (_cpu) + +/* lth. #define _MD_SET_INTSOFF(_val) (_pr_ints_off = (_val)) */ +/* lth. #define _MD_GET_INTSOFF() _pr_ints_off */ +/* lth. #define _MD_INCREMENT_INTSOFF() (_pr_ints_off++) */ +/* lth. #define _MD_DECREMENT_INTSOFF() (_pr_ints_off--) */ + +/* --- Scheduler stuff --- */ +#define LOCK_SCHEDULER() 0 +#define UNLOCK_SCHEDULER() 0 +#define _PR_LockSched() 0 +#define _PR_UnlockSched() 0 + +/* --- Initialization stuff --- */ +#define _MD_INIT_LOCKS() + +/* --- Stack stuff --- */ +#define _MD_INIT_STACK(stack, redzone) +#define _MD_CLEAR_STACK(stack) + +/* --- Memory-mapped files stuff --- */ +/* ReadOnly and WriteCopy modes are simulated on OS/2; + * ReadWrite mode is not supported. + */ +struct _MDFileMap { + PROffset64 maxExtent; +}; + +extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size); +#define _MD_CREATE_FILE_MAP _MD_CreateFileMap + +extern PRInt32 _MD_GetMemMapAlignment(void); +#define _MD_GET_MEM_MAP_ALIGNMENT _MD_GetMemMapAlignment + +extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset, + PRUint32 len); +#define _MD_MEM_MAP _MD_MemMap + +extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size); +#define _MD_MEM_UNMAP _MD_MemUnmap + +extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap); +#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap + +/* Some stuff for setting up thread contexts */ +typedef ULONG DWORD, *PDWORD; + +/* The following definitions and two structures are new in OS/2 Warp 4.0. + */ +#ifndef CONTEXT_CONTROL +#define CONTEXT_CONTROL 0x00000001 +#define CONTEXT_INTEGER 0x00000002 +#define CONTEXT_SEGMENTS 0x00000004 +#define CONTEXT_FLOATING_POINT 0x00000008 +#define CONTEXT_FULL 0x0000000F + +#pragma pack(2) +typedef struct _FPREG { + ULONG losig; /* Low 32-bits of the significand. */ + ULONG hisig; /* High 32-bits of the significand. */ + USHORT signexp; /* Sign and exponent. */ +} FPREG; +typedef struct _CONTEXTRECORD { + ULONG ContextFlags; + ULONG ctx_env[7]; + FPREG ctx_stack[8]; + ULONG ctx_SegGs; /* GS register. */ + ULONG ctx_SegFs; /* FS register. */ + ULONG ctx_SegEs; /* ES register. */ + ULONG ctx_SegDs; /* DS register. */ + ULONG ctx_RegEdi; /* EDI register. */ + ULONG ctx_RegEsi; /* ESI register. */ + ULONG ctx_RegEax; /* EAX register. */ + ULONG ctx_RegEbx; /* EBX register. */ + ULONG ctx_RegEcx; /* ECX register. */ + ULONG ctx_RegEdx; /* EDX register. */ + ULONG ctx_RegEbp; /* EBP register. */ + ULONG ctx_RegEip; /* EIP register. */ + ULONG ctx_SegCs; /* CS register. */ + ULONG ctx_EFlags; /* EFLAGS register. */ + ULONG ctx_RegEsp; /* ESP register. */ + ULONG ctx_SegSs; /* SS register. */ +} CONTEXTRECORD, *PCONTEXTRECORD; +#pragma pack() +#endif + +extern APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD); + +/* +#define _pr_tid (((PTIB2)_getTIBvalue(offsetof(TIB, tib_ptib2)))->tib2_ultid) +#define _pr_current_Thread (_system_tls[_pr_tid-1].__pr_current_thread) +*/ + +/* Some simple mappings of Windows API's to OS/2 API's to make our lives a + * little bit easier. Only add one here if it is a DIRECT mapping. We are + * not emulating anything. Just mapping. + */ +#define FreeLibrary(x) DosFreeModule((HMODULE)x) +#define OutputDebugStringA(x) + +extern int _MD_os2_get_nonblocking_connect_error(int osfd); + +#endif /* nspr_os2_defs_h___ */ diff --git a/nsprpub/pr/include/md/_os2_errors.h b/nsprpub/pr/include/md/_os2_errors.h new file mode 100644 index 0000000000..556a9ae8d4 --- /dev/null +++ b/nsprpub/pr/include/md/_os2_errors.h @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_os2_errors_h___ +#define nspr_os2_errors_h___ + +#include "md/_os2.h" +#ifndef assert +#include <assert.h> +#endif + +NSPR_API(void) _MD_os2_map_default_error(PRInt32 err); +#define _PR_MD_MAP_DEFAULT_ERROR _MD_os2_map_default_error + +NSPR_API(void) _MD_os2_map_opendir_error(PRInt32 err); +#define _PR_MD_MAP_OPENDIR_ERROR _MD_os2_map_opendir_error + +NSPR_API(void) _MD_os2_map_closedir_error(PRInt32 err); +#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_os2_map_closedir_error + +NSPR_API(void) _MD_os2_readdir_error(PRInt32 err); +#define _PR_MD_MAP_READDIR_ERROR _MD_os2_readdir_error + +NSPR_API(void) _MD_os2_map_delete_error(PRInt32 err); +#define _PR_MD_MAP_DELETE_ERROR _MD_os2_map_delete_error + +NSPR_API(void) _MD_os2_map_stat_error(PRInt32 err); +#define _PR_MD_MAP_STAT_ERROR _MD_os2_map_stat_error + +NSPR_API(void) _MD_os2_map_fstat_error(PRInt32 err); +#define _PR_MD_MAP_FSTAT_ERROR _MD_os2_map_fstat_error + +NSPR_API(void) _MD_os2_map_rename_error(PRInt32 err); +#define _PR_MD_MAP_RENAME_ERROR _MD_os2_map_rename_error + +NSPR_API(void) _MD_os2_map_access_error(PRInt32 err); +#define _PR_MD_MAP_ACCESS_ERROR _MD_os2_map_access_error + +NSPR_API(void) _MD_os2_map_mkdir_error(PRInt32 err); +#define _PR_MD_MAP_MKDIR_ERROR _MD_os2_map_mkdir_error + +NSPR_API(void) _MD_os2_map_rmdir_error(PRInt32 err); +#define _PR_MD_MAP_RMDIR_ERROR _MD_os2_map_rmdir_error + +NSPR_API(void) _MD_os2_map_read_error(PRInt32 err); +#define _PR_MD_MAP_READ_ERROR _MD_os2_map_read_error + +NSPR_API(void) _MD_os2_map_transmitfile_error(PRInt32 err); +#define _PR_MD_MAP_TRANSMITFILE_ERROR _MD_os2_map_transmitfile_error + +NSPR_API(void) _MD_os2_map_write_error(PRInt32 err); +#define _PR_MD_MAP_WRITE_ERROR _MD_os2_map_write_error + +NSPR_API(void) _MD_os2_map_lseek_error(PRInt32 err); +#define _PR_MD_MAP_LSEEK_ERROR _MD_os2_map_lseek_error + +NSPR_API(void) _MD_os2_map_fsync_error(PRInt32 err); +#define _PR_MD_MAP_FSYNC_ERROR _MD_os2_map_fsync_error + +NSPR_API(void) _MD_os2_map_close_error(PRInt32 err); +#define _PR_MD_MAP_CLOSE_ERROR _MD_os2_map_close_error + +NSPR_API(void) _MD_os2_map_socket_error(PRInt32 err); +#define _PR_MD_MAP_SOCKET_ERROR _MD_os2_map_socket_error + +NSPR_API(void) _MD_os2_map_recv_error(PRInt32 err); +#define _PR_MD_MAP_RECV_ERROR _MD_os2_map_recv_error + +NSPR_API(void) _MD_os2_map_recvfrom_error(PRInt32 err); +#define _PR_MD_MAP_RECVFROM_ERROR _MD_os2_map_recvfrom_error + +NSPR_API(void) _MD_os2_map_send_error(PRInt32 err); +#define _PR_MD_MAP_SEND_ERROR _MD_os2_map_send_error + +NSPR_API(void) _MD_os2_map_sendto_error(PRInt32 err); +#define _PR_MD_MAP_SENDTO_ERROR _MD_os2_map_sendto_error + +NSPR_API(void) _MD_os2_map_writev_error(int err); +#define _PR_MD_MAP_WRITEV_ERROR _MD_os2_map_writev_error + +NSPR_API(void) _MD_os2_map_accept_error(PRInt32 err); +#define _PR_MD_MAP_ACCEPT_ERROR _MD_os2_map_accept_error + +NSPR_API(void) _MD_os2_map_acceptex_error(PRInt32 err); +#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_os2_map_acceptex_error + +NSPR_API(void) _MD_os2_map_connect_error(PRInt32 err); +#define _PR_MD_MAP_CONNECT_ERROR _MD_os2_map_connect_error + +NSPR_API(void) _MD_os2_map_bind_error(PRInt32 err); +#define _PR_MD_MAP_BIND_ERROR _MD_os2_map_bind_error + +NSPR_API(void) _MD_os2_map_listen_error(PRInt32 err); +#define _PR_MD_MAP_LISTEN_ERROR _MD_os2_map_listen_error + +NSPR_API(void) _MD_os2_map_shutdown_error(PRInt32 err); +#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_os2_map_shutdown_error + +NSPR_API(void) _MD_os2_map_socketpair_error(int err); +#define _PR_MD_MAP_SOCKETPAIR_ERROR _MD_os2_map_socketpair_error + +NSPR_API(void) _MD_os2_map_getsockname_error(PRInt32 err); +#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_os2_map_getsockname_error + +NSPR_API(void) _MD_os2_map_getpeername_error(PRInt32 err); +#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_os2_map_getpeername_error + +NSPR_API(void) _MD_os2_map_getsockopt_error(PRInt32 err); +#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_os2_map_getsockopt_error + +NSPR_API(void) _MD_os2_map_setsockopt_error(PRInt32 err); +#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_os2_map_setsockopt_error + +NSPR_API(void) _MD_os2_map_open_error(PRInt32 err); +#define _PR_MD_MAP_OPEN_ERROR _MD_os2_map_open_error + +NSPR_API(void) _MD_os2_map_gethostname_error(PRInt32 err); +#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_os2_map_gethostname_error + +NSPR_API(void) _MD_os2_map_select_error(PRInt32 err); +#define _PR_MD_MAP_SELECT_ERROR _MD_os2_map_select_error + +NSPR_API(void) _MD_os2_map_lockf_error(int err); +#define _PR_MD_MAP_LOCKF_ERROR _MD_os2_map_lockf_error + +#endif /* nspr_os2_errors_h___ */ diff --git a/nsprpub/pr/include/md/_pcos.h b/nsprpub/pr/include/md/_pcos.h new file mode 100644 index 0000000000..679fe19b10 --- /dev/null +++ b/nsprpub/pr/include/md/_pcos.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prpcos_h___ +#define prpcos_h___ + +#define PR_DLL_SUFFIX ".dll" + +#include <stdlib.h> + +#define DIRECTORY_SEPARATOR '\\' +#define DIRECTORY_SEPARATOR_STR "\\" +#define PATH_SEPARATOR ';' + +/* +** Routines for processing command line arguments +*/ +PR_BEGIN_EXTERN_C +#ifndef XP_OS2 +extern char *optarg; +extern int optind; +extern int getopt(int argc, char **argv, char *spec); +#endif +PR_END_EXTERN_C + + +/* +** Definitions of directory structures amd functions +** These definitions are from: +** <dirent.h> +*/ +#ifdef XP_OS2 +#include <sys/types.h> +#endif +#include <sys/stat.h> +#include <io.h> +#include <fcntl.h> /* O_BINARY */ + +#ifdef OS2 +extern PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen); +#define _MD_GETHOSTNAME _MD_OS2GetHostName +#else +extern PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen); +#define _MD_GETHOSTNAME _MD_WindowsGetHostName +extern PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char *name, PRUint32 namelen); +#define _MD_GETSYSINFO _MD_WindowsGetSysInfo +#endif + +#endif /* prpcos_h___ */ diff --git a/nsprpub/pr/include/md/_pth.h b/nsprpub/pr/include/md/_pth.h new file mode 100644 index 0000000000..37d26e7005 --- /dev/null +++ b/nsprpub/pr/include/md/_pth.h @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_pth_defs_h_ +#define nspr_pth_defs_h_ + +/* +** Appropriate definitions of entry points not used in a pthreads world +*/ +#define _PR_MD_BLOCK_CLOCK_INTERRUPTS() +#define _PR_MD_UNBLOCK_CLOCK_INTERRUPTS() +#define _PR_MD_DISABLE_CLOCK_INTERRUPTS() +#define _PR_MD_ENABLE_CLOCK_INTERRUPTS() + +#if defined(BSDI) +/* + * Mutex and condition attributes are not supported. The attr + * argument to pthread_mutex_init() and pthread_cond_init() must + * be passed as NULL. + * + * The memset calls in _PT_PTHREAD_MUTEX_INIT and _PT_PTHREAD_COND_INIT + * are to work around BSDI's using a single bit to indicate a mutex + * or condition variable is initialized. This entire BSDI section + * will go away when BSDI releases updated threads libraries for + * BSD/OS 3.1 and 4.0. + */ +#define _PT_PTHREAD_MUTEXATTR_INIT(x) 0 +#define _PT_PTHREAD_MUTEXATTR_DESTROY(x) /* */ +#define _PT_PTHREAD_MUTEX_INIT(m, a) (memset(&(m), 0, sizeof(m)), \ + pthread_mutex_init(&(m), NULL)) +#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) (EBUSY == pthread_mutex_trylock(&(m))) +#define _PT_PTHREAD_CONDATTR_INIT(x) 0 +#define _PT_PTHREAD_CONDATTR_DESTROY(x) /* */ +#define _PT_PTHREAD_COND_INIT(m, a) (memset(&(m), 0, sizeof(m)), \ + pthread_cond_init(&(m), NULL)) +#else +#define _PT_PTHREAD_MUTEXATTR_INIT pthread_mutexattr_init +#define _PT_PTHREAD_MUTEXATTR_DESTROY pthread_mutexattr_destroy +#define _PT_PTHREAD_MUTEX_INIT(m, a) pthread_mutex_init(&(m), &(a)) +#if defined(FREEBSD) +#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) pt_pthread_mutex_is_locked(&(m)) +#else +#define _PT_PTHREAD_MUTEX_IS_LOCKED(m) (EBUSY == pthread_mutex_trylock(&(m))) +#endif +#if defined(ANDROID) +/* Conditional attribute init and destroy aren't implemented in bionic. */ +#define _PT_PTHREAD_CONDATTR_INIT(x) 0 +#define _PT_PTHREAD_CONDATTR_DESTROY(x) /* */ +#else +#define _PT_PTHREAD_CONDATTR_INIT pthread_condattr_init +#define _PT_PTHREAD_CONDATTR_DESTROY pthread_condattr_destroy +#endif +#define _PT_PTHREAD_COND_INIT(m, a) pthread_cond_init(&(m), &(a)) +#endif + +/* The pthreads standard does not specify an invalid value for the + * pthread_t handle. (0 is usually an invalid pthread identifier + * but there are exceptions, for example, DG/UX.) These macros + * define a way to set the handle to or compare the handle with an + * invalid identifier. These macros are not portable and may be + * more of a problem as we adapt to more pthreads implementations. + * They are only used in the PRMonitor functions. Do not use them + * in new code. + * + * Unfortunately some of our clients depend on certain properties + * of our PRMonitor implementation, preventing us from replacing + * it by a portable implementation. + * - High-performance servers like the fact that PR_EnterMonitor + * only calls PR_Lock and PR_ExitMonitor only calls PR_Unlock. + * (A portable implementation would use a PRLock and a PRCondVar + * to implement the recursive lock in a monitor and call both + * PR_Lock and PR_Unlock in PR_EnterMonitor and PR_ExitMonitor.) + * Unfortunately this forces us to read the monitor owner field + * without holding a lock. + * - One way to make it safe to read the monitor owner field + * without holding a lock is to make that field a PRThread* + * (one should be able to read a pointer with a single machine + * instruction). However, PR_GetCurrentThread calls calloc if + * it is called by a thread that was not created by NSPR. The + * malloc tracing tools in the Mozilla client use PRMonitor for + * locking in their malloc, calloc, and free functions. If + * PR_EnterMonitor calls any of these functions, infinite + * recursion ensues. + */ +#if defined(AIX) || defined(SOLARIS) \ + || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(HPUX) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD) || defined(BSDI) \ + || defined(NTO) || defined(DARWIN) \ + || defined(UNIXWARE) || defined(RISCOS) +#define _PT_PTHREAD_INVALIDATE_THR_HANDLE(t) (t) = 0 +#define _PT_PTHREAD_THR_HANDLE_IS_INVALID(t) (t) == 0 +#define _PT_PTHREAD_COPY_THR_HANDLE(st, dt) (dt) = (st) +#else +#error "pthreads is not supported for this architecture" +#endif + +#if defined(_PR_PTHREADS) +#define _PT_PTHREAD_ATTR_INIT pthread_attr_init +#define _PT_PTHREAD_ATTR_DESTROY pthread_attr_destroy +#define _PT_PTHREAD_CREATE(t, a, f, r) pthread_create(t, &a, f, r) +#define _PT_PTHREAD_KEY_CREATE pthread_key_create +#define _PT_PTHREAD_ATTR_SETSCHEDPOLICY pthread_attr_setschedpolicy +#define _PT_PTHREAD_ATTR_GETSTACKSIZE(a, s) pthread_attr_getstacksize(a, s) +#define _PT_PTHREAD_GETSPECIFIC(k, r) (r) = pthread_getspecific(k) +#else +#error "Cannot determine pthread strategy" +#endif + +/* + * These platforms don't have sigtimedwait() + */ +#if (defined(AIX) && !defined(AIX4_3_PLUS)) \ + || defined(LINUX) || defined(__GNU__)|| defined(__GLIBC__) \ + || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \ + || defined(BSDI) || defined(UNIXWARE) \ + || defined(DARWIN) +#define PT_NO_SIGTIMEDWAIT +#endif + +#if defined(AIX) +#include <sys/priv.h> +#include <sys/sched.h> +#ifndef PTHREAD_CREATE_JOINABLE +#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED +#endif +#define PT_PRIO_MIN DEFAULT_PRIO +#define PT_PRIO_MAX DEFAULT_PRIO +#elif defined(HPUX) +#include <sys/sched.h> +#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER) +#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER) +#elif defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(FREEBSD) +#define PT_PRIO_MIN sched_get_priority_min(SCHED_OTHER) +#define PT_PRIO_MAX sched_get_priority_max(SCHED_OTHER) +#elif defined(NTO) +/* + * Neutrino has functions that return the priority range but + * they return invalid numbers, so I just hard coded these here + * for now. Jerry.Kirk@Nexarecorp.com + */ +#define PT_PRIO_MIN 0 +#define PT_PRIO_MAX 30 +#elif defined(SOLARIS) +/* + * Solaris doesn't seem to have macros for the min/max priorities. + * The range of 0-127 is mentioned in the pthread_setschedparam(3T) + * man pages, and pthread_setschedparam indeed allows 0-127. However, + * pthread_attr_setschedparam does not allow 0; it allows 1-127. + */ +#define PT_PRIO_MIN 1 +#define PT_PRIO_MAX 127 +#elif defined(OPENBSD) +#define PT_PRIO_MIN 0 +#define PT_PRIO_MAX 31 +#elif defined(NETBSD) \ + || defined(BSDI) || defined(DARWIN) || defined(UNIXWARE) \ + || defined(RISCOS) /* XXX */ +#define PT_PRIO_MIN 0 +#define PT_PRIO_MAX 126 +#else +#error "pthreads is not supported for this architecture" +#endif + +/* + * The _PT_PTHREAD_YIELD function is called from a signal handler. + * Needed for garbage collection -- Look at PR_Suspend/PR_Resume + * implementation. + */ +#if defined(AIX) +extern int (*_PT_aix_yield_fcn)(); +#define _PT_PTHREAD_YIELD() (*_PT_aix_yield_fcn)() +#elif defined(HPUX) || defined(SOLARIS) \ + || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \ + || defined(BSDI) || defined(NTO) || defined(DARWIN) \ + || defined(UNIXWARE) || defined(RISCOS) +#define _PT_PTHREAD_YIELD() sched_yield() +#else +#error "Need to define _PT_PTHREAD_YIELD for this platform" +#endif + +#endif /* nspr_pth_defs_h_ */ diff --git a/nsprpub/pr/include/md/_qnx.cfg b/nsprpub/pr/include/md/_qnx.cfg new file mode 100644 index 0000000000..a459b8cb7c --- /dev/null +++ b/nsprpub/pr/include/md/_qnx.cfg @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef QNX +#define QNX +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#undef HAVE_LONG_LONG +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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_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 1 +#define PR_ALIGN_OF_INT 1 +#define PR_ALIGN_OF_LONG 1 +#define PR_ALIGN_OF_INT64 1 +#define PR_ALIGN_OF_FLOAT 1 +#define PR_ALIGN_OF_DOUBLE 1 +#define PR_ALIGN_OF_POINTER 1 +#define PR_ALIGN_OF_WORD 1 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 +#define PR_WORDS_PER_DWORD_LOG2 1 + +#endif /* nspr_cpucfg___ */ diff --git a/nsprpub/pr/include/md/_qnx.h b/nsprpub/pr/include/md/_qnx.h new file mode 100644 index 0000000000..923e174386 --- /dev/null +++ b/nsprpub/pr/include/md/_qnx.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_qnx_defs_h___ +#define nspr_qnx_defs_h___ + +/* +** Internal configuration macros +*/ +#define PR_LINKER_ARCH "qnx" +#define _PR_SI_SYSNAME "QNX" +#define _PR_SI_ARCHITECTURE "x86" +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#ifndef HAVE_WEAK_IO_SYMBOLS +#define HAVE_WEAK_IO_SYMBOLS +#endif + +#undef _PR_POLL_AVAILABLE +#undef _PR_USE_POLL +#define _PR_HAVE_SOCKADDR_LEN +#define HAVE_BSD_FLOCK +#define _PR_NO_LARGE_FILES +#define _PR_STAT_HAS_ONLY_ST_ATIME + +#include <sys/select.h> + +#undef HAVE_STACK_GROWING_UP +#undef HAVE_DLL +#undef USE_DLFCN +#define NEED_STRFTIME_LOCK +#define NEED_TIME_R +#define _PR_NEED_STRCASECMP + +#ifndef HAVE_STRERROR +#define HAVE_STRERROR +#endif + +#define USE_SETJMP + +#include <setjmp.h> + +#define _SETJMP setjmp +#define _LONGJMP longjmp +#define _PR_CONTEXT_TYPE jmp_buf +#define _PR_NUM_GCREGS _JBLEN +#define _MD_GET_SP(_t) (_t)->md.context[7] + +#define CONTEXT(_th) ((_th)->md.context) + +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if(_SETJMP(CONTEXT(_thread))) (*_main)(); \ + _MD_GET_SP(_thread) = (int) ((_sp) - 128); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!_SETJMP(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + _LONGJMP(CONTEXT(_thread), 1); \ +} + +/* +** Machine-dependent (MD) data structures. +*/ +struct _MDThread { + _PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* +** md-specific cpu structure field +*/ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD], fd_write_cnt[_PR_MD_MAX_OSFD], fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INTERVAL_USE_GTOD +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +/* +** We wrapped the select() call. _MD_SELECT refers to the built-in, +** unwrapped version. +*/ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#define _MD_SELECT select + +#define SA_RESTART 0 + +#endif /* nspr_qnx_defs_h___ */ diff --git a/nsprpub/pr/include/md/_riscos.cfg b/nsprpub/pr/include/md/_riscos.cfg new file mode 100644 index 0000000000..c7adf2e135 --- /dev/null +++ b/nsprpub/pr/include/md/_riscos.cfg @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef RISCOS +#define RISCOS +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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_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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 +#define PR_WORDS_PER_DWORD_LOG2 1 + +#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/nsprpub/pr/include/md/_riscos.h b/nsprpub/pr/include/md/_riscos.h new file mode 100644 index 0000000000..ec03dcbd7e --- /dev/null +++ b/nsprpub/pr/include/md/_riscos.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_riscos_defs_h___ +#define nspr_riscos_defs_h___ + +/* +** Internal configuration macros +*/ +#define PR_LINKER_ARCH "riscos" +#define _PR_SI_SYSNAME "RISCOS" +#define _PR_SI_ARCHITECTURE "arm" +#define PR_DLL_SUFFIX ".so" + +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_HAVE_SOCKADDR_LEN +#undef HAVE_BSD_FLOCK +#define _PR_NO_LARGE_FILES +#define _PR_STAT_HAS_ONLY_ST_ATIME +#define _PR_HAVE_POSIX_SEMAPHORES + +#include <sys/select.h> +#include <sys/poll.h> +#include <kernel.h> + + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN +#define NEED_STRFTIME_LOCK +#define NEED_TIME_R +#define PT_NO_SIGTIMEDWAIT + +#ifndef HAVE_STRERROR +#define HAVE_STRERROR +#endif + +#define USE_SETJMP + +#include <setjmp.h> + +#define _SETJMP setjmp +#define _LONGJMP longjmp +#define _PR_CONTEXT_TYPE jmp_buf +#define _PR_NUM_GCREGS _JBLEN +#define _MD_GET_SP(_t) (_t)->md.context[7] + +#define CONTEXT(_th) ((_th)->md.context) + + +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if(_SETJMP(CONTEXT(_thread))) (*_main)(); \ + _MD_GET_SP(_thread) = (int) ((_sp) - 128); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!_SETJMP(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + _LONGJMP(CONTEXT(_thread), 1); \ +} + +/* +** Machine-dependent (MD) data structures. +*/ +struct _MDThread { + _PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* +** md-specific cpu structure field +*/ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD], fd_write_cnt[_PR_MD_MAX_OSFD], fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif +}; + +#define _PR_IOQ(_cpu) /* */ ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INTERVAL_USE_GTOD +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +/* +** We wrapped the select() call. _MD_SELECT refers to the built-in, +** unwrapped version. +*/ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#define _MD_SELECT select + +#endif /* nspr_riscos_defs_h___ */ diff --git a/nsprpub/pr/include/md/_scoos.cfg b/nsprpub/pr/include/md/_scoos.cfg new file mode 100644 index 0000000000..70cc6b7109 --- /dev/null +++ b/nsprpub/pr/include/md/_scoos.cfg @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef SCO +#define SCO +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#undef HAVE_LONG_LONG +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define _PR_POLL_BACKCOMPAT + +#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/nsprpub/pr/include/md/_scoos.h b/nsprpub/pr/include/md/_scoos.h new file mode 100644 index 0000000000..1f6dda1535 --- /dev/null +++ b/nsprpub/pr/include/md/_scoos.h @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_scoos5_defs_h___ +#define nspr_scoos5_defs_h___ + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "scoos5" +#define PR_DLL_SUFFIX ".so" + +#define _PR_SI_SYSNAME "SCO" +#define _PR_SI_ARCHITECTURE "x86" +#define _PR_STACK_VMBASE 0x50000000 + +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#undef HAVE_STACK_GROWING_UP +#define HAVE_DLL +#define USE_DLFCN + +#if !defined (HAVE_STRERROR) +#define HAVE_STRERROR +#endif + +#ifndef HAVE_WEAK_IO_SYMBOLS +#define HAVE_WEAK_IO_SYMBOLS +#endif + +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_NO_LARGE_FILES +#define _PR_STAT_HAS_ONLY_ST_ATIME + +#define NEED_STRFTIME_LOCK +#define NEED_TIME_R +#define _PR_RECV_BROKEN /* recv doesn't work on Unix Domain Sockets */ + +#define USE_SETJMP + +#ifdef _PR_LOCAL_THREADS_ONLY +#include <setjmp.h> + +#define _MD_GET_SP(_t) (_t)->md.jb[4] +#define PR_NUM_GCREGS _SIGJBLEN +#define PR_CONTEXT_TYPE sigjmp_buf + +#define CONTEXT(_th) ((_th)->md.jb) + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if (sigsetjmp(CONTEXT(_thread),1)) { \ + (*_main)(); \ + } \ + _MD_GET_SP(_thread) = (int) ((_sp) - 64); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!sigsetjmp(CONTEXT(_thread), 1)) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->osErrorCode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + siglongjmp(CONTEXT(_thread), 1); \ +} + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +struct _MDThread { + jmp_buf jb; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +#define _MD_INTERVAL_USE_GTOD + +#define _MD_SELECT _select +#define _MD_POLL _poll + +#endif /* nspr_scoos5_defs_h___ */ diff --git a/nsprpub/pr/include/md/_solaris.cfg b/nsprpub/pr/include/md/_solaris.cfg new file mode 100644 index 0000000000..674563747a --- /dev/null +++ b/nsprpub/pr/include/md/_solaris.cfg @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef SOLARIS +#define SOLARIS +#endif + +#define PR_AF_INET6 26 /* same as AF_INET6 */ + +#if defined(sparc) || defined(__sparc) +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_DOUBLE 8 +#if defined(__sparcv9) +#define IS_64 +#endif +#elif defined(__x86_64) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define IS_64 +#elif defined(i386) || 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 + +#ifdef 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 + +#else /* 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 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_FLOAT 4 +#define PR_ALIGN_OF_POINTER 4 + +#endif /* IS_64 */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#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/nsprpub/pr/include/md/_solaris.h b/nsprpub/pr/include/md/_solaris.h new file mode 100644 index 0000000000..4ebf77fd96 --- /dev/null +++ b/nsprpub/pr/include/md/_solaris.h @@ -0,0 +1,493 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_solaris_defs_h___ +#define nspr_solaris_defs_h___ + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "solaris" +#define _PR_SI_SYSNAME "SOLARIS" +#ifdef sparc +#define _PR_SI_ARCHITECTURE "sparc" +#elif defined(__x86_64) +#define _PR_SI_ARCHITECTURE "x86-64" +#elif defined(i386) +#define _PR_SI_ARCHITECTURE "x86" +#else +#error unknown processor +#endif +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE (2*65536L) +#define _MD_MMAP_FLAGS MAP_SHARED + +#undef HAVE_STACK_GROWING_UP + +#ifndef HAVE_WEAK_IO_SYMBOLS +#define HAVE_WEAK_IO_SYMBOLS +#endif + +#undef HAVE_WEAK_MALLOC_SYMBOLS +#define HAVE_DLL +#define USE_DLFCN +#define NEED_STRFTIME_LOCK + +/* + * Intel x86 has atomic instructions. + * + * Sparc v8 does not have instructions to efficiently implement + * atomic increment/decrement operations. We use the default + * atomic routine implementation in pratom.c. + * + * 64-bit Solaris requires sparc v9, which has atomic instructions. + */ +#if defined(i386) || defined(IS_64) +#define _PR_HAVE_ATOMIC_OPS +#endif + +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_STAT_HAS_ST_ATIM +#ifdef SOLARIS2_5 +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY +#else +#define _PR_HAVE_POSIX_SEMAPHORES +#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY +#endif +#define _PR_HAVE_GETIPNODEBYNAME +#define _PR_HAVE_GETIPNODEBYADDR +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#define _PR_ACCEPT_INHERIT_NONBLOCK +#ifdef _PR_INET6 +#define _PR_HAVE_INET_NTOP +#else +#define AF_INET6 26 +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +#define AI_CANONNAME 0x0010 +#define AI_V4MAPPED 0x0001 +#define AI_ALL 0x0002 +#define AI_ADDRCONFIG 0x0004 +#define _PR_HAVE_MD_SOCKADDR_IN6 +/* isomorphic to struct in6_addr on Solaris 8 */ +struct _md_in6_addr { + union { + PRUint8 _S6_u8[16]; + PRUint32 _S6_u32[4]; + PRUint32 __S6_align; + } _S6_un; +}; +/* isomorphic to struct sockaddr_in6 on Solaris 8 */ +struct _md_sockaddr_in6 { + PRUint16 sin6_family; + PRUint16 sin6_port; + PRUint32 sin6_flowinfo; + struct _md_in6_addr sin6_addr; + PRUint32 sin6_scope_id; + PRUint32 __sin6_src_id; +}; +#endif +#if defined(_PR_PTHREADS) +#define _PR_HAVE_GETHOST_R +#define _PR_HAVE_GETHOST_R_POINTER +#endif + +#include "prinrval.h" +#define _MD_INTERVAL_INIT() +NSPR_API(PRIntervalTime) _MD_Solaris_GetInterval(void); +#define _MD_GET_INTERVAL _MD_Solaris_GetInterval +NSPR_API(PRIntervalTime) _MD_Solaris_TicksPerSecond(void); +#define _MD_INTERVAL_PER_SEC _MD_Solaris_TicksPerSecond + +#if defined(_PR_HAVE_ATOMIC_OPS) +/* +** Atomic Operations +*/ +#define _MD_INIT_ATOMIC() + +NSPR_API(PRInt32) _MD_AtomicIncrement(PRInt32 *val); +#define _MD_ATOMIC_INCREMENT _MD_AtomicIncrement + +NSPR_API(PRInt32) _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val); +#define _MD_ATOMIC_ADD _MD_AtomicAdd + +NSPR_API(PRInt32) _MD_AtomicDecrement(PRInt32 *val); +#define _MD_ATOMIC_DECREMENT _MD_AtomicDecrement + +NSPR_API(PRInt32) _MD_AtomicSet(PRInt32 *val, PRInt32 newval); +#define _MD_ATOMIC_SET _MD_AtomicSet +#endif /* _PR_HAVE_ATOMIC_OPS */ + +#if defined(_PR_PTHREADS) + +NSPR_API(void) _MD_EarlyInit(void); + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit + +#else /* _PR_PTHREADS */ + +/* + * _PR_LOCAL_THREADS_ONLY implementation on Solaris + */ + +#include "prthread.h" + +#include <errno.h> +#include <ucontext.h> +#include <sys/stack.h> +#include <synch.h> + +/* +** Initialization Related definitions +*/ + +NSPR_API(void) _MD_EarlyInit(void); +NSPR_API(void) _MD_SolarisInit(); +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _MD_SolarisInit +#define _MD_INIT_THREAD _MD_InitializeThread + +#ifdef USE_SETJMP + +#include <setjmp.h> + +#define _PR_CONTEXT_TYPE jmp_buf + +#ifdef sparc +#define _MD_GET_SP(_t) (_t)->md.context[2] +#else +#define _MD_GET_SP(_t) (_t)->md.context[4] +#endif + +#define PR_NUM_GCREGS _JBLEN +#define CONTEXT(_thread) (_thread)->md.context + +#else /* ! USE_SETJMP */ + +#ifdef sparc +#define _PR_CONTEXT_TYPE ucontext_t +#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[REG_SP] +/* +** Sparc's use register windows. the _MD_GetRegisters for the sparc's +** doesn't actually store anything into the argument buffer; instead the +** register windows are homed to the stack. I assume that the stack +** always has room for the registers to spill to... +*/ +#define PR_NUM_GCREGS 0 +#else +#define _PR_CONTEXT_TYPE unsigned int edi; sigset_t oldMask, blockMask; ucontext_t +#define _MD_GET_SP(_t) (_t)->md.context.uc_mcontext.gregs[USP] +#define PR_NUM_GCREGS _JBLEN +#endif + +#define CONTEXT(_thread) (&(_thread)->md.context) + +#endif /* ! USE_SETJMP */ + +#include <time.h> +/* + * Because clock_gettime() on Solaris/x86 always generates a + * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(), + * which is implemented using gettimeofday(). + */ +#ifdef i386 +#define GETTIME(tt) _pr_solx86_clock_gettime(CLOCK_REALTIME, (tt)) +#else +#define GETTIME(tt) clock_gettime(CLOCK_REALTIME, (tt)) +#endif /* i386 */ + +#define _MD_SAVE_ERRNO(_thread) (_thread)->md.errcode = errno; +#define _MD_RESTORE_ERRNO(_thread) errno = (_thread)->md.errcode; + +#ifdef sparc + +#ifdef USE_SETJMP +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + int *context = (_thread)->md.context; \ + *status = PR_TRUE; \ + (void) setjmp(context); \ + (_thread)->md.context[1] = (int) ((_sp) - 64); \ + (_thread)->md.context[2] = (int) _main; \ + (_thread)->md.context[3] = (int) _main + 4; \ + _thread->no_sched = 0; \ + PR_END_MACRO + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!setjmp(CONTEXT(_thread))) { \ + _MD_SAVE_ERRNO(_thread) \ + _MD_SET_LAST_THREAD(_thread); \ + _MD_SET_CURRENT_THREAD(_thread); \ + _PR_Schedule(); \ + } + +#define _MD_RESTORE_CONTEXT(_newThread) \ +{ \ + _MD_RESTORE_ERRNO(_newThread) \ + _MD_SET_CURRENT_THREAD(_newThread); \ + longjmp(CONTEXT(_newThread), 1); \ +} + +#else +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + ucontext_t *uc = CONTEXT(_thread); \ + *status = PR_TRUE; \ + getcontext(uc); \ + uc->uc_stack.ss_sp = (char *) ((unsigned long)(_sp - WINDOWSIZE - SA(MINFRAME)) & 0xfffffff8); \ + uc->uc_stack.ss_size = _thread->stack->stackSize; \ + uc->uc_stack.ss_flags = 0; /* ? */ \ + uc->uc_mcontext.gregs[REG_SP] = (unsigned int) uc->uc_stack.ss_sp; \ + uc->uc_mcontext.gregs[REG_PC] = (unsigned int) _main; \ + uc->uc_mcontext.gregs[REG_nPC] = (unsigned int) ((char*)_main)+4; \ + uc->uc_flags = UC_ALL; \ + _thread->no_sched = 0; \ + PR_END_MACRO + +/* +** Switch away from the current thread context by saving its state and +** calling the thread scheduler. Reload cpu when we come back from the +** context switch because it might have changed. +*/ +#define _MD_SWITCH_CONTEXT(_thread) \ + PR_BEGIN_MACRO \ + if (!getcontext(CONTEXT(_thread))) { \ + _MD_SAVE_ERRNO(_thread); \ + _MD_SET_LAST_THREAD(_thread); \ + _PR_Schedule(); \ + } \ + PR_END_MACRO + +/* +** Restore a thread context that was saved by _MD_SWITCH_CONTEXT or +** initialized by _MD_INIT_CONTEXT. +*/ +#define _MD_RESTORE_CONTEXT(_newThread) \ + PR_BEGIN_MACRO \ + ucontext_t *uc = CONTEXT(_newThread); \ + uc->uc_mcontext.gregs[11] = 1; \ + _MD_RESTORE_ERRNO(_newThread); \ + _MD_SET_CURRENT_THREAD(_newThread); \ + setcontext(uc); \ + PR_END_MACRO +#endif + +#else /* x86 solaris */ + +#ifdef USE_SETJMP + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + *status = PR_TRUE; \ + if (setjmp(CONTEXT(_thread))) _main(); \ + _MD_GET_SP(_thread) = (int) ((_sp) - 64); \ + PR_END_MACRO + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!setjmp(CONTEXT(_thread))) { \ + _MD_SAVE_ERRNO(_thread) \ + _PR_Schedule(); \ + } + +#define _MD_RESTORE_CONTEXT(_newThread) \ +{ \ + _MD_RESTORE_ERRNO(_newThread) \ + _MD_SET_CURRENT_THREAD(_newThread); \ + longjmp(CONTEXT(_newThread), 1); \ +} + +#else /* USE_SETJMP */ + +#define WINDOWSIZE 0 + +int getedi(void); +void setedi(int); + +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + ucontext_t *uc = CONTEXT(_thread); \ + *status = PR_TRUE; \ + getcontext(uc); \ + /* Force sp to be double aligned! */ \ + uc->uc_mcontext.gregs[USP] = (int) ((unsigned long)(_sp - WINDOWSIZE - SA(MINFRAME)) & 0xfffffff8); \ + uc->uc_mcontext.gregs[PC] = (int) _main; \ + (_thread)->no_sched = 0; \ + PR_END_MACRO + +/* getcontext() may return 1, contrary to what the man page says */ +#define _MD_SWITCH_CONTEXT(_thread) \ + PR_BEGIN_MACRO \ + ucontext_t *uc = CONTEXT(_thread); \ + PR_ASSERT(_thread->no_sched); \ + sigfillset(&((_thread)->md.blockMask)); \ + sigprocmask(SIG_BLOCK, &((_thread)->md.blockMask), \ + &((_thread)->md.oldMask)); \ + (_thread)->md.edi = getedi(); \ + if (! getcontext(uc)) { \ + sigprocmask(SIG_SETMASK, &((_thread)->md.oldMask), NULL); \ + uc->uc_mcontext.gregs[EDI] = (_thread)->md.edi; \ + _MD_SAVE_ERRNO(_thread) \ + _MD_SET_LAST_THREAD(_thread); \ + _PR_Schedule(); \ + } else { \ + sigprocmask(SIG_SETMASK, &((_thread)->md.oldMask), NULL); \ + setedi((_thread)->md.edi); \ + PR_ASSERT(_MD_LAST_THREAD() !=_MD_CURRENT_THREAD()); \ + _MD_LAST_THREAD()->no_sched = 0; \ + } \ + PR_END_MACRO + +/* +** Restore a thread context, saved by _PR_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_newthread) \ + PR_BEGIN_MACRO \ + ucontext_t *uc = CONTEXT(_newthread); \ + uc->uc_mcontext.gregs[EAX] = 1; \ + _MD_RESTORE_ERRNO(_newthread) \ + _MD_SET_CURRENT_THREAD(_newthread); \ + (_newthread)->no_sched = 1; \ + setcontext(uc); \ + PR_END_MACRO +#endif /* USE_SETJMP */ + +#endif /* sparc */ + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDThread { + _PR_CONTEXT_TYPE context; + int errcode; + int id; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +extern PRStatus _MD_WAIT(struct PRThread *, PRIntervalTime timeout); +extern PRStatus _MD_WAKEUP_WAITER(struct PRThread *); +extern void _MD_YIELD(void); +extern PRStatus _MD_InitializeThread(PRThread *thread); +extern void _MD_SET_PRIORITY(struct _MDThread *thread, + PRThreadPriority newPri); +extern PRStatus _MD_CREATE_THREAD(PRThread *thread, void (*start) (void *), + PRThreadPriority priority, PRThreadScope scope, PRThreadState state, + PRUint32 stackSize); + +/* The following defines the unwrapped versions of select() and poll(). */ +extern int _select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); +#define _MD_SELECT _select + +#include <stropts.h> +#include <poll.h> +#define _MD_POLL _poll +extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout); + +PR_BEGIN_EXTERN_C + +/* +** Missing function prototypes +*/ +extern int gethostname (char *name, int namelen); + +PR_END_EXTERN_C + +#endif /* _PR_PTHREADS */ + +extern void _MD_solaris_map_sendfile_error(int err); + +#endif /* nspr_solaris_defs_h___ */ + diff --git a/nsprpub/pr/include/md/_unix_errors.h b/nsprpub/pr/include/md/_unix_errors.h new file mode 100644 index 0000000000..0578fd0275 --- /dev/null +++ b/nsprpub/pr/include/md/_unix_errors.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prunixerrors_h___ +#define prunixerrors_h___ + +#include <unistd.h> +#include <stddef.h> + +PR_BEGIN_EXTERN_C + +extern void _MD_unix_map_default_error(int err); +#define _PR_MD_MAP_DEFAULT_ERROR _MD_unix_map_default_error + +extern void _MD_unix_map_opendir_error(int err); +#define _PR_MD_MAP_OPENDIR_ERROR _MD_unix_map_opendir_error + +extern void _MD_unix_map_closedir_error(int err); +#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_unix_map_closedir_error + +extern void _MD_unix_readdir_error(int err); +#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error + +extern void _MD_unix_map_unlink_error(int err); +#define _PR_MD_MAP_UNLINK_ERROR _MD_unix_map_unlink_error + +extern void _MD_unix_map_stat_error(int err); +#define _PR_MD_MAP_STAT_ERROR _MD_unix_map_stat_error + +extern void _MD_unix_map_fstat_error(int err); +#define _PR_MD_MAP_FSTAT_ERROR _MD_unix_map_fstat_error + +extern void _MD_unix_map_rename_error(int err); +#define _PR_MD_MAP_RENAME_ERROR _MD_unix_map_rename_error + +extern void _MD_unix_map_access_error(int err); +#define _PR_MD_MAP_ACCESS_ERROR _MD_unix_map_access_error + +extern void _MD_unix_map_mkdir_error(int err); +#define _PR_MD_MAP_MKDIR_ERROR _MD_unix_map_mkdir_error + +extern void _MD_unix_map_rmdir_error(int err); +#define _PR_MD_MAP_RMDIR_ERROR _MD_unix_map_rmdir_error + +extern void _MD_unix_map_read_error(int err); +#define _PR_MD_MAP_READ_ERROR _MD_unix_map_read_error + +extern void _MD_unix_map_write_error(int err); +#define _PR_MD_MAP_WRITE_ERROR _MD_unix_map_write_error + +extern void _MD_unix_map_lseek_error(int err); +#define _PR_MD_MAP_LSEEK_ERROR _MD_unix_map_lseek_error + +extern void _MD_unix_map_fsync_error(int err); +#define _PR_MD_MAP_FSYNC_ERROR _MD_unix_map_fsync_error + +extern void _MD_unix_map_close_error(int err); +#define _PR_MD_MAP_CLOSE_ERROR _MD_unix_map_close_error + +extern void _MD_unix_map_socket_error(int err); +#define _PR_MD_MAP_SOCKET_ERROR _MD_unix_map_socket_error + +extern void _MD_unix_map_socketavailable_error(int err); +#define _PR_MD_MAP_SOCKETAVAILABLE_ERROR _MD_unix_map_socketavailable_error + +extern void _MD_unix_map_recv_error(int err); +#define _PR_MD_MAP_RECV_ERROR _MD_unix_map_recv_error + +extern void _MD_unix_map_recvfrom_error(int err); +#define _PR_MD_MAP_RECVFROM_ERROR _MD_unix_map_recvfrom_error + +extern void _MD_unix_map_send_error(int err); +#define _PR_MD_MAP_SEND_ERROR _MD_unix_map_send_error + +extern void _MD_unix_map_sendto_error(int err); +#define _PR_MD_MAP_SENDTO_ERROR _MD_unix_map_sendto_error + +extern void _MD_unix_map_writev_error(int err); +#define _PR_MD_MAP_WRITEV_ERROR _MD_unix_map_writev_error + +extern void _MD_unix_map_accept_error(int err); +#define _PR_MD_MAP_ACCEPT_ERROR _MD_unix_map_accept_error + +extern void _MD_unix_map_connect_error(int err); +#define _PR_MD_MAP_CONNECT_ERROR _MD_unix_map_connect_error + +extern void _MD_unix_map_bind_error(int err); +#define _PR_MD_MAP_BIND_ERROR _MD_unix_map_bind_error + +extern void _MD_unix_map_listen_error(int err); +#define _PR_MD_MAP_LISTEN_ERROR _MD_unix_map_listen_error + +extern void _MD_unix_map_shutdown_error(int err); +#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_unix_map_shutdown_error + +extern void _MD_unix_map_socketpair_error(int err); +#define _PR_MD_MAP_SOCKETPAIR_ERROR _MD_unix_map_socketpair_error + +extern void _MD_unix_map_getsockname_error(int err); +#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_unix_map_getsockname_error + +extern void _MD_unix_map_getpeername_error(int err); +#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_unix_map_getpeername_error + +extern void _MD_unix_map_getsockopt_error(int err); +#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_unix_map_getsockopt_error + +extern void _MD_unix_map_setsockopt_error(int err); +#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_unix_map_setsockopt_error + +extern void _MD_unix_map_open_error(int err); +#define _PR_MD_MAP_OPEN_ERROR _MD_unix_map_open_error + +extern void _MD_unix_map_mmap_error(int err); +#define _PR_MD_MAP_MMAP_ERROR _MD_unix_map_mmap_error + +extern void _MD_unix_map_gethostname_error(int err); +#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_unix_map_gethostname_error + +extern void _MD_unix_map_select_error(int err); +#define _PR_MD_MAP_SELECT_ERROR _MD_unix_map_select_error + +extern void _MD_unix_map_poll_error(int err); +#define _PR_MD_MAP_POLL_ERROR _MD_unix_map_poll_error + +extern void _MD_unix_map_poll_revents_error(int err); +#define _PR_MD_MAP_POLL_REVENTS_ERROR _MD_unix_map_poll_revents_error + +extern void _MD_unix_map_flock_error(int err); +#define _PR_MD_MAP_FLOCK_ERROR _MD_unix_map_flock_error + +extern void _MD_unix_map_lockf_error(int err); +#define _PR_MD_MAP_LOCKF_ERROR _MD_unix_map_lockf_error + +PR_END_EXTERN_C + +#endif /* prunixerrors_h___ */ diff --git a/nsprpub/pr/include/md/_unixos.h b/nsprpub/pr/include/md/_unixos.h new file mode 100644 index 0000000000..7d444cda16 --- /dev/null +++ b/nsprpub/pr/include/md/_unixos.h @@ -0,0 +1,620 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prunixos_h___ +#define prunixos_h___ + +/* + * If FD_SETSIZE is not defined on the command line, set the default value + * before include select.h + */ +/* + * Linux: FD_SETSIZE is defined in /usr/include/sys/select.h and should + * not be redefined. + */ +#if !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__) \ + && !defined(DARWIN) +#ifndef FD_SETSIZE +#define FD_SETSIZE 4096 +#endif +#endif + +#include <unistd.h> +#include <stddef.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> + +#include "prio.h" +#include "prmem.h" +#include "prclist.h" + +/* + * For select(), fd_set, and struct timeval. + * + * In The Single UNIX(R) Specification, Version 2, + * the header file for select() is <sys/time.h>. + * In Version 3, the header file for select() is + * changed to <sys/select.h>. + * + * fd_set is defined in <sys/types.h>. Usually + * <sys/time.h> includes <sys/types.h>, but on some + * older systems <sys/time.h> does not include + * <sys/types.h>, so we include it explicitly. + */ +#include <sys/time.h> +#include <sys/types.h> +#if defined(AIX) +#include <sys/select.h> +#endif + +#define HAVE_NETINET_TCP_H + +#define _PR_HAVE_O_APPEND + +#define PR_DIRECTORY_SEPARATOR '/' +#define PR_DIRECTORY_SEPARATOR_STR "/" +#define PR_PATH_SEPARATOR ':' +#define PR_PATH_SEPARATOR_STR ":" +typedef int (*FARPROC)(); + +/* + * intervals at which GLOBAL threads wakeup to check for pending interrupt + */ +#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5 +extern PRIntervalTime intr_timeout_ticks; + +/* + * The bit flags for the in_flags and out_flags fields + * of _PR_UnixPollDesc + */ +#ifdef _PR_USE_POLL +#define _PR_UNIX_POLL_READ POLLIN +#define _PR_UNIX_POLL_WRITE POLLOUT +#define _PR_UNIX_POLL_EXCEPT POLLPRI +#define _PR_UNIX_POLL_ERR POLLERR +#define _PR_UNIX_POLL_NVAL POLLNVAL +#define _PR_UNIX_POLL_HUP POLLHUP +#else /* _PR_USE_POLL */ +#define _PR_UNIX_POLL_READ 0x1 +#define _PR_UNIX_POLL_WRITE 0x2 +#define _PR_UNIX_POLL_EXCEPT 0x4 +#define _PR_UNIX_POLL_ERR 0x8 +#define _PR_UNIX_POLL_NVAL 0x10 +#define _PR_UNIX_POLL_HUP 0x20 +#endif /* _PR_USE_POLL */ + +typedef struct _PRUnixPollDesc { + PRInt32 osfd; + PRInt16 in_flags; + PRInt16 out_flags; +} _PRUnixPollDesc; + +typedef struct PRPollQueue { + PRCList links; /* for linking PRPollQueue's together */ + _PRUnixPollDesc *pds; /* array of poll descriptors */ + PRUintn npds; /* length of the array */ + PRPackedBool on_ioq; /* is this on the async i/o work q? */ + PRIntervalTime timeout; /* timeout, in ticks */ + struct PRThread *thr; +} PRPollQueue; + +#define _PR_POLLQUEUE_PTR(_qp) \ + ((PRPollQueue*) ((char*) (_qp) - offsetof(PRPollQueue,links))) + + +extern PRInt32 _PR_WaitForMultipleFDs( + _PRUnixPollDesc *unixpds, + PRInt32 pdcnt, + PRIntervalTime timeout); +extern void _PR_Unblock_IO_Wait(struct PRThread *thr); + +#if defined(_PR_LOCAL_THREADS_ONLY) || defined(_PR_GLOBAL_THREADS_ONLY) +#define _MD_CHECK_FOR_EXIT() +#endif + +extern fd_set _pr_md_read_set, _pr_md_write_set, _pr_md_exception_set; +extern PRInt16 _pr_md_read_cnt[], _pr_md_write_cnt[], _pr_md_exception_cnt[]; +extern PRInt32 _pr_md_ioq_max_osfd; +extern PRUint32 _pr_md_ioq_timeout; + +struct _MDFileDesc { + int osfd; +#if defined(LINUX) && defined(_PR_PTHREADS) + int tcp_nodelay; /* used by pt_LinuxSendFile */ +#endif +}; + +struct _MDDir { + DIR *d; +}; + +struct _PRCPU; +extern void _MD_unix_init_running_cpu(struct _PRCPU *cpu); + +/* +** Make a redzone at both ends of the stack segment. Disallow access +** to those pages of memory. It's ok if the mprotect call's don't +** work - it just means that we don't really have a functional +** redzone. +*/ +#include <sys/mman.h> +#ifndef PROT_NONE +#define PROT_NONE 0x0 +#endif + +#if defined(DEBUG) && !defined(DARWIN) +#if !defined(SOLARIS) +#include <string.h> /* for memset() */ +#define _MD_INIT_STACK(ts,REDZONE) \ + PR_BEGIN_MACRO \ + (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_NONE); \ + (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\ + REDZONE, PROT_NONE); \ + /* \ + ** Fill stack memory with something that turns into an illegal \ + ** pointer value. This will sometimes find runtime references to \ + ** uninitialized pointers. We don't do this for solaris because we \ + ** can use purify instead. \ + */ \ + if (_pr_debugStacks) { \ + memset(ts->allocBase + REDZONE, 0xf7, ts->stackSize); \ + } \ + PR_END_MACRO +#else /* !SOLARIS */ +#define _MD_INIT_STACK(ts,REDZONE) \ + PR_BEGIN_MACRO \ + (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_NONE); \ + (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\ + REDZONE, PROT_NONE); \ + PR_END_MACRO +#endif /* !SOLARIS */ + +/* + * _MD_CLEAR_STACK + * Allow access to the redzone pages; the access was turned off in + * _MD_INIT_STACK. + */ +#define _MD_CLEAR_STACK(ts) \ + PR_BEGIN_MACRO \ + (void) mprotect((void*)ts->seg->vaddr, REDZONE, PROT_READ|PROT_WRITE);\ + (void) mprotect((void*) ((char*)ts->seg->vaddr + REDZONE + ts->stackSize),\ + REDZONE, PROT_READ|PROT_WRITE); \ + PR_END_MACRO + +#else /* DEBUG */ + +#define _MD_INIT_STACK(ts,REDZONE) +#define _MD_CLEAR_STACK(ts) + +#endif /* DEBUG */ + +#if !defined(SOLARIS) + +#define PR_SET_INTSOFF(newval) + +#endif + +/************************************************************************/ + +extern void _PR_UnixInit(void); + +extern void _PR_UnixCleanup(void); +#define _MD_EARLY_CLEANUP _PR_UnixCleanup + +/************************************************************************/ + +struct _MDProcess { + pid_t pid; +}; + +struct PRProcess; +struct PRProcessAttr; + +/* Create a new process (fork() + exec()) */ +#define _MD_CREATE_PROCESS _MD_CreateUnixProcess +extern struct PRProcess * _MD_CreateUnixProcess( + const char *path, + char *const *argv, + char *const *envp, + const struct PRProcessAttr *attr +); + +#define _MD_DETACH_PROCESS _MD_DetachUnixProcess +extern PRStatus _MD_DetachUnixProcess(struct PRProcess *process); + +/* Wait for a child process to terminate */ +#define _MD_WAIT_PROCESS _MD_WaitUnixProcess +extern PRStatus _MD_WaitUnixProcess(struct PRProcess *process, + PRInt32 *exitCode); + +#define _MD_KILL_PROCESS _MD_KillUnixProcess +extern PRStatus _MD_KillUnixProcess(struct PRProcess *process); + +/************************************************************************/ + +extern void _MD_EnableClockInterrupts(void); +extern void _MD_DisableClockInterrupts(void); + +#define _MD_START_INTERRUPTS _MD_StartInterrupts +#define _MD_STOP_INTERRUPTS _MD_StopInterrupts +#define _MD_DISABLE_CLOCK_INTERRUPTS _MD_DisableClockInterrupts +#define _MD_ENABLE_CLOCK_INTERRUPTS _MD_EnableClockInterrupts +#define _MD_BLOCK_CLOCK_INTERRUPTS _MD_BlockClockInterrupts +#define _MD_UNBLOCK_CLOCK_INTERRUPTS _MD_UnblockClockInterrupts + +/************************************************************************/ + +extern void _MD_InitCPUS(void); +#define _MD_INIT_CPUS _MD_InitCPUS + +extern void _MD_Wakeup_CPUs(void); +#define _MD_WAKEUP_CPUS _MD_Wakeup_CPUs + +#define _MD_PAUSE_CPU _MD_PauseCPU + +#if defined(_PR_LOCAL_THREADS_ONLY) || defined(_PR_GLOBAL_THREADS_ONLY) +#define _MD_CLEANUP_BEFORE_EXIT() +#endif + +#define _MD_EXIT(status) _exit(status) + +/************************************************************************/ + +#define _MD_GET_ENV getenv +#define _MD_PUT_ENV putenv + +/************************************************************************/ + +#define _MD_INIT_FILEDESC(fd) + +extern void _MD_MakeNonblock(PRFileDesc *fd); +#define _MD_MAKE_NONBLOCK _MD_MakeNonblock + +/************************************************************************/ + +#if !defined(_PR_PTHREADS) + +extern void _MD_InitSegs(void); +extern PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, + void *vaddr); +extern void _MD_FreeSegment(PRSegment *seg); + +#define _MD_INIT_SEGS _MD_InitSegs +#define _MD_ALLOC_SEGMENT _MD_AllocSegment +#define _MD_FREE_SEGMENT _MD_FreeSegment + +#endif /* !defined(_PR_PTHREADS) */ + +/************************************************************************/ + +#ifdef _MD_INTERVAL_USE_GTOD +extern PRIntervalTime _PR_UNIX_GetInterval(void); +extern PRIntervalTime _PR_UNIX_TicksPerSecond(void); +#define _MD_INTERVAL_INIT() +#define _MD_GET_INTERVAL _PR_UNIX_GetInterval +#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond +#endif + +#ifdef _PR_HAVE_CLOCK_MONOTONIC +extern PRIntervalTime _PR_UNIX_GetInterval2(void); +extern PRIntervalTime _PR_UNIX_TicksPerSecond2(void); +#define _MD_INTERVAL_INIT() +#define _MD_GET_INTERVAL _PR_UNIX_GetInterval2 +#define _MD_INTERVAL_PER_SEC _PR_UNIX_TicksPerSecond2 +#endif + +#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000) +#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000) + +/************************************************************************/ + +#define _MD_ERRNO() (errno) +#define _MD_GET_SOCKET_ERROR() (errno) + +/************************************************************************/ + +extern PRInt32 _MD_AvailableSocket(PRInt32 osfd); + +extern void _MD_StartInterrupts(void); +extern void _MD_StopInterrupts(void); +extern void _MD_DisableClockInterrupts(void); +extern void _MD_BlockClockInterrupts(void); +extern void _MD_UnblockClockInterrupts(void); +extern void _MD_PauseCPU(PRIntervalTime timeout); + +extern PRStatus _MD_open_dir(struct _MDDir *, const char *); +extern PRInt32 _MD_close_dir(struct _MDDir *); +extern char * _MD_read_dir(struct _MDDir *, PRIntn); +extern PRInt32 _MD_open(const char *name, PRIntn osflags, PRIntn mode); +extern PRInt32 _MD_delete(const char *name); +extern PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info); +extern PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info); +extern PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info); +extern PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info); +extern PRInt32 _MD_rename(const char *from, const char *to); +extern PRInt32 _MD_access(const char *name, PRAccessHow how); +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_UnixSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout); + +extern PRStatus _MD_LockFile(PRInt32 osfd); +extern PRStatus _MD_TLockFile(PRInt32 osfd); +extern PRStatus _MD_UnlockFile(PRInt32 osfd); + +#define _MD_OPEN_DIR(dir, name) _MD_open_dir(dir, name) +#define _MD_CLOSE_DIR(dir) _MD_close_dir(dir) +#define _MD_READ_DIR(dir, flags) _MD_read_dir(dir, flags) +#define _MD_OPEN(name, osflags, mode) _MD_open(name, osflags, mode) +#define _MD_OPEN_FILE(name, osflags, mode) _MD_open(name, osflags, mode) +extern PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount); +#define _MD_READ(fd,buf,amount) _MD_read(fd,buf,amount) +extern PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount); +#define _MD_WRITE(fd,buf,amount) _MD_write(fd,buf,amount) +#define _MD_DELETE(name) _MD_delete(name) +#define _MD_GETFILEINFO(fn, info) _MD_getfileinfo(fn, info) +#define _MD_GETFILEINFO64(fn, info) _MD_getfileinfo64(fn, info) +#define _MD_GETOPENFILEINFO(fd, info) _MD_getopenfileinfo(fd, info) +#define _MD_GETOPENFILEINFO64(fd, info) _MD_getopenfileinfo64(fd, info) +#define _MD_RENAME(from, to) _MD_rename(from, to) +#define _MD_ACCESS(name, how) _MD_access(name, how) +#define _MD_MKDIR(name, mode) _MD_mkdir(name, mode) +#define _MD_MAKE_DIR(name, mode) _MD_mkdir(name, mode) +#define _MD_RMDIR(name) _MD_rmdir(name) +#define _MD_ACCEPT_READ(sock, newSock, raddr, buf, amount) _MD_accept_read(sock, newSock, raddr, buf, amount) + +#define _MD_LOCKFILE _MD_LockFile +#define _MD_TLOCKFILE _MD_TLockFile +#define _MD_UNLOCKFILE _MD_UnlockFile + + +extern PRInt32 _MD_socket(int af, int type, int flags); +#define _MD_SOCKET _MD_socket +extern PRInt32 _MD_connect(PRFileDesc *fd, const PRNetAddr *addr, + PRUint32 addrlen, PRIntervalTime timeout); +#define _MD_CONNECT _MD_connect +extern PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, + PRIntervalTime timeout); +#define _MD_ACCEPT _MD_accept +extern PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen); +#define _MD_BIND _MD_bind +extern PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog); +#define _MD_LISTEN _MD_listen +extern PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how); +#define _MD_SHUTDOWN _MD_shutdown + +extern PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); +#define _MD_RECV _MD_recv +extern PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); +#define _MD_SEND _MD_send +extern PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, + PRIntervalTime timeout); +#define _MD_RECVFROM _MD_recvfrom +extern PRInt32 _MD_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRUint32 addrlen, + PRIntervalTime timeout); +#define _MD_SENDTO _MD_sendto +extern PRInt32 _MD_writev(PRFileDesc *fd, const struct PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout); +#define _MD_WRITEV _MD_writev + +extern PRInt32 _MD_socketavailable(PRFileDesc *fd); +#define _MD_SOCKETAVAILABLE _MD_socketavailable +extern PRInt64 _MD_socketavailable64(PRFileDesc *fd); +#define _MD_SOCKETAVAILABLE64 _MD_socketavailable64 + +#define _MD_PIPEAVAILABLE _MD_socketavailable + +extern PRInt32 _MD_pr_poll(PRPollDesc *pds, PRIntn npds, + PRIntervalTime timeout); +#define _MD_PR_POLL _MD_pr_poll + +extern PRInt32 _MD_close(PRInt32 osfd); +#define _MD_CLOSE_FILE _MD_close +extern PRInt32 _MD_lseek(PRFileDesc*, PRInt32, PRSeekWhence); +#define _MD_LSEEK _MD_lseek +extern PRInt64 _MD_lseek64(PRFileDesc*, PRInt64, PRSeekWhence); +#define _MD_LSEEK64 _MD_lseek64 +extern PRInt32 _MD_fsync(PRFileDesc *fd); +#define _MD_FSYNC _MD_fsync + +extern PRInt32 _MD_socketpair(int af, int type, int flags, PRInt32 *osfd); +#define _MD_SOCKETPAIR _MD_socketpair + +#define _MD_CLOSE_SOCKET _MD_close + +#ifndef NO_NSPR_10_SUPPORT +#define _MD_STAT stat +#endif + +extern PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen); +#define _MD_GETPEERNAME _MD_getpeername +extern PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen); +#define _MD_GETSOCKNAME _MD_getsockname + +extern PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, + PRInt32 optname, char* optval, PRInt32* optlen); +#define _MD_GETSOCKOPT _MD_getsockopt +extern PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, + PRInt32 optname, const char* optval, PRInt32 optlen); +#define _MD_SETSOCKOPT _MD_setsockopt + +extern PRStatus _MD_set_fd_inheritable(PRFileDesc *fd, PRBool inheritable); +#define _MD_SET_FD_INHERITABLE _MD_set_fd_inheritable + +extern void _MD_init_fd_inheritable(PRFileDesc *fd, PRBool imported); +#define _MD_INIT_FD_INHERITABLE _MD_init_fd_inheritable + +extern void _MD_query_fd_inheritable(PRFileDesc *fd); +#define _MD_QUERY_FD_INHERITABLE _MD_query_fd_inheritable + +extern PRStatus _MD_gethostname(char *name, PRUint32 namelen); +#define _MD_GETHOSTNAME _MD_gethostname + +extern PRStatus _MD_getsysinfo(PRSysInfo cmd, char *name, PRUint32 namelen); +#define _MD_GETSYSINFO _MD_getsysinfo + +extern int _MD_unix_get_nonblocking_connect_error(int osfd); + +/* Memory-mapped files */ + +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); +#define _MD_CREATE_FILE_MAP _MD_CreateFileMap + +#define _MD_GET_MEM_MAP_ALIGNMENT() PR_GetPageSize() + +extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset, + PRUint32 len); +#define _MD_MEM_MAP _MD_MemMap + +extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size); +#define _MD_MEM_UNMAP _MD_MemUnmap + +extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap); +#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap + +extern PRStatus _MD_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len); +#define _MD_SYNC_MEM_MAP _MD_SyncMemMap + +/* + * The standard (XPG4) gettimeofday() (from BSD) takes two arguments. + * On some SVR4 derivatives, gettimeofday() takes only one argument. + * The GETTIMEOFDAY macro is intended to hide this difference. + */ +#ifdef HAVE_SVID_GETTOD +#define GETTIMEOFDAY(tp) gettimeofday(tp) +#else +#define GETTIMEOFDAY(tp) gettimeofday((tp), NULL) +#endif + +#if defined(_PR_PTHREADS) && !defined(_PR_POLL_AVAILABLE) +#define _PR_NEED_FAKE_POLL +#endif + +#if defined(_PR_NEED_FAKE_POLL) + +/* + * Some platforms don't have poll(), but our pthreads code calls poll(). + * As a temporary measure, I implemented a fake poll() using select(). + * Here are the struct and macro definitions copied from sys/poll.h + * on Solaris 2.5. + */ + +struct pollfd { + int fd; + short events; + short revents; +}; + +/* poll events */ + +#define POLLIN 0x0001 /* fd is readable */ +#define POLLPRI 0x0002 /* high priority info at fd */ +#define POLLOUT 0x0004 /* fd is writeable (won't block) */ +#define POLLRDNORM 0x0040 /* normal data is readable */ +#define POLLWRNORM POLLOUT +#define POLLRDBAND 0x0080 /* out-of-band data is readable */ +#define POLLWRBAND 0x0100 /* out-of-band data is writeable */ + +#define POLLNORM POLLRDNORM + +#define POLLERR 0x0008 /* fd has error condition */ +#define POLLHUP 0x0010 /* fd has been hung up on */ +#define POLLNVAL 0x0020 /* invalid pollfd entry */ + +extern int poll(struct pollfd *, unsigned long, int); + +#endif /* _PR_NEED_FAKE_POLL */ + +/* +** A vector of the UNIX I/O calls we use. These are here to smooth over +** the rough edges needed for large files. All of NSPR's implmentaions +** go through this vector using syntax of the form +** result = _md_iovector.xxx64(args); +*/ + +#if defined(SOLARIS2_5) +/* +** Special case: Solaris 2.5.1 +** Solaris starts to have 64-bit file I/O in 2.6. We build on Solaris +** 2.5.1 so that we can use the same binaries on both Solaris 2.5.1 and +** 2.6. At run time, we detect whether 64-bit file I/O is available by +** looking up the 64-bit file function symbols in libc. At build time, +** we need to define the 64-bit file I/O datatypes that are compatible +** with their definitions on Solaris 2.6. +*/ +typedef PRInt64 off64_t; +typedef PRUint64 ino64_t; +typedef PRInt64 blkcnt64_t; +struct stat64 { + dev_t st_dev; + long st_pad1[3]; + ino64_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + long t_pad2[2]; + off64_t st_size; + timestruc_t st_atim; + timestruc_t st_mtim; + timestruc_t st_ctim; + long st_blksize; + blkcnt64_t st_blocks; + char st_fstype[_ST_FSTYPSZ]; + long st_pad4[8]; +}; +typedef struct stat64 _MDStat64; +typedef off64_t _MDOff64_t; + +#elif defined(_PR_HAVE_OFF64_T) +typedef struct stat64 _MDStat64; +typedef off64_t _MDOff64_t; +#elif defined(_PR_HAVE_LARGE_OFF_T) +typedef struct stat _MDStat64; +typedef off_t _MDOff64_t; +#elif defined(_PR_NO_LARGE_FILES) +typedef struct stat _MDStat64; +typedef PRInt64 _MDOff64_t; +#else +#error "I don't know yet" +#endif + +typedef PRIntn (*_MD_Fstat64)(PRIntn osfd, _MDStat64 *buf); +typedef PRIntn (*_MD_Open64)(const char *path, int oflag, ...); +typedef PRIntn (*_MD_Stat64)(const char *path, _MDStat64 *buf); +typedef _MDOff64_t (*_MD_Lseek64)(PRIntn osfd, _MDOff64_t, PRIntn whence); +typedef void* (*_MD_Mmap64)( + void *addr, PRSize len, PRIntn prot, PRIntn flags, + PRIntn fildes, _MDOff64_t offset); +struct _MD_IOVector +{ + _MD_Open64 _open64; + _MD_Mmap64 _mmap64; + _MD_Stat64 _stat64; + _MD_Fstat64 _fstat64; + _MD_Lseek64 _lseek64; +}; +extern struct _MD_IOVector _md_iovector; + +#endif /* prunixos_h___ */ diff --git a/nsprpub/pr/include/md/_unixware.cfg b/nsprpub/pr/include/md/_unixware.cfg new file mode 100644 index 0000000000..0fa8dfc9b5 --- /dev/null +++ b/nsprpub/pr/include/md/_unixware.cfg @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef UNIXWARE +#define UNIXWARE +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#undef HAVE_LONG_LONG +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define _PR_POLL_BACKCOMPAT + +#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/nsprpub/pr/include/md/_unixware.h b/nsprpub/pr/include/md/_unixware.h new file mode 100644 index 0000000000..06667b2988 --- /dev/null +++ b/nsprpub/pr/include/md/_unixware.h @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_unixware_defs_h___ +#define nspr_unixware_defs_h___ + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "unixware" +#define _PR_SI_SYSNAME "UnixWare" +#define _PR_SI_ARCHITECTURE "x86" +#define PR_DLL_SUFFIX ".so" + +#define _PR_VMBASE 0x30000000 +#define _PR_STACK_VMBASE 0x50000000 +#define _MD_DEFAULT_STACK_SIZE 65536L +#define _MD_MMAP_FLAGS MAP_PRIVATE + +#ifndef HAVE_WEAK_IO_SYMBOLS +#define HAVE_WEAK_IO_SYMBOLS +#endif +#define _PR_POLL_AVAILABLE +#define _PR_USE_POLL +#define _PR_STAT_HAS_ST_ATIM_UNION + +#undef HAVE_STACK_GROWING_UP +#define HAVE_NETCONFIG +#define HAVE_DLL +#define USE_DLFCN +#define HAVE_STRERROR +#define NEED_STRFTIME_LOCK +#define NEED_TIME_R +#define _PR_NEED_STRCASECMP + +#define USE_SETJMP + +#include <setjmp.h> + +#define _SETJMP setjmp +#define _LONGJMP longjmp +#define _PR_CONTEXT_TYPE jmp_buf +#define _MD_GET_SP(_t) (_t)->md.context[4] +#define _PR_NUM_GCREGS _JBLEN + +#define CONTEXT(_th) ((_th)->md.context) + +/* +** Initialize the thread context preparing it to execute _main. +*/ +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ +{ \ + *status = PR_TRUE; \ + if(_SETJMP(CONTEXT(_thread))) (*_main)(); \ + _MD_GET_SP(_thread) = (int) ((_sp) - 128); \ +} + +#define _MD_SWITCH_CONTEXT(_thread) \ + if (!_SETJMP(CONTEXT(_thread))) { \ + (_thread)->md.errcode = errno; \ + _PR_Schedule(); \ + } + +/* +** Restore a thread context, saved by _MD_SWITCH_CONTEXT +*/ +#define _MD_RESTORE_CONTEXT(_thread) \ +{ \ + errno = (_thread)->md.errcode; \ + _MD_SET_CURRENT_THREAD(_thread); \ + _LONGJMP(CONTEXT(_thread), 1); \ +} + +/* Machine-dependent (MD) data structures. + * Don't use SVR4 native threads (yet). + */ + +struct _MDThread { + _PR_CONTEXT_TYPE context; + int id; + int errcode; +}; + +struct _MDThreadStack { + PRInt8 notused; +}; + +struct _MDLock { + PRInt8 notused; +}; + +struct _MDSemaphore { + PRInt8 notused; +}; + +struct _MDCVar { + PRInt8 notused; +}; + +struct _MDSegment { + PRInt8 notused; +}; + +/* + * md-specific cpu structure field + */ +#define _PR_MD_MAX_OSFD FD_SETSIZE + +struct _MDCPU_Unix { + PRCList ioQ; + PRUint32 ioq_timeout; + PRInt32 ioq_max_osfd; + PRInt32 ioq_osfd_cnt; +#ifndef _PR_USE_POLL + fd_set fd_read_set, fd_write_set, fd_exception_set; + PRInt16 fd_read_cnt[_PR_MD_MAX_OSFD],fd_write_cnt[_PR_MD_MAX_OSFD], + fd_exception_cnt[_PR_MD_MAX_OSFD]; +#else + struct pollfd *ioq_pollfds; + int ioq_pollfds_size; +#endif /* _PR_USE_POLL */ +}; + +#define _PR_IOQ(_cpu) ((_cpu)->md.md_unix.ioQ) +#define _PR_ADD_TO_IOQ(_pq, _cpu) PR_APPEND_LINK(&_pq.links, &_PR_IOQ(_cpu)) +#define _PR_FD_READ_SET(_cpu) ((_cpu)->md.md_unix.fd_read_set) +#define _PR_FD_READ_CNT(_cpu) ((_cpu)->md.md_unix.fd_read_cnt) +#define _PR_FD_WRITE_SET(_cpu) ((_cpu)->md.md_unix.fd_write_set) +#define _PR_FD_WRITE_CNT(_cpu) ((_cpu)->md.md_unix.fd_write_cnt) +#define _PR_FD_EXCEPTION_SET(_cpu) ((_cpu)->md.md_unix.fd_exception_set) +#define _PR_FD_EXCEPTION_CNT(_cpu) ((_cpu)->md.md_unix.fd_exception_cnt) +#define _PR_IOQ_TIMEOUT(_cpu) ((_cpu)->md.md_unix.ioq_timeout) +#define _PR_IOQ_MAX_OSFD(_cpu) ((_cpu)->md.md_unix.ioq_max_osfd) +#define _PR_IOQ_OSFD_CNT(_cpu) ((_cpu)->md.md_unix.ioq_osfd_cnt) +#define _PR_IOQ_POLLFDS(_cpu) ((_cpu)->md.md_unix.ioq_pollfds) +#define _PR_IOQ_POLLFDS_SIZE(_cpu) ((_cpu)->md.md_unix.ioq_pollfds_size) + +#define _PR_IOQ_MIN_POLLFDS_SIZE(_cpu) 32 + +struct _MDCPU { + struct _MDCPU_Unix md_unix; +}; + +#define _MD_INIT_LOCKS() +#define _MD_NEW_LOCK(lock) PR_SUCCESS +#define _MD_FREE_LOCK(lock) +#define _MD_LOCK(lock) +#define _MD_UNLOCK(lock) +#define _MD_INIT_IO() +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + +/* + * The following are copied from _sunos.h, _aix.h. This means + * some of them should probably be moved into _unixos.h. But + * _irix.h seems to be quite different in regard to these macros. + */ +#define _MD_INTERVAL_USE_GTOD + +#define _MD_EARLY_INIT _MD_EarlyInit +#define _MD_FINAL_INIT _PR_UnixInit +#define _MD_INIT_RUNNING_CPU(cpu) _MD_unix_init_running_cpu(cpu) +#define _MD_INIT_THREAD _MD_InitializeThread +#define _MD_EXIT_THREAD(thread) +#define _MD_SUSPEND_THREAD(thread) +#define _MD_RESUME_THREAD(thread) +#define _MD_CLEAN_THREAD(_thread) + +/* + * We wrapped the select() call. _MD_SELECT refers to the built-in, + * unwrapped version. + */ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +extern int _select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *execptfds, struct timeval *timeout); +#define _MD_SELECT _select + +#define _MD_POLL _poll +extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout); + +#endif /* nspr_unixware_defs_h___ */ diff --git a/nsprpub/pr/include/md/_unixware7.cfg b/nsprpub/pr/include/md/_unixware7.cfg new file mode 100644 index 0000000000..33ce83bda1 --- /dev/null +++ b/nsprpub/pr/include/md/_unixware7.cfg @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef UNIXWARE +#define UNIXWARE +#endif + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#define PR_AF_INET6 27 /* same as AF_INET6 */ + +#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 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define _PR_POLL_BACKCOMPAT + +#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/nsprpub/pr/include/md/_win32_errors.h b/nsprpub/pr/include/md/_win32_errors.h new file mode 100644 index 0000000000..d267d8a28c --- /dev/null +++ b/nsprpub/pr/include/md/_win32_errors.h @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_win32_errors_h___ +#define nspr_win32_errors_h___ + +#include <windows.h> +#include <winsock.h> +#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 + +extern void _MD_win32_map_closedir_error(PRInt32 err); +#define _PR_MD_MAP_CLOSEDIR_ERROR _MD_win32_map_closedir_error + +extern void _MD_unix_readdir_error(PRInt32 err); +#define _PR_MD_MAP_READDIR_ERROR _MD_unix_readdir_error + +extern void _MD_win32_map_delete_error(PRInt32 err); +#define _PR_MD_MAP_DELETE_ERROR _MD_win32_map_delete_error + +extern void _MD_win32_map_stat_error(PRInt32 err); +#define _PR_MD_MAP_STAT_ERROR _MD_win32_map_stat_error + +extern void _MD_win32_map_fstat_error(PRInt32 err); +#define _PR_MD_MAP_FSTAT_ERROR _MD_win32_map_fstat_error + +extern void _MD_win32_map_rename_error(PRInt32 err); +#define _PR_MD_MAP_RENAME_ERROR _MD_win32_map_rename_error + +extern void _MD_win32_map_access_error(PRInt32 err); +#define _PR_MD_MAP_ACCESS_ERROR _MD_win32_map_access_error + +extern void _MD_win32_map_mkdir_error(PRInt32 err); +#define _PR_MD_MAP_MKDIR_ERROR _MD_win32_map_mkdir_error + +extern void _MD_win32_map_rmdir_error(PRInt32 err); +#define _PR_MD_MAP_RMDIR_ERROR _MD_win32_map_rmdir_error + +extern void _MD_win32_map_read_error(PRInt32 err); +#define _PR_MD_MAP_READ_ERROR _MD_win32_map_read_error + +extern void _MD_win32_map_transmitfile_error(PRInt32 err); +#define _PR_MD_MAP_TRANSMITFILE_ERROR _MD_win32_map_transmitfile_error + +extern void _MD_win32_map_write_error(PRInt32 err); +#define _PR_MD_MAP_WRITE_ERROR _MD_win32_map_write_error + +extern void _MD_win32_map_lseek_error(PRInt32 err); +#define _PR_MD_MAP_LSEEK_ERROR _MD_win32_map_lseek_error + +extern void _MD_win32_map_fsync_error(PRInt32 err); +#define _PR_MD_MAP_FSYNC_ERROR _MD_win32_map_fsync_error + +extern void _MD_win32_map_close_error(PRInt32 err); +#define _PR_MD_MAP_CLOSE_ERROR _MD_win32_map_close_error + +extern void _MD_win32_map_socket_error(PRInt32 err); +#define _PR_MD_MAP_SOCKET_ERROR _MD_win32_map_socket_error + +extern void _MD_win32_map_recv_error(PRInt32 err); +#define _PR_MD_MAP_RECV_ERROR _MD_win32_map_recv_error + +extern void _MD_win32_map_recvfrom_error(PRInt32 err); +#define _PR_MD_MAP_RECVFROM_ERROR _MD_win32_map_recvfrom_error + +extern void _MD_win32_map_send_error(PRInt32 err); +#define _PR_MD_MAP_SEND_ERROR _MD_win32_map_send_error + +extern void _MD_win32_map_sendto_error(PRInt32 err); +#define _PR_MD_MAP_SENDTO_ERROR _MD_win32_map_sendto_error + +extern void _MD_win32_map_accept_error(PRInt32 err); +#define _PR_MD_MAP_ACCEPT_ERROR _MD_win32_map_accept_error + +extern void _MD_win32_map_acceptex_error(PRInt32 err); +#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_win32_map_acceptex_error + +extern void _MD_win32_map_connect_error(PRInt32 err); +#define _PR_MD_MAP_CONNECT_ERROR _MD_win32_map_connect_error + +extern void _MD_win32_map_bind_error(PRInt32 err); +#define _PR_MD_MAP_BIND_ERROR _MD_win32_map_bind_error + +extern void _MD_win32_map_listen_error(PRInt32 err); +#define _PR_MD_MAP_LISTEN_ERROR _MD_win32_map_listen_error + +extern void _MD_win32_map_shutdown_error(PRInt32 err); +#define _PR_MD_MAP_SHUTDOWN_ERROR _MD_win32_map_shutdown_error + +extern void _MD_win32_map_getsockname_error(PRInt32 err); +#define _PR_MD_MAP_GETSOCKNAME_ERROR _MD_win32_map_getsockname_error + +extern void _MD_win32_map_getpeername_error(PRInt32 err); +#define _PR_MD_MAP_GETPEERNAME_ERROR _MD_win32_map_getpeername_error + +extern void _MD_win32_map_getsockopt_error(PRInt32 err); +#define _PR_MD_MAP_GETSOCKOPT_ERROR _MD_win32_map_getsockopt_error + +extern void _MD_win32_map_setsockopt_error(PRInt32 err); +#define _PR_MD_MAP_SETSOCKOPT_ERROR _MD_win32_map_setsockopt_error + +extern void _MD_win32_map_open_error(PRInt32 err); +#define _PR_MD_MAP_OPEN_ERROR _MD_win32_map_open_error + +extern void _MD_win32_map_gethostname_error(PRInt32 err); +#define _PR_MD_MAP_GETHOSTNAME_ERROR _MD_win32_map_gethostname_error + +extern void _MD_win32_map_select_error(PRInt32 err); +#define _PR_MD_MAP_SELECT_ERROR _MD_win32_map_select_error + +extern void _MD_win32_map_lockf_error(int err); +#define _PR_MD_MAP_LOCKF_ERROR _MD_win32_map_lockf_error + +#endif /* nspr_win32_errors_h___ */ diff --git a/nsprpub/pr/include/md/_win95.cfg b/nsprpub/pr/include/md/_win95.cfg new file mode 100644 index 0000000000..7c379fce70 --- /dev/null +++ b/nsprpub/pr/include/md/_win95.cfg @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_PC +#define XP_PC +#endif + +#ifndef WIN32 +#define WIN32 +#endif + +#ifndef WIN95 +#define WIN95 +#endif + +#define PR_AF_INET6 23 /* same as AF_INET6 */ + +#if defined(_M_IX86) || defined(_X86_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_WORD 4 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 32 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 5 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 4 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(_M_IA64) || defined(_IA64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(_M_ARM) || defined(_ARM_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_WORD 4 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 32 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 5 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 4 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(_M_ARM64) || defined(_ARM64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else /* defined(_M_IX86) || defined(_X86_) */ + +#error unknown processor architecture + +#endif /* defined(_M_IX86) || defined(_X86_) */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#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/nsprpub/pr/include/md/_win95.h b/nsprpub/pr/include/md/_win95.h new file mode 100644 index 0000000000..281cd6f09d --- /dev/null +++ b/nsprpub/pr/include/md/_win95.h @@ -0,0 +1,549 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_win95_defs_h___ +#define nspr_win95_defs_h___ + +#include "prio.h" + +#include <windows.h> +#include <winsock.h> +#include <errno.h> + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "win32" +#define _PR_SI_SYSNAME "WIN95" +#if defined(_M_IX86) || defined(_X86_) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) +#define _PR_SI_ARCHITECTURE "x86-64" +#elif defined(_M_IA64) || defined(_IA64_) +#define _PR_SI_ARCHITECTURE "ia64" +#elif defined(_M_ARM) || defined(_ARM_) +#define _PR_SI_ARCHITECTURE "arm" +#elif defined(_M_ARM64) +#define _PR_SI_ARCHITECTURE "aarch64" +#else +#error unknown processor architecture +#endif + +#define HAVE_DLL +#undef HAVE_THREAD_AFFINITY +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#ifndef _PR_INET6 +#define AF_INET6 23 +/* newer ws2tcpip.h provides these */ +#ifndef AI_CANONNAME +#define AI_CANONNAME 0x2 +#define AI_NUMERICHOST 0x4 +#define NI_NUMERICHOST 0x02 +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +#endif +#define _PR_HAVE_MD_SOCKADDR_IN6 +/* isomorphic to struct in6_addr on Windows */ +struct _md_in6_addr { + union { + PRUint8 _S6_u8[16]; + PRUint16 _S6_u16[8]; + } _S6_un; +}; +/* isomorphic to struct sockaddr_in6 on Windows */ +struct _md_sockaddr_in6 { + PRInt16 sin6_family; + PRUint16 sin6_port; + PRUint32 sin6_flowinfo; + struct _md_in6_addr sin6_addr; + PRUint32 sin6_scope_id; +}; +#endif +#define _PR_HAVE_THREADSAFE_GETHOST +#define _PR_HAVE_ATOMIC_OPS +#define PR_HAVE_WIN32_NAMED_SHARED_MEMORY + +/* --- Common User-Thread/Native-Thread Definitions --------------------- */ + +/* --- Globals --- */ +extern struct PRLock *_pr_schedLock; + +/* --- Typedefs --- */ +typedef void (*FiberFunc)(void *); + +#define PR_NUM_GCREGS 8 +typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS]; +#define GC_VMBASE 0x40000000 +#define GC_VMLIMIT 0x00FFFFFF + +#define _MD_MAGIC_THREAD 0x22222222 +#define _MD_MAGIC_THREADSTACK 0x33333333 +#define _MD_MAGIC_SEGMENT 0x44444444 +#define _MD_MAGIC_DIR 0x55555555 +#define _MD_MAGIC_CV 0x66666666 + +struct _MDCPU { + int unused; +}; + +struct _MDThread { + HANDLE blocked_sema; /* Threads block on this when waiting + * for IO or CondVar. + */ + PRBool inCVWaitQueue; /* PR_TRUE if the thread is in the + * wait queue of some cond var. + * PR_FALSE otherwise. */ + HANDLE handle; /* Win32 thread handle */ + PRUint32 id; + void *sp; /* only valid when suspended */ + PRUint32 magic; /* for debugging */ + PR_CONTEXT_TYPE gcContext; /* Thread context for GC */ + struct PRThread *prev, *next; /* used by the cvar wait queue to + * chain the PRThread structures + * together */ + void (*start)(void *); /* used by _PR_MD_CREATE_THREAD to + * pass its 'start' argument to + * pr_root. */ +}; + +struct _MDThreadStack { + PRUint32 magic; /* for debugging */ +}; + +struct _MDSegment { + PRUint32 magic; /* for debugging */ +}; + +#undef PROFILE_LOCKS + +struct _MDDir { + HANDLE d_hdl; + WIN32_FIND_DATAA d_entry; + PRBool firstEntry; /* Is this the entry returned + * by FindFirstFile()? */ + PRUint32 magic; /* for debugging */ +}; + +#ifdef MOZ_UNICODE +struct _MDDirUTF16 { + HANDLE d_hdl; + WIN32_FIND_DATAW d_entry; + PRBool firstEntry; /* Is this the entry returned + * by FindFirstFileW()? */ + PRUint32 magic; /* for debugging */ +}; +#endif /* MOZ_UNICODE */ + +struct _MDCVar { + PRUint32 magic; + struct PRThread *waitHead, *waitTail; /* the wait queue: a doubly- + * linked list of threads + * waiting on this condition + * variable */ + PRIntn nwait; /* number of threads in the + * wait queue */ +}; + +#define _MD_CV_NOTIFIED_LENGTH 6 +typedef struct _MDNotified _MDNotified; +struct _MDNotified { + PRIntn length; /* # of used entries in this + * structure */ + struct { + struct _MDCVar *cv; /* the condition variable notified */ + PRIntn times; /* and the number of times notified */ + struct PRThread *notifyHead; /* list of threads to wake up */ + } cv[_MD_CV_NOTIFIED_LENGTH]; + _MDNotified *link; /* link to another of these, or NULL */ +}; + +struct _MDLock { + CRITICAL_SECTION mutex; /* this is recursive on NT */ + + /* + * When notifying cvars, there is no point in actually + * waking up the threads waiting on the cvars until we've + * released the lock. So, we temporarily record the cvars. + * When doing an unlock, we'll then wake up the waiting threads. + */ + struct _MDNotified notified; /* array of conditions notified */ +#ifdef PROFILE_LOCKS + PRInt32 hitcount; + PRInt32 misscount; +#endif +}; + +struct _MDSemaphore { + HANDLE sem; +}; + +struct _MDFileDesc { + PROsfd osfd; /* The osfd can come from one of three spaces: + * - For stdin, stdout, and stderr, we are using + * the libc file handle (0, 1, 2), which is an int. + * - For files and pipes, we are using Win32 HANDLE, + * which is a void*. + * - For sockets, we are using Winsock SOCKET, which + * is a u_int. + */ +}; + +struct _MDProcess { + HANDLE handle; + DWORD id; +}; + +/* --- Misc stuff --- */ +#define _MD_GET_SP(thread) (thread)->md.gcContext[6] + +/* --- NT security stuff --- */ + +extern void _PR_NT_InitSids(void); +extern void _PR_NT_FreeSids(void); +extern PRStatus _PR_NT_MakeSecurityDescriptorACL( + PRIntn mode, + DWORD accessTable[], + PSECURITY_DESCRIPTOR *resultSD, + PACL *resultACL +); +extern void _PR_NT_FreeSecurityDescriptorACL( + PSECURITY_DESCRIPTOR pSD, PACL pACL); + +/* --- IO stuff --- */ + +#define _MD_OPEN _PR_MD_OPEN +#define _MD_OPEN_FILE _PR_MD_OPEN_FILE +#define _MD_READ _PR_MD_READ +#define _MD_WRITE _PR_MD_WRITE +#define _MD_WRITEV _PR_MD_WRITEV +#define _MD_LSEEK _PR_MD_LSEEK +#define _MD_LSEEK64 _PR_MD_LSEEK64 +extern PRInt32 _MD_CloseFile(PROsfd osfd); +#define _MD_CLOSE_FILE _MD_CloseFile +#define _MD_GETFILEINFO _PR_MD_GETFILEINFO +#define _MD_GETFILEINFO64 _PR_MD_GETFILEINFO64 +#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO +#define _MD_GETOPENFILEINFO64 _PR_MD_GETOPENFILEINFO64 +#define _MD_STAT _PR_MD_STAT +#define _MD_RENAME _PR_MD_RENAME +#define _MD_ACCESS _PR_MD_ACCESS +#define _MD_DELETE _PR_MD_DELETE +#define _MD_MKDIR _PR_MD_MKDIR +#define _MD_MAKE_DIR _PR_MD_MAKE_DIR +#define _MD_RMDIR _PR_MD_RMDIR +#define _MD_LOCKFILE _PR_MD_LOCKFILE +#define _MD_TLOCKFILE _PR_MD_TLOCKFILE +#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE + +/* --- UTF16 IO stuff --- */ +#ifdef MOZ_UNICODE +#define _MD_OPEN_FILE_UTF16 _PR_MD_OPEN_FILE_UTF16 +#define _MD_OPEN_DIR_UTF16 _PR_MD_OPEN_DIR_UTF16 +#define _MD_READ_DIR_UTF16 _PR_MD_READ_DIR_UTF16 +#define _MD_CLOSE_DIR_UTF16 _PR_MD_CLOSE_DIR_UTF16 +#define _MD_GETFILEINFO64_UTF16 _PR_MD_GETFILEINFO64_UTF16 +#endif /* MOZ_UNICODE */ + +/* --- Socket IO stuff --- */ +extern void _PR_MD_InitSockets(void); +extern void _PR_MD_CleanupSockets(void); +#define _MD_EACCES WSAEACCES +#define _MD_EADDRINUSE WSAEADDRINUSE +#define _MD_EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define _MD_EAFNOSUPPORT WSAEAFNOSUPPORT +#define _MD_EAGAIN WSAEWOULDBLOCK +#define _MD_EALREADY WSAEALREADY +#define _MD_EBADF WSAEBADF +#define _MD_ECONNREFUSED WSAECONNREFUSED +#define _MD_ECONNRESET WSAECONNRESET +#define _MD_EFAULT WSAEFAULT +#define _MD_EINPROGRESS WSAEINPROGRESS +#define _MD_EINTR WSAEINTR +#define _MD_EINVAL EINVAL +#define _MD_EISCONN WSAEISCONN +#define _MD_ENETUNREACH WSAENETUNREACH +#define _MD_ENOENT ENOENT +#define _MD_ENOTCONN WSAENOTCONN +#define _MD_ENOTSOCK WSAENOTSOCK +#define _MD_EOPNOTSUPP WSAEOPNOTSUPP +#define _MD_EWOULDBLOCK WSAEWOULDBLOCK +#define _MD_GET_SOCKET_ERROR() WSAGetLastError() +#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err) + +#define _MD_INIT_FILEDESC(fd) +extern void _MD_MakeNonblock(PRFileDesc *f); +#define _MD_MAKE_NONBLOCK _MD_MakeNonblock +#define _MD_INIT_FD_INHERITABLE _PR_MD_INIT_FD_INHERITABLE +#define _MD_QUERY_FD_INHERITABLE _PR_MD_QUERY_FD_INHERITABLE +#define _MD_SHUTDOWN _PR_MD_SHUTDOWN +#define _MD_LISTEN _PR_MD_LISTEN +extern PRInt32 _MD_CloseSocket(PROsfd osfd); +#define _MD_CLOSE_SOCKET _MD_CloseSocket +#define _MD_SENDTO _PR_MD_SENDTO +#ifdef _WIN64 +#define _MD_TCPSENDTO _PR_MD_TCPSENDTO +#endif +#define _MD_RECVFROM _PR_MD_RECVFROM +#define _MD_SOCKETPAIR(s, type, proto, sv) -1 +#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME +#define _MD_GETPEERNAME _PR_MD_GETPEERNAME +#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT +#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT +#define _MD_SET_FD_INHERITABLE _PR_MD_SET_FD_INHERITABLE +#define _MD_SELECT select +#define _MD_FSYNC _PR_MD_FSYNC +#define READ_FD 1 +#define WRITE_FD 2 + +#define _MD_INIT_ATOMIC() +#if defined(_M_IX86) || defined(_X86_) +#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT +#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD +#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT +#else /* non-x86 processors */ +#define _MD_ATOMIC_INCREMENT(x) InterlockedIncrement((PLONG)x) +#define _MD_ATOMIC_ADD(ptr,val) (InterlockedExchangeAdd((PLONG)ptr, (LONG)val) + val) +#define _MD_ATOMIC_DECREMENT(x) InterlockedDecrement((PLONG)x) +#endif /* x86 */ +#define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y) + +#define _MD_INIT_IO _PR_MD_INIT_IO + + +/* win95 doesn't have async IO */ +#define _MD_SOCKET _PR_MD_SOCKET +extern PRInt32 _MD_SocketAvailable(PRFileDesc *fd); +#define _MD_SOCKETAVAILABLE _MD_SocketAvailable +#define _MD_PIPEAVAILABLE _PR_MD_PIPEAVAILABLE +#define _MD_CONNECT _PR_MD_CONNECT +extern PROsfd _MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen, + PRIntervalTime timeout); +#define _MD_ACCEPT _MD_Accept +#define _MD_BIND _PR_MD_BIND +#define _MD_RECV _PR_MD_RECV +#define _MD_SEND _PR_MD_SEND +#define _MD_PR_POLL _PR_MD_PR_POLL + +/* --- Scheduler stuff --- */ +// #define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU +#define _MD_PAUSE_CPU + +/* --- DIR stuff --- */ +#define PR_DIRECTORY_SEPARATOR '\\' +#define PR_DIRECTORY_SEPARATOR_STR "\\" +#define PR_PATH_SEPARATOR ';' +#define PR_PATH_SEPARATOR_STR ";" +#define _MD_ERRNO() GetLastError() +#define _MD_OPEN_DIR _PR_MD_OPEN_DIR +#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR +#define _MD_READ_DIR _PR_MD_READ_DIR + +/* --- Segment stuff --- */ +#define _MD_INIT_SEGS() +#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0 +#define _MD_FREE_SEGMENT(seg) + +/* --- Environment Stuff --- */ +#define _MD_GET_ENV _PR_MD_GET_ENV +#define _MD_PUT_ENV _PR_MD_PUT_ENV + +/* --- Threading Stuff --- */ +#define _MD_DEFAULT_STACK_SIZE 0 +#define _MD_INIT_THREAD _PR_MD_INIT_THREAD +#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD +#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD +#define _MD_YIELD _PR_MD_YIELD +#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY +#define _MD_SET_CURRENT_THREAD_NAME _PR_MD_SET_CURRENT_THREAD_NAME +#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD +#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK +#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK +#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD +#define _MD_EXIT _PR_MD_EXIT +#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD +#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD +#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU +#define _MD_RESUME_CPU _PR_MD_RESUME_CPU +#define _MD_BEGIN_SUSPEND_ALL() +#define _MD_BEGIN_RESUME_ALL() +#define _MD_END_SUSPEND_ALL() +#define _MD_END_RESUME_ALL() + +/* --- Lock stuff --- */ +#define _PR_LOCK _MD_LOCK +#define _PR_UNLOCK _MD_UNLOCK + +#define _MD_NEW_LOCK _PR_MD_NEW_LOCK +#define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex)) +#define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex)) +#define _MD_TEST_AND_LOCK(lock) (EnterCriticalSection(&((lock)->mutex)),0) +#define _MD_UNLOCK _PR_MD_UNLOCK + +/* --- lock and cv waiting --- */ +#define _MD_WAIT _PR_MD_WAIT +#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER + +/* --- CVar ------------------- */ +#define _MD_WAIT_CV _PR_MD_WAIT_CV +#define _MD_NEW_CV _PR_MD_NEW_CV +#define _MD_FREE_CV _PR_MD_FREE_CV +#define _MD_NOTIFY_CV _PR_MD_NOTIFY_CV +#define _MD_NOTIFYALL_CV _PR_MD_NOTIFYALL_CV + +/* XXXMB- the IOQ stuff is certainly not working correctly yet. */ +// extern struct _MDLock _pr_ioq_lock; +#define _MD_IOQ_LOCK() +#define _MD_IOQ_UNLOCK() + + +/* --- Initialization stuff --- */ +#define _MD_START_INTERRUPTS() +#define _MD_STOP_INTERRUPTS() +#define _MD_DISABLE_CLOCK_INTERRUPTS() +#define _MD_ENABLE_CLOCK_INTERRUPTS() +#define _MD_BLOCK_CLOCK_INTERRUPTS() +#define _MD_UNBLOCK_CLOCK_INTERRUPTS() +#define _MD_EARLY_INIT _PR_MD_EARLY_INIT +#define _MD_FINAL_INIT() +#define _MD_EARLY_CLEANUP() +#define _MD_INIT_CPUS() +#define _MD_INIT_RUNNING_CPU(cpu) + +struct PRProcess; +struct PRProcessAttr; + +#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess +extern struct PRProcess * _PR_CreateWindowsProcess( + const char *path, + char *const *argv, + char *const *envp, + const struct PRProcessAttr *attr +); + +#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess +extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process); + +/* --- Wait for a child process to terminate --- */ +#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess +extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process, + PRInt32 *exitCode); + +#define _MD_KILL_PROCESS _PR_KillWindowsProcess +extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process); + +#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT +#define _MD_INIT_CONTEXT(_thread, _sp, _main, status) \ + PR_BEGIN_MACRO \ + *status = PR_TRUE; \ + PR_END_MACRO +#define _MD_SWITCH_CONTEXT +#define _MD_RESTORE_CONTEXT + +/* --- Intervals --- */ +#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT +#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL +#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC +#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000) +#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000) + +/* --- Time --- */ +extern void _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm); + +#ifdef WINCE +extern void _MD_InitTime(void); +extern void _MD_CleanupTime(void); +#endif + +/* --- Native-Thread Specific Definitions ------------------------------- */ + +extern struct PRThread * _MD_CURRENT_THREAD(void); + +#ifdef _PR_USE_STATIC_TLS +extern __declspec(thread) struct PRThread *_pr_currentThread; +#define _MD_GET_ATTACHED_THREAD() _pr_currentThread +#define _MD_SET_CURRENT_THREAD(_thread) (_pr_currentThread = (_thread)) + +extern __declspec(thread) struct PRThread *_pr_thread_last_run; +#define _MD_LAST_THREAD() _pr_thread_last_run +#define _MD_SET_LAST_THREAD(_thread) (_pr_thread_last_run = 0) + +extern __declspec(thread) struct _PRCPU *_pr_currentCPU; +#define _MD_CURRENT_CPU() _pr_currentCPU +#define _MD_SET_CURRENT_CPU(_cpu) (_pr_currentCPU = 0) +#else /* _PR_USE_STATIC_TLS */ +extern DWORD _pr_currentThreadIndex; +#define _MD_GET_ATTACHED_THREAD() ((PRThread *) TlsGetValue(_pr_currentThreadIndex)) +#define _MD_SET_CURRENT_THREAD(_thread) TlsSetValue(_pr_currentThreadIndex, (_thread)) + +extern DWORD _pr_lastThreadIndex; +#define _MD_LAST_THREAD() ((PRThread *) TlsGetValue(_pr_lastThreadIndex)) +#define _MD_SET_LAST_THREAD(_thread) TlsSetValue(_pr_lastThreadIndex, 0) + +extern DWORD _pr_currentCPUIndex; +#define _MD_CURRENT_CPU() ((struct _PRCPU *) TlsGetValue(_pr_currentCPUIndex)) +#define _MD_SET_CURRENT_CPU(_cpu) TlsSetValue(_pr_currentCPUIndex, 0) +#endif /* _PR_USE_STATIC_TLS */ + +/* --- Scheduler stuff --- */ +#define LOCK_SCHEDULER() 0 +#define UNLOCK_SCHEDULER() 0 +#define _PR_LockSched() 0 +#define _PR_UnlockSched() 0 + +/* --- Initialization stuff --- */ +#define _MD_INIT_LOCKS _PR_MD_INIT_LOCKS + +/* --- Stack stuff --- */ +#define _MD_INIT_STACK(stack, redzone) +#define _MD_CLEAR_STACK(stack) + +/* --- Memory-mapped files stuff --- */ + +struct _MDFileMap { + HANDLE hFileMap; + DWORD dwAccess; +}; + +extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size); +#define _MD_CREATE_FILE_MAP _MD_CreateFileMap + +extern PRInt32 _MD_GetMemMapAlignment(void); +#define _MD_GET_MEM_MAP_ALIGNMENT _MD_GetMemMapAlignment + +extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset, + PRUint32 len); +#define _MD_MEM_MAP _MD_MemMap + +extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size); +#define _MD_MEM_UNMAP _MD_MemUnmap + +extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap); +#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap + +extern PRStatus _MD_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len); +#define _MD_SYNC_MEM_MAP _MD_SyncMemMap + +/* --- 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/nsprpub/pr/include/md/_winnt.cfg b/nsprpub/pr/include/md/_winnt.cfg new file mode 100644 index 0000000000..e2a277fdcd --- /dev/null +++ b/nsprpub/pr/include/md/_winnt.cfg @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_PC +#define XP_PC +#endif + +#ifndef WIN32 +#define WIN32 +#endif + +#ifndef WINNT +#define WINNT +#endif + +#define PR_AF_INET6 23 /* same as AF_INET6 */ + +#if defined(_M_IX86) || defined(_X86_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#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_WORD 4 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 32 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 5 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 4 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 2 + +#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(_M_IA64) || defined(_IA64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#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 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#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_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 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 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#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_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else /* defined(_M_IX86) || defined(_X86_) */ + +#error unknown processor architecture + +#endif /* defined(_M_IX86) || defined(_X86_) */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#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/nsprpub/pr/include/md/_winnt.h b/nsprpub/pr/include/md/_winnt.h new file mode 100644 index 0000000000..faed03ec83 --- /dev/null +++ b/nsprpub/pr/include/md/_winnt.h @@ -0,0 +1,599 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_win32_defs_h___ +#define nspr_win32_defs_h___ + +/* Need to force service-pack 3 extensions to be defined by +** setting _WIN32_WINNT to NT 4.0 for winsock.h, winbase.h, winnt.h. +*/ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#elif (_WIN32_WINNT < 0x0400) +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif /* _WIN32_WINNT */ + +#include <windows.h> +#include <winsock.h> +#ifdef __MINGW32__ +#include <mswsock.h> +#endif +#include <errno.h> + +#include "prio.h" +#include "prclist.h" + +/* + * Internal configuration macros + */ + +#define PR_LINKER_ARCH "win32" +#define _PR_SI_SYSNAME "WINNT" +#if defined(_M_IX86) || defined(_X86_) +#define _PR_SI_ARCHITECTURE "x86" +#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) +#define _PR_SI_ARCHITECTURE "x86-64" +#elif defined(_M_IA64) || defined(_IA64_) +#define _PR_SI_ARCHITECTURE "ia64" +#else +#error unknown processor architecture +#endif + +#define HAVE_DLL +#define HAVE_CUSTOM_USER_THREADS +#define HAVE_THREAD_AFFINITY +#define _PR_HAVE_GETADDRINFO +#define _PR_INET6_PROBE +#ifndef _PR_INET6 +#define AF_INET6 23 +/* newer ws2tcpip.h provides these */ +#ifndef AI_CANONNAME +#define AI_CANONNAME 0x2 +#define AI_NUMERICHOST 0x4 +#define NI_NUMERICHOST 0x02 +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +#endif +#define _PR_HAVE_MD_SOCKADDR_IN6 +/* isomorphic to struct in6_addr on Windows */ +struct _md_in6_addr { + union { + PRUint8 _S6_u8[16]; + PRUint16 _S6_u16[8]; + } _S6_un; +}; +/* isomorphic to struct sockaddr_in6 on Windows */ +struct _md_sockaddr_in6 { + PRInt16 sin6_family; + PRUint16 sin6_port; + PRUint32 sin6_flowinfo; + struct _md_in6_addr sin6_addr; + PRUint32 sin6_scope_id; +}; +#endif +#define _PR_HAVE_THREADSAFE_GETHOST +#define _PR_HAVE_ATOMIC_OPS +#if defined(_M_IX86) || defined(_X86_) +#define _PR_HAVE_ATOMIC_CAS +#endif +#define PR_HAVE_WIN32_NAMED_SHARED_MEMORY +#define _PR_HAVE_PEEK_BUFFER +#define _PR_PEEK_BUFFER_MAX (32 * 1024) +#define _PR_FD_NEED_EMULATE_MSG_PEEK(fd) \ + (!(fd)->secret->nonblocking && (fd)->secret->inheritable != _PR_TRI_TRUE) +#define _PR_NEED_SECRET_AF + +/* --- Common User-Thread/Native-Thread Definitions --------------------- */ + +/* --- Globals --- */ +extern struct PRLock *_pr_schedLock; + +/* --- Typedefs --- */ +typedef void (*FiberFunc)(void *); + +#define PR_NUM_GCREGS 8 +typedef PRInt32 PR_CONTEXT_TYPE[PR_NUM_GCREGS]; +#define GC_VMBASE 0x40000000 +#define GC_VMLIMIT 0x00FFFFFF + +#define _MD_MAGIC_THREAD 0x22222222 +#define _MD_MAGIC_THREADSTACK 0x33333333 +#define _MD_MAGIC_SEGMENT 0x44444444 +#define _MD_MAGIC_DIR 0x55555555 + +struct _MDCPU { + int unused; +}; + +enum _MDIOModel { + _MD_BlockingIO = 0x38, + _MD_MultiWaitIO = 0x49 +}; + +typedef struct _MDOverlapped { + OVERLAPPED overlapped; /* Used for async I/O */ + + 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 + * structure. + */ + struct { + PRCList links; /* for group->io_ready list */ + struct PRRecvWait *desc; /* For multiwait I/O, this structure + * is associated with a PRRecvWait + * structure. + */ + struct PRWaitGroup *group; + struct TimerEvent *timer; + DWORD error; + } mw; + } data; +} _MDOverlapped; + +struct _MDThread { + /* The overlapped structure must be first! */ + struct _MDOverlapped overlapped; /* Used for async IO for this thread */ + void *acceptex_buf; /* Used for AcceptEx() */ + TRANSMIT_FILE_BUFFERS *xmit_bufs; /* Used for TransmitFile() */ + HANDLE blocked_sema; /* Threads block on this when waiting + * for IO or CondVar. + */ + PRInt32 blocked_io_status; /* Status of the completed IO */ + PRInt32 blocked_io_bytes; /* Bytes transferred for completed IO */ + PRInt32 blocked_io_error; /* Save error if status is FALSE */ + HANDLE handle; + PRUint32 id; + 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*/ + FiberFunc fiber_fn; /* main fiber routine */ + void *fiber_arg; /* arg to main fiber routine */ + PRUint32 fiber_stacksize; /* stacksize for fiber */ + PRInt32 fiber_last_error; /* last error for the fiber */ + void (*start)(void *); /* used by _PR_MD_CREATE_THREAD to + * pass its 'start' argument to + * pr_root. */ +}; + +struct _MDThreadStack { + PRUint32 magic; /* for debugging */ +}; + +struct _MDSegment { + PRUint32 magic; /* for debugging */ +}; + +#undef PROFILE_LOCKS + +struct _MDLock { + CRITICAL_SECTION mutex; /* this is recursive on NT */ +#ifdef PROFILE_LOCKS + PRInt32 hitcount; + PRInt32 misscount; +#endif +}; + +struct _MDDir { + HANDLE d_hdl; + WIN32_FIND_DATA d_entry; + PRBool firstEntry; /* Is this the entry returned + * by FindFirstFile()? */ + PRUint32 magic; /* for debugging */ +}; + +struct _MDCVar { + PRUint32 unused; +}; + +struct _MDSemaphore { + HANDLE sem; +}; + +struct _MDFileDesc { + PROsfd osfd; /* The osfd can come from one of three spaces: + * - For stdin, stdout, and stderr, we are using + * the libc file handle (0, 1, 2), which is an int. + * - For files and pipes, we are using Win32 HANDLE, + * which is a void*. + * - For sockets, we are using Winsock SOCKET, which + * is a u_int. + */ + PRBool io_model_committed; /* The io model (blocking or nonblocking) + * for this osfd has been committed and + * cannot be changed. The osfd has been + * either associated with the io + * completion port or made nonblocking. */ + PRBool sync_file_io; /* Use synchronous file I/O on the osfd + * (a file handle) */ + PRBool accepted_socket; /* Is this an accepted socket (on the + * server side)? */ + PRNetAddr peer_addr; /* If this is an accepted socket, cache + * the peer's address returned by + * AcceptEx(). This is to work around + * the bug that getpeername() on an + * socket accepted by AcceptEx() returns + * an all-zero net address. */ +}; + +struct _MDProcess { + HANDLE handle; + DWORD id; +}; + + +/* --- Misc stuff --- */ +#define _MD_GET_SP(thread) (thread)->md.gcContext[6] + +/* --- NT security stuff --- */ + +extern void _PR_NT_InitSids(void); +extern void _PR_NT_FreeSids(void); +extern PRStatus _PR_NT_MakeSecurityDescriptorACL( + PRIntn mode, + DWORD accessTable[], + PSECURITY_DESCRIPTOR *resultSD, + PACL *resultACL +); +extern void _PR_NT_FreeSecurityDescriptorACL( + PSECURITY_DESCRIPTOR pSD, PACL pACL); + +/* --- IO stuff --- */ + +extern PRInt32 _md_Associate(HANDLE); +extern PRInt32 _PR_MD_CLOSE(PROsfd osfd, PRBool socket); + +#define _MD_OPEN _PR_MD_OPEN +#define _MD_OPEN_FILE _PR_MD_OPEN_FILE +#define _MD_READ _PR_MD_READ +#define _MD_WRITE _PR_MD_WRITE +#define _MD_WRITEV _PR_MD_WRITEV +#define _MD_LSEEK _PR_MD_LSEEK +#define _MD_LSEEK64 _PR_MD_LSEEK64 +#define _MD_CLOSE_FILE(f) _PR_MD_CLOSE(f, PR_FALSE) +#define _MD_GETFILEINFO _PR_MD_GETFILEINFO +#define _MD_GETFILEINFO64 _PR_MD_GETFILEINFO64 +#define _MD_GETOPENFILEINFO _PR_MD_GETOPENFILEINFO +#define _MD_GETOPENFILEINFO64 _PR_MD_GETOPENFILEINFO64 +#define _MD_STAT _PR_MD_STAT +#define _MD_RENAME _PR_MD_RENAME +#define _MD_ACCESS _PR_MD_ACCESS +#define _MD_DELETE _PR_MD_DELETE +#define _MD_MKDIR _PR_MD_MKDIR +#define _MD_MAKE_DIR _PR_MD_MAKE_DIR +#define _MD_RMDIR _PR_MD_RMDIR +#define _MD_LOCKFILE _PR_MD_LOCKFILE +#define _MD_TLOCKFILE _PR_MD_TLOCKFILE +#define _MD_UNLOCKFILE _PR_MD_UNLOCKFILE + +/* --- Socket IO stuff --- */ +#define _MD_GET_SOCKET_ERROR() WSAGetLastError() +#define _MD_SET_SOCKET_ERROR(_err) WSASetLastError(_err) + +#define _MD_INIT_FILEDESC(fd) +#define _MD_MAKE_NONBLOCK _PR_MD_MAKE_NONBLOCK +#define _MD_INIT_FD_INHERITABLE _PR_MD_INIT_FD_INHERITABLE +#define _MD_QUERY_FD_INHERITABLE _PR_MD_QUERY_FD_INHERITABLE +#define _MD_SHUTDOWN _PR_MD_SHUTDOWN +#define _MD_LISTEN _PR_MD_LISTEN +#define _MD_CLOSE_SOCKET(s) _PR_MD_CLOSE(s, PR_TRUE) +#define _MD_SENDTO _PR_MD_SENDTO +#define _MD_RECVFROM _PR_MD_RECVFROM +#define _MD_SOCKETPAIR(s, type, proto, sv) -1 +#define _MD_GETSOCKNAME _PR_MD_GETSOCKNAME +#define _MD_GETPEERNAME _PR_MD_GETPEERNAME +#define _MD_GETSOCKOPT _PR_MD_GETSOCKOPT +#define _MD_SETSOCKOPT _PR_MD_SETSOCKOPT +#define _MD_SELECT select +extern int _PR_NTFiberSafeSelect(int, fd_set *, fd_set *, fd_set *, + const struct timeval *); +#define _MD_FSYNC _PR_MD_FSYNC +#define _MD_SOCKETAVAILABLE _PR_MD_SOCKETAVAILABLE +#define _MD_PIPEAVAILABLE _PR_MD_PIPEAVAILABLE +#define _MD_SET_FD_INHERITABLE _PR_MD_SET_FD_INHERITABLE + +#define _MD_INIT_ATOMIC() +#if defined(_M_IX86) || defined(_X86_) +#define _MD_ATOMIC_INCREMENT _PR_MD_ATOMIC_INCREMENT +#define _MD_ATOMIC_ADD _PR_MD_ATOMIC_ADD +#define _MD_ATOMIC_DECREMENT _PR_MD_ATOMIC_DECREMENT +#else /* non-x86 processors */ +#define _MD_ATOMIC_INCREMENT(x) InterlockedIncrement((PLONG)x) +#define _MD_ATOMIC_ADD(ptr,val) (InterlockedExchangeAdd((PLONG)ptr, (LONG)val) + val) +#define _MD_ATOMIC_DECREMENT(x) InterlockedDecrement((PLONG)x) +#endif /* x86 */ +#define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y) + +#define _MD_INIT_IO _PR_MD_INIT_IO +#define _MD_SOCKET _PR_MD_SOCKET +#define _MD_CONNECT _PR_MD_CONNECT + +#define _MD_ACCEPT(s, a, l, to) \ + _MD_FAST_ACCEPT(s, a, l, to, PR_FALSE, NULL, NULL) +#define _MD_FAST_ACCEPT(s, a, l, to, fast, cb, cba) \ + _PR_MD_FAST_ACCEPT(s, a, l, to, fast, cb, cba) +#define _MD_ACCEPT_READ(s, ns, ra, buf, l, t) \ + _MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, PR_FALSE, NULL, NULL) +#define _MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, fast, cb, cba) \ + _PR_MD_FAST_ACCEPT_READ(s, ns, ra, buf, l, t, fast, cb, cba) +#define _MD_UPDATE_ACCEPT_CONTEXT _PR_MD_UPDATE_ACCEPT_CONTEXT + +#define _MD_BIND _PR_MD_BIND +#define _MD_RECV _PR_MD_RECV +#define _MD_SEND _PR_MD_SEND +#define _MD_SENDFILE _PR_MD_SENDFILE +#define _MD_PR_POLL _PR_MD_PR_POLL + +/* --- Scheduler stuff --- */ +#define _MD_PAUSE_CPU _PR_MD_PAUSE_CPU + +/* --- DIR stuff --- */ +#define PR_DIRECTORY_SEPARATOR '\\' +#define PR_DIRECTORY_SEPARATOR_STR "\\" +#define PR_PATH_SEPARATOR ';' +#define PR_PATH_SEPARATOR_STR ";" +#define _MD_ERRNO() GetLastError() +#define _MD_OPEN_DIR _PR_MD_OPEN_DIR +#define _MD_CLOSE_DIR _PR_MD_CLOSE_DIR +#define _MD_READ_DIR _PR_MD_READ_DIR + +/* --- Segment stuff --- */ +#define _MD_INIT_SEGS() +#define _MD_ALLOC_SEGMENT(seg, size, vaddr) 0 +#define _MD_FREE_SEGMENT(seg) + +/* --- Environment Stuff --- */ +#define _MD_GET_ENV _PR_MD_GET_ENV +#define _MD_PUT_ENV _PR_MD_PUT_ENV + +/* --- Threading Stuff --- */ +#define _MD_DEFAULT_STACK_SIZE 0 +#define _MD_INIT_THREAD _PR_MD_INIT_THREAD +#define _MD_INIT_ATTACHED_THREAD _PR_MD_INIT_THREAD +#define _MD_CREATE_THREAD _PR_MD_CREATE_THREAD +#define _MD_JOIN_THREAD _PR_MD_JOIN_THREAD +#define _MD_END_THREAD _PR_MD_END_THREAD +#define _MD_YIELD _PR_MD_YIELD +#define _MD_SET_PRIORITY _PR_MD_SET_PRIORITY +#define _MD_SET_CURRENT_THREAD_NAME _PR_MD_SET_CURRENT_THREAD_NAME +#define _MD_CLEAN_THREAD _PR_MD_CLEAN_THREAD +#define _MD_SETTHREADAFFINITYMASK _PR_MD_SETTHREADAFFINITYMASK +#define _MD_GETTHREADAFFINITYMASK _PR_MD_GETTHREADAFFINITYMASK +#define _MD_EXIT_THREAD _PR_MD_EXIT_THREAD +#define _MD_SUSPEND_THREAD _PR_MD_SUSPEND_THREAD +#define _MD_RESUME_THREAD _PR_MD_RESUME_THREAD +#define _MD_SUSPEND_CPU _PR_MD_SUSPEND_CPU +#define _MD_RESUME_CPU _PR_MD_RESUME_CPU +#define _MD_BEGIN_SUSPEND_ALL() +#define _MD_BEGIN_RESUME_ALL() +#define _MD_END_SUSPEND_ALL() +#define _MD_END_RESUME_ALL() + +extern void _PR_Unblock_IO_Wait(PRThread *thr); + +/* --- Lock stuff --- */ +#define _MD_NEW_LOCK(lock) (InitializeCriticalSection(&((lock)->mutex)),PR_SUCCESS) +#define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex)) +#ifndef PROFILE_LOCKS +#define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex)) +#define _MD_TEST_AND_LOCK(lock) (TryEnterCriticalSection(&((lock)->mutex))== FALSE) +#define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex)) +#else +#define _MD_LOCK(lock) \ + PR_BEGIN_MACRO \ + BOOL rv = TryEnterCriticalSection(&((lock)->mutex)); \ + if (rv == TRUE) { \ + InterlockedIncrement(&((lock)->hitcount)); \ + } else { \ + InterlockedIncrement(&((lock)->misscount)); \ + EnterCriticalSection(&((lock)->mutex)); \ + } \ + PR_END_MACRO +#define _MD_TEST_AND_LOCK(lock) 0 /* XXXMB */ +#define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex)) +#endif +#define _PR_LOCK _MD_LOCK +#define _PR_UNLOCK _MD_UNLOCK + +/* --- lock and cv waiting --- */ +#define _MD_WAIT _PR_MD_WAIT +#define _MD_WAKEUP_WAITER _PR_MD_WAKEUP_WAITER + +/* XXXMB- the IOQ stuff is certainly not working correctly yet. */ +extern struct _MDLock _pr_ioq_lock; +#define _MD_IOQ_LOCK() _MD_LOCK(&_pr_ioq_lock) +#define _MD_IOQ_UNLOCK() _MD_UNLOCK(&_pr_ioq_lock) + + +/* --- Initialization stuff --- */ +#define _MD_START_INTERRUPTS() +#define _MD_STOP_INTERRUPTS() +#define _MD_DISABLE_CLOCK_INTERRUPTS() +#define _MD_ENABLE_CLOCK_INTERRUPTS() +#define _MD_BLOCK_CLOCK_INTERRUPTS() +#define _MD_UNBLOCK_CLOCK_INTERRUPTS() +#define _MD_EARLY_INIT _PR_MD_EARLY_INIT +#define _MD_FINAL_INIT() +#define _MD_EARLY_CLEANUP() +#define _MD_INIT_CPUS() +#define _MD_INIT_RUNNING_CPU(cpu) + +struct PRProcess; +struct PRProcessAttr; + +/* --- Create a new process --- */ +#define _MD_CREATE_PROCESS _PR_CreateWindowsProcess +extern struct PRProcess * _PR_CreateWindowsProcess( + const char *path, + char *const *argv, + char *const *envp, + const struct PRProcessAttr *attr +); + +#define _MD_DETACH_PROCESS _PR_DetachWindowsProcess +extern PRStatus _PR_DetachWindowsProcess(struct PRProcess *process); + +/* --- Wait for a child process to terminate --- */ +#define _MD_WAIT_PROCESS _PR_WaitWindowsProcess +extern PRStatus _PR_WaitWindowsProcess(struct PRProcess *process, + PRInt32 *exitCode); + +#define _MD_KILL_PROCESS _PR_KillWindowsProcess +extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process); + +/* --- User Threading stuff --- */ +#define HAVE_FIBERS +#define _MD_CREATE_USER_THREAD _PR_MD_CREATE_USER_THREAD +#define _MD_CREATE_PRIMORDIAL_USER_THREAD _PR_MD_CREATE_PRIMORDIAL_USER_THREAD +#define _MD_CLEANUP_BEFORE_EXIT _PR_MD_CLEANUP_BEFORE_EXIT +#define _MD_EXIT _PR_MD_EXIT +#define _MD_INIT_CONTEXT _PR_MD_INIT_CONTEXT +#define _MD_SWITCH_CONTEXT _PR_MD_SWITCH_CONTEXT +#define _MD_RESTORE_CONTEXT _PR_MD_RESTORE_CONTEXT + +/* --- Intervals --- */ +#define _MD_INTERVAL_INIT _PR_MD_INTERVAL_INIT +#define _MD_GET_INTERVAL _PR_MD_GET_INTERVAL +#define _MD_INTERVAL_PER_SEC _PR_MD_INTERVAL_PER_SEC +#define _MD_INTERVAL_PER_MILLISEC() (_PR_MD_INTERVAL_PER_SEC() / 1000) +#define _MD_INTERVAL_PER_MICROSEC() (_PR_MD_INTERVAL_PER_SEC() / 1000000) + +/* --- Time --- */ +extern void _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm); + +/* --- Native-Thread Specific Definitions ------------------------------- */ + +extern BOOL _pr_use_static_tls; + +extern __declspec(thread) struct PRThread *_pr_current_fiber; +extern DWORD _pr_currentFiberIndex; + +#define _MD_GET_ATTACHED_THREAD() \ + (_pr_use_static_tls ? _pr_current_fiber \ + : (PRThread *) TlsGetValue(_pr_currentFiberIndex)) + +extern struct PRThread * _MD_CURRENT_THREAD(void); + +#define _MD_SET_CURRENT_THREAD(_thread) \ + PR_BEGIN_MACRO \ + if (_pr_use_static_tls) { \ + _pr_current_fiber = (_thread); \ + } else { \ + TlsSetValue(_pr_currentFiberIndex, (_thread)); \ + } \ + PR_END_MACRO + +extern __declspec(thread) struct PRThread *_pr_fiber_last_run; +extern DWORD _pr_lastFiberIndex; + +#define _MD_LAST_THREAD() \ + (_pr_use_static_tls ? _pr_fiber_last_run \ + : (PRThread *) TlsGetValue(_pr_lastFiberIndex)) + +#define _MD_SET_LAST_THREAD(_thread) \ + PR_BEGIN_MACRO \ + if (_pr_use_static_tls) { \ + _pr_fiber_last_run = (_thread); \ + } else { \ + TlsSetValue(_pr_lastFiberIndex, (_thread)); \ + } \ + PR_END_MACRO + +extern __declspec(thread) struct _PRCPU *_pr_current_cpu; +extern DWORD _pr_currentCPUIndex; + +#define _MD_CURRENT_CPU() \ + (_pr_use_static_tls ? _pr_current_cpu \ + : (struct _PRCPU *) TlsGetValue(_pr_currentCPUIndex)) + +#define _MD_SET_CURRENT_CPU(_cpu) \ + PR_BEGIN_MACRO \ + if (_pr_use_static_tls) { \ + _pr_current_cpu = (_cpu); \ + } else { \ + TlsSetValue(_pr_currentCPUIndex, (_cpu)); \ + } \ + PR_END_MACRO + +extern __declspec(thread) PRUintn _pr_ints_off; +extern DWORD _pr_intsOffIndex; + +#define _MD_GET_INTSOFF() \ + (_pr_use_static_tls ? _pr_ints_off \ + : (PRUintn) TlsGetValue(_pr_intsOffIndex)) + +#define _MD_SET_INTSOFF(_val) \ + PR_BEGIN_MACRO \ + if (_pr_use_static_tls) { \ + _pr_ints_off = (_val); \ + } else { \ + TlsSetValue(_pr_intsOffIndex, (LPVOID) (_val)); \ + } \ + PR_END_MACRO + +/* --- Initialization stuff --- */ +#define _MD_INIT_LOCKS() + +/* --- Stack stuff --- */ +#define _MD_INIT_STACK(stack, redzone) +#define _MD_CLEAR_STACK(stack) + +/* --- Memory-mapped files stuff --- */ + +struct _MDFileMap { + HANDLE hFileMap; + DWORD dwAccess; +}; + +extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size); +#define _MD_CREATE_FILE_MAP _MD_CreateFileMap + +extern PRInt32 _MD_GetMemMapAlignment(void); +#define _MD_GET_MEM_MAP_ALIGNMENT _MD_GetMemMapAlignment + +extern void * _MD_MemMap(struct PRFileMap *fmap, PRInt64 offset, + PRUint32 len); +#define _MD_MEM_MAP _MD_MemMap + +extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size); +#define _MD_MEM_UNMAP _MD_MemUnmap + +extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap); +#define _MD_CLOSE_FILE_MAP _MD_CloseFileMap + +extern PRStatus _MD_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len); +#define _MD_SYNC_MEM_MAP _MD_SyncMemMap + +/* --- 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/nsprpub/pr/include/md/prosdep.h b/nsprpub/pr/include/md/prosdep.h new file mode 100644 index 0000000000..42eba3da74 --- /dev/null +++ b/nsprpub/pr/include/md/prosdep.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prosdep_h___ +#define prosdep_h___ + +/* +** Get OS specific header information +*/ +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +#ifdef XP_PC + +#include "md/_pcos.h" +#ifdef WINNT +#include "md/_winnt.h" +#include "md/_win32_errors.h" +#elif defined(WIN95) || defined(WINCE) +#include "md/_win95.h" +#include "md/_win32_errors.h" +#elif defined(OS2) +#include "md/_os2.h" +#include "md/_os2_errors.h" +#else +#error unknown Windows platform +#endif + +#elif defined(XP_UNIX) + +#if defined(AIX) +#include "md/_aix.h" + +#elif defined(FREEBSD) +#include "md/_freebsd.h" + +#elif defined(NETBSD) +#include "md/_netbsd.h" + +#elif defined(OPENBSD) +#include "md/_openbsd.h" + +#elif defined(BSDI) +#include "md/_bsdi.h" + +#elif defined(HPUX) +#include "md/_hpux.h" + +#elif defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) +#include "md/_linux.h" + +#elif defined(DARWIN) +#include "md/_darwin.h" + +#elif defined(SOLARIS) +#include "md/_solaris.h" + +#elif defined(SCO) +#include "md/_scoos.h" + +#elif defined(UNIXWARE) +#include "md/_unixware.h" + +#elif defined(QNX) +#include "md/_qnx.h" + +#elif defined(NTO) +#include "md/_nto.h" + +#elif defined(RISCOS) +#include "md/_riscos.h" + +#else +#error unknown Unix flavor + +#endif + +#include "md/_unixos.h" +#include "md/_unix_errors.h" + +#else + +#error "The platform is not Unix, Windows, or Mac" + +#endif + +#ifdef _PR_PTHREADS +#include "md/_pth.h" +#endif + +PR_END_EXTERN_C + +#endif /* prosdep_h___ */ diff --git a/nsprpub/pr/include/nspr.h b/nsprpub/pr/include/nspr.h new file mode 100644 index 0000000000..0cbc71c10d --- /dev/null +++ b/nsprpub/pr/include/nspr.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nspr_h___ +#define nspr_h___ + +#include "pratom.h" +#include "prbit.h" +#include "prclist.h" +#include "prcmon.h" +#include "prcvar.h" +#include "prdtoa.h" +#include "prenv.h" +#include "prerror.h" +#include "prinet.h" +#include "prinit.h" +#include "prinrval.h" +#include "prio.h" +#include "pripcsem.h" +#include "prlink.h" +#include "prlock.h" +#include "prlog.h" +#include "prlong.h" +#include "prmem.h" +#include "prmon.h" +#include "prmwait.h" +#include "prnetdb.h" +#include "prprf.h" +#include "prproces.h" +#include "prrng.h" +#include "prrwlock.h" +#include "prshm.h" +#include "prshma.h" +#include "prsystem.h" +#include "prthread.h" +#include "prtime.h" +#include "prtpool.h" +#include "prtrace.h" +#include "prtypes.h" + +#endif /* nspr_h___ */ diff --git a/nsprpub/pr/include/obsolete/Makefile.in b/nsprpub/pr/include/obsolete/Makefile.in new file mode 100644 index 0000000000..27116a2c7a --- /dev/null +++ b/nsprpub/pr/include/obsolete/Makefile.in @@ -0,0 +1,28 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +HEADERS = $(wildcard $(srcdir)/*.h) + +RELEASE_HEADERS = $(HEADERS) +RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)/obsolete + +include_subdir = obsolete + +include $(topsrcdir)/config/rules.mk + +export:: $(RELEASE_HEADERS) + $(INSTALL) -m 444 $(RELEASE_HEADERS) $(dist_includedir)/obsolete diff --git a/nsprpub/pr/include/obsolete/pralarm.h b/nsprpub/pr/include/obsolete/pralarm.h new file mode 100644 index 0000000000..9ae3a4bcf0 --- /dev/null +++ b/nsprpub/pr/include/obsolete/pralarm.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: pralarm.h +** Description: API to periodic alarms. +** +** +** Alarms are defined to invoke some client specified function at +** a time in the future. The notification may be a one time event +** or repeated at a fixed interval. The interval at which the next +** notification takes place may be modified by the client code only +** during the respective notification. +** +** The notification is delivered on a thread that is part of the +** alarm context (PRAlarm). The thread will inherit the priority +** of the Alarm creator. +** +** Any number of periodic alarms (PRAlarmID) may be created within +** the context of a single alarm (PRAlarm). The notifications will be +** scheduled as close to the desired time as possible. +** +** Repeating periodic notifies are expected to run at a fixed rate. +** That rate is expressed as some number of notifies per period where +** the period is much larger than a PRIntervalTime (see prinrval.h). +*/ + +#if !defined(pralarm_h) +#define pralarm_h + +#include "prtypes.h" +#include "prinrval.h" + + +PR_BEGIN_EXTERN_C + +/**********************************************************************/ +/************************* TYPES AND CONSTANTS ************************/ +/**********************************************************************/ + +typedef struct PRAlarm PRAlarm; +typedef struct PRAlarmID PRAlarmID; + +typedef PRBool (PR_CALLBACK *PRPeriodicAlarmFn)( + PRAlarmID *id, void *clientData, PRUint32 late); + +/**********************************************************************/ +/****************************** FUNCTIONS *****************************/ +/**********************************************************************/ + +/*********************************************************************** +** FUNCTION: PR_CreateAlarm +** DESCRIPTION: +** Create an alarm context. +** INPUTS: void +** OUTPUTS: None +** RETURN: PRAlarm* +** +** SIDE EFFECTS: +** This creates an alarm context, which is an object used for subsequent +** notification creations. It also creates a thread that will be used to +** deliver the notifications that are expected to be defined. The client +** is resposible for destroying the context when appropriate. +** RESTRICTIONS: +** None. +** MEMORY: The object (PRAlarm) and a thread to support notifications. +** ALGORITHM: N/A +***********************************************************************/ +NSPR_API(PRAlarm*) PR_CreateAlarm(void); + +/*********************************************************************** +** FUNCTION: PR_DestroyAlarm +** DESCRIPTION: +** Destroys the context created by PR_CreateAlarm(). +** INPUTS: PRAlarm* +** OUTPUTS: None +** RETURN: PRStatus +** +** SIDE EFFECTS: +** This destroys the context that was created by PR_CreateAlarm(). +** If there are any active alarms (PRAlarmID), they will be cancelled. +** Once that is done, the thread that was used to deliver the alarms +** will be joined. +** RESTRICTIONS: +** None. +** MEMORY: N/A +** ALGORITHM: N/A +***********************************************************************/ +NSPR_API(PRStatus) PR_DestroyAlarm(PRAlarm *alarm); + +/*********************************************************************** +** FUNCTION: PR_SetAlarm +** DESCRIPTION: +** Creates a periodic notifier that is to be delivered to a specified +** function at some fixed interval. +** INPUTS: PRAlarm *alarm Parent alarm context +** PRIntervalTime period Interval over which the notifies +** are delivered. +** PRUint32 rate The rate within the interval that +** the notifies will be delivered. +** PRPeriodicAlarmFn function Entry point where the notifies +** will be delivered. +** OUTPUTS: None +** RETURN: PRAlarmID* Handle to the notifier just created +** or NULL if the request failed. +** +** SIDE EFFECTS: +** A periodic notifier is created. The notifications will be delivered +** by the alarm's internal thread at a fixed interval whose rate is the +** number of interrupts per interval specified. The first notification +** will be delivered as soon as possible, and they will continue until +** the notifier routine indicates that they should cease of the alarm +** context is destroyed (PR_DestroyAlarm). +** RESTRICTIONS: +** None. +** MEMORY: Memory for the notifier object. +** ALGORITHM: The rate at which notifications are delivered are stated +** to be "'rate' notifies per 'interval'". The exact time of +** the notification is computed based on a epoch established +** when the notifier was set. Each notification is delivered +** not ealier than the epoch plus the fixed rate times the +** notification sequence number. Such notifications have the +** potential to be late by not more than 'interval'/'rate'. +** The amount of lateness of one notification is taken into +** account on the next in an attempt to avoid long term slew. +***********************************************************************/ +NSPR_API(PRAlarmID*) PR_SetAlarm( + PRAlarm *alarm, PRIntervalTime period, PRUint32 rate, + PRPeriodicAlarmFn function, void *clientData); + +/*********************************************************************** +** FUNCTION: PR_ResetAlarm +** DESCRIPTION: +** Resets an existing alarm. +** INPUTS: PRAlarmID *id Identify of the notifier. +** PRIntervalTime period Interval over which the notifies +** are delivered. +** PRUint32 rate The rate within the interval that +** the notifies will be delivered. +** OUTPUTS: None +** RETURN: PRStatus Indication of completion. +** +** SIDE EFFECTS: +** An existing alarm may have its period and rate redefined. The +** additional side effect is that the notifier's epoch is recomputed. +** The first notification delivered by the newly refreshed alarm is +** defined to be 'interval'/'rate' from the time of the reset. +** RESTRICTIONS: +** This function may only be called in the notifier for that alarm. +** MEMORY: N/A. +** ALGORITHM: See PR_SetAlarm(). +***********************************************************************/ +NSPR_API(PRStatus) PR_ResetAlarm( + PRAlarmID *id, PRIntervalTime period, PRUint32 rate); + +PR_END_EXTERN_C + +#endif /* !defined(pralarm_h) */ + +/* prinrval.h */ diff --git a/nsprpub/pr/include/obsolete/probslet.h b/nsprpub/pr/include/obsolete/probslet.h new file mode 100644 index 0000000000..db743c5ed1 --- /dev/null +++ b/nsprpub/pr/include/obsolete/probslet.h @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** A collection of things thought to be obsolete +*/ + +#if defined(PROBSLET_H) +#else +#define PROBSLET_H + +#include "prio.h" +#include "private/pprio.h" /* for PROsfd */ + +PR_BEGIN_EXTERN_C + +/* +** Yield the current thread. The proper function to use in place of +** PR_Yield() is PR_Sleep() with an argument of PR_INTERVAL_NO_WAIT. +*/ +NSPR_API(PRStatus) PR_Yield(void); + +/************************************************************************/ +/************* The following definitions are for select *****************/ +/************************************************************************/ + +/* +** The following is obsolete and will be deleted in the next release! +** These are provided for compatibility, but are GUARANTEED to be slow. +** +** Override PR_MAX_SELECT_DESC if you need more space in the select set. +*/ +#ifndef PR_MAX_SELECT_DESC +#define PR_MAX_SELECT_DESC 1024 +#endif +typedef struct PR_fd_set { + PRUint32 hsize; + PRFileDesc *harray[PR_MAX_SELECT_DESC]; + PRUint32 nsize; + PROsfd narray[PR_MAX_SELECT_DESC]; +} PR_fd_set; + +/* +************************************************************************* +** FUNCTION: PR_Select +** DESCRIPTION: +** +** The call returns as soon as I/O is ready on one or more of the underlying +** file/socket descriptors or an exceptional condition is pending. A count of the +** number of ready descriptors is returned unless a timeout occurs in which case +** zero is returned. On return, PR_Select replaces the given descriptor sets with +** subsets consisting of those descriptors that are ready for the requested condition. +** The total number of ready descriptors in all the sets is the return value. +** +** INPUTS: +** PRInt32 num +** This argument is unused but is provided for select(unix) interface +** compatability. All input PR_fd_set arguments are self-describing +** with its own maximum number of elements in the set. +** +** PR_fd_set *readfds +** A set describing the io descriptors for which ready for reading +** condition is of interest. +** +** PR_fd_set *writefds +** A set describing the io descriptors for which ready for writing +** condition is of interest. +** +** PR_fd_set *exceptfds +** A set describing the io descriptors for which exception pending +** condition is of interest. +** +** Any of the above readfds, writefds or exceptfds may be given as NULL +** pointers if no descriptors are of interest for that particular condition. +** +** PRIntervalTime timeout +** Amount of time the call will block waiting for I/O to become ready. +** If this time expires without any I/O becoming ready, the result will +** be zero. +** +** OUTPUTS: +** PR_fd_set *readfds +** A set describing the io descriptors which are ready for reading. +** +** PR_fd_set *writefds +** A set describing the io descriptors which are ready for writing. +** +** PR_fd_set *exceptfds +** A set describing the io descriptors which have pending exception. +** +** RETURN:PRInt32 +** Number of io descriptors with asked for conditions or zero if the function +** timed out or -1 on failure. The reason for the failure is obtained by +** calling PR_GetError(). +** XXX can we implement this on windoze and mac? +************************************************************************** +*/ +NSPR_API(PRInt32) PR_Select( + PRInt32 num, PR_fd_set *readfds, PR_fd_set *writefds, + PR_fd_set *exceptfds, PRIntervalTime timeout); + +/* +** The following are not thread safe for two threads operating on them at the +** same time. +** +** The following routines are provided for manipulating io descriptor sets. +** PR_FD_ZERO(&fdset) initializes a descriptor set fdset to the null set. +** PR_FD_SET(fd, &fdset) includes a particular file descriptor fd in fdset. +** PR_FD_CLR(fd, &fdset) removes a file descriptor fd from fdset. +** PR_FD_ISSET(fd, &fdset) is nonzero if file descriptor fd is a member of +** fdset, zero otherwise. +** +** PR_FD_NSET(osfd, &fdset) includes a particular native file descriptor osfd +** in fdset. +** PR_FD_NCLR(osfd, &fdset) removes a native file descriptor osfd from fdset. +** PR_FD_NISSET(osfd, &fdset) is nonzero if native file descriptor osfd is a member of +** fdset, zero otherwise. +*/ + +NSPR_API(void) PR_FD_ZERO(PR_fd_set *set); +NSPR_API(void) PR_FD_SET(PRFileDesc *fd, PR_fd_set *set); +NSPR_API(void) PR_FD_CLR(PRFileDesc *fd, PR_fd_set *set); +NSPR_API(PRInt32) PR_FD_ISSET(PRFileDesc *fd, PR_fd_set *set); +NSPR_API(void) PR_FD_NSET(PROsfd osfd, PR_fd_set *set); +NSPR_API(void) PR_FD_NCLR(PROsfd osfd, PR_fd_set *set); +NSPR_API(PRInt32) PR_FD_NISSET(PROsfd osfd, PR_fd_set *set); + +/* +** The next two entry points should not be in the API, but they are +** declared here for historical reasons. +*/ + +NSPR_API(PRInt32) PR_GetSysfdTableMax(void); + +NSPR_API(PRInt32) PR_SetSysfdTableSize(PRIntn table_size); + +#ifndef NO_NSPR_10_SUPPORT +#include <sys/stat.h> + +NSPR_API(PRInt32) PR_Stat(const char *path, struct stat *buf); +#endif /* NO_NSPR_10_SUPPORT */ + +PR_END_EXTERN_C + +#endif /* defined(PROBSLET_H) */ + +/* probslet.h */ diff --git a/nsprpub/pr/include/obsolete/protypes.h b/nsprpub/pr/include/obsolete/protypes.h new file mode 100644 index 0000000000..5400afb234 --- /dev/null +++ b/nsprpub/pr/include/obsolete/protypes.h @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This header typedefs the old 'native' types to the new PR<type>s. + * These definitions are scheduled to be eliminated at the earliest + * possible time. The NSPR API is implemented and documented using + * the new definitions. + */ + +#if !defined(PROTYPES_H) +#define PROTYPES_H + +typedef PRUintn uintn; +#ifndef _XP_Core_ +typedef PRIntn intn; +#endif + +/* + * It is trickier to define uint, int8, uint8, int16, uint16, + * int32, uint32, int64, and uint64 because some of these int + * types are defined by standard header files on some platforms. + * Our strategy here is to include all such standard headers + * first, and then define these int types only if they are not + * defined by those standard headers. + */ + +/* + * SVR4 typedef of uint is commonly found on UNIX machines. + * + * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h) + * defines the types int8, int16, int32, and int64. + * + * On OS/2, sys/types.h defines uint. + */ +#if defined(XP_UNIX) || defined(XP_OS2) +#include <sys/types.h> +#endif + +/* model.h on HP-UX defines int8, int16, and int32. */ +#ifdef HPUX +#include <model.h> +#endif + +/* + * uint + */ + +#if !defined(XP_OS2) && !defined(XP_UNIX) || defined(NTO) +typedef PRUintn uint; +#endif + +/* + * uint64 + */ + +typedef PRUint64 uint64; + +/* + * uint32 + */ + +#if !defined(_WIN32) && !defined(XP_OS2) && !defined(NTO) +typedef PRUint32 uint32; +#else +typedef unsigned long uint32; +#endif + +/* + * uint16 + */ + +typedef PRUint16 uint16; + +/* + * uint8 + */ + +typedef PRUint8 uint8; + +/* + * int64 + */ + +#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) +typedef PRInt64 int64; +#endif + +/* + * int32 + */ + +#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \ + && !defined(HPUX) +#if !defined(_WIN32) && !defined(XP_OS2) && !defined(NTO) +typedef PRInt32 int32; +#else +typedef long int32; +#endif +#endif + +/* + * int16 + */ + +#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \ + && !defined(HPUX) +typedef PRInt16 int16; +#endif + +/* + * int8 + */ + +#if !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \ + && !defined(HPUX) +typedef PRInt8 int8; +#endif + +typedef PRFloat64 float64; +typedef PRUptrdiff uptrdiff_t; +typedef PRUword uprword_t; +typedef PRWord prword_t; + + +/* Re: prbit.h */ +#define TEST_BIT PR_TEST_BIT +#define SET_BIT PR_SET_BIT +#define CLEAR_BIT PR_CLEAR_BIT + +/* Re: prarena.h->plarena.h */ +#define PRArena PLArena +#define PRArenaPool PLArenaPool +#define PRArenaStats PLArenaStats +#define PR_ARENA_ALIGN PL_ARENA_ALIGN +#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL +#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE +#define PR_ARENA_GROW PL_ARENA_GROW +#define PR_ARENA_MARK PL_ARENA_MARK +#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED +#define PR_CLEAR_ARENA PL_CLEAR_ARENA +#define PR_ARENA_RELEASE PL_ARENA_RELEASE +#define PR_COUNT_ARENA PL_COUNT_ARENA +#define PR_ARENA_DESTROY PL_ARENA_DESTROY +#define PR_InitArenaPool PL_InitArenaPool +#define PR_FreeArenaPool PL_FreeArenaPool +#define PR_FinishArenaPool PL_FinishArenaPool +#define PR_CompactArenaPool PL_CompactArenaPool +#define PR_ArenaFinish PL_ArenaFinish +#define PR_ArenaAllocate PL_ArenaAllocate +#define PR_ArenaGrow PL_ArenaGrow +#define PR_ArenaRelease PL_ArenaRelease +#define PR_ArenaCountAllocation PL_ArenaCountAllocation +#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth +#define PR_ArenaCountGrowth PL_ArenaCountGrowth +#define PR_ArenaCountRelease PL_ArenaCountRelease +#define PR_ArenaCountRetract PL_ArenaCountRetract + +/* Re: prhash.h->plhash.h */ +#define PRHashEntry PLHashEntry +#define PRHashTable PLHashTable +#define PRHashNumber PLHashNumber +#define PRHashFunction PLHashFunction +#define PRHashComparator PLHashComparator +#define PRHashEnumerator PLHashEnumerator +#define PRHashAllocOps PLHashAllocOps +#define PR_NewHashTable PL_NewHashTable +#define PR_HashTableDestroy PL_HashTableDestroy +#define PR_HashTableRawLookup PL_HashTableRawLookup +#define PR_HashTableRawAdd PL_HashTableRawAdd +#define PR_HashTableRawRemove PL_HashTableRawRemove +#define PR_HashTableAdd PL_HashTableAdd +#define PR_HashTableRemove PL_HashTableRemove +#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries +#define PR_HashTableLookup PL_HashTableLookup +#define PR_HashTableDump PL_HashTableDump +#define PR_HashString PL_HashString +#define PR_CompareStrings PL_CompareStrings +#define PR_CompareValues PL_CompareValues + +#endif /* !defined(PROTYPES_H) */ diff --git a/nsprpub/pr/include/obsolete/prsem.h b/nsprpub/pr/include/obsolete/prsem.h new file mode 100644 index 0000000000..d1981d2b9b --- /dev/null +++ b/nsprpub/pr/include/obsolete/prsem.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prsem_h___ +#define prsem_h___ + +/* +** API for counting semaphores. Semaphores are counting synchronizing +** variables based on a lock and a condition variable. They are lightweight +** contention control for a given count of resources. +*/ +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +typedef struct PRSemaphore PRSemaphore; + +/* +** Create a new semaphore object. +*/ +NSPR_API(PRSemaphore*) PR_NewSem(PRUintn value); + +/* +** Destroy the given semaphore object. +** +*/ +NSPR_API(void) PR_DestroySem(PRSemaphore *sem); + +/* +** Wait on a Semaphore. +** +** This routine allows a calling thread to wait or proceed depending upon the +** state of the semahore sem. The thread can proceed only if the counter value +** of the semaphore sem is currently greater than 0. If the value of semaphore +** sem is positive, it is decremented by one and the routine returns immediately +** allowing the calling thread to continue. If the value of semaphore sem is 0, +** the calling thread blocks awaiting the semaphore to be released by another +** thread. +** +** This routine can return PR_PENDING_INTERRUPT if the waiting thread +** has been interrupted. +*/ +NSPR_API(PRStatus) PR_WaitSem(PRSemaphore *sem); + +/* +** This routine increments the counter value of the semaphore. If other threads +** are blocked for the semaphore, then the scheduler will determine which ONE +** thread will be unblocked. +*/ +NSPR_API(void) PR_PostSem(PRSemaphore *sem); + +/* +** Returns the value of the semaphore referenced by sem without affecting +** the state of the semaphore. The value represents the semaphore vaule +F** at the time of the call, but may not be the actual value when the +** caller inspects it. +*/ +NSPR_API(PRUintn) PR_GetValueSem(PRSemaphore *sem); + +PR_END_EXTERN_C + +#endif /* prsem_h___ */ diff --git a/nsprpub/pr/include/pratom.h b/nsprpub/pr/include/pratom.h new file mode 100644 index 0000000000..11a8aaedc9 --- /dev/null +++ b/nsprpub/pr/include/pratom.h @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* GLOBAL FUNCTIONS: +** DESCRIPTION: +** PR Atomic operations +*/ + +#ifndef pratom_h___ +#define pratom_h___ + +#include "prtypes.h" +#include "prlock.h" + +PR_BEGIN_EXTERN_C + +/* +** FUNCTION: PR_AtomicIncrement +** DESCRIPTION: +** Atomically increment a 32 bit value. +** INPUTS: +** val: a pointer to the value to increment +** RETURN: +** the returned value is the result of the increment +*/ +NSPR_API(PRInt32) PR_AtomicIncrement(PRInt32 *val); + +/* +** FUNCTION: PR_AtomicDecrement +** DESCRIPTION: +** Atomically decrement a 32 bit value. +** INPUTS: +** val: a pointer to the value to decrement +** RETURN: +** the returned value is the result of the decrement +*/ +NSPR_API(PRInt32) PR_AtomicDecrement(PRInt32 *val); + +/* +** FUNCTION: PR_AtomicSet +** DESCRIPTION: +** Atomically set a 32 bit value. +** INPUTS: +** val: A pointer to a 32 bit value to be set +** newval: The newvalue to assign to val +** RETURN: +** Returns the prior value +*/ +NSPR_API(PRInt32) PR_AtomicSet(PRInt32 *val, PRInt32 newval); + +/* +** FUNCTION: PR_AtomicAdd +** DESCRIPTION: +** Atomically add a 32 bit value. +** INPUTS: +** ptr: a pointer to the value to increment +** val: value to be added +** RETURN: +** the returned value is the result of the addition +*/ +NSPR_API(PRInt32) PR_AtomicAdd(PRInt32 *ptr, PRInt32 val); + +/* +** MACRO: PR_ATOMIC_INCREMENT +** MACRO: PR_ATOMIC_DECREMENT +** MACRO: PR_ATOMIC_SET +** MACRO: PR_ATOMIC_ADD +** DESCRIPTION: +** Macro versions of the atomic operations. They may be implemented +** as compiler intrinsics. +** +** IMPORTANT NOTE TO NSPR MAINTAINERS: +** Implement these macros with compiler intrinsics only on platforms +** where the PR_AtomicXXX functions are truly atomic (i.e., where the +** configuration macro _PR_HAVE_ATOMIC_OPS is defined). Otherwise, +** the macros and functions won't be compatible and can't be used +** interchangeably. +*/ +#if defined(_WIN32) && !defined(_WIN32_WCE) && \ + (!defined(_MSC_VER) || (_MSC_VER >= 1310)) + +#include <intrin.h> + +#ifdef _MSC_VER +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedExchange) +#pragma intrinsic(_InterlockedExchangeAdd) +#endif + +#define PR_ATOMIC_INCREMENT(val) _InterlockedIncrement((long volatile *)(val)) +#define PR_ATOMIC_DECREMENT(val) _InterlockedDecrement((long volatile *)(val)) +#define PR_ATOMIC_SET(val, newval) \ + _InterlockedExchange((long volatile *)(val), (long)(newval)) +#define PR_ATOMIC_ADD(ptr, val) \ + (_InterlockedExchangeAdd((long volatile *)(ptr), (long)(val)) + (val)) + +#elif ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && \ + ((defined(__APPLE__) && \ + (defined(__ppc__) || defined(__i386__) || defined(__x86_64__))) || \ + (defined(__linux__) && \ + ((defined(__i386__) && \ + defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \ + defined(__ia64__) || defined(__x86_64__) || \ + defined(__powerpc__) || \ + (defined(__arm__) && \ + defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \ + defined(__aarch64__) || defined(__alpha) || \ + (defined(__mips__) && \ + defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4))))) + +/* + * Because the GCC manual warns that some processors may support + * reduced functionality of __sync_lock_test_and_set, we test for the + * processors that we believe support a full atomic exchange operation. + */ + +#define PR_ATOMIC_INCREMENT(val) __sync_add_and_fetch(val, 1) +#define PR_ATOMIC_DECREMENT(val) __sync_sub_and_fetch(val, 1) +#define PR_ATOMIC_SET(val, newval) __sync_lock_test_and_set(val, newval) +#define PR_ATOMIC_ADD(ptr, val) __sync_add_and_fetch(ptr, val) + +#else + +#define PR_ATOMIC_INCREMENT(val) PR_AtomicIncrement(val) +#define PR_ATOMIC_DECREMENT(val) PR_AtomicDecrement(val) +#define PR_ATOMIC_SET(val, newval) PR_AtomicSet(val, newval) +#define PR_ATOMIC_ADD(ptr, val) PR_AtomicAdd(ptr, val) + +#endif + +/* +** LIFO linked-list (stack) +*/ +typedef struct PRStackElemStr PRStackElem; + +struct PRStackElemStr { + PRStackElem *prstk_elem_next; /* next pointer MUST be at offset 0; + assembly language code relies on this */ +}; + +typedef struct PRStackStr PRStack; + +/* +** FUNCTION: PR_CreateStack +** DESCRIPTION: +** Create a stack, a LIFO linked list +** INPUTS: +** stack_name: a pointer to string containing the name of the stack +** RETURN: +** A pointer to the created stack, if successful, else NULL. +*/ +NSPR_API(PRStack *) PR_CreateStack(const char *stack_name); + +/* +** FUNCTION: PR_StackPush +** DESCRIPTION: +** Push an element on the top of the stack +** INPUTS: +** stack: pointer to the stack +** stack_elem: pointer to the stack element +** RETURN: +** None +*/ +NSPR_API(void) PR_StackPush(PRStack *stack, PRStackElem *stack_elem); + +/* +** FUNCTION: PR_StackPop +** DESCRIPTION: +** Remove the element on the top of the stack +** INPUTS: +** stack: pointer to the stack +** RETURN: +** A pointer to the stack element removed from the top of the stack, +** if non-empty, +** else NULL +*/ +NSPR_API(PRStackElem *) PR_StackPop(PRStack *stack); + +/* +** FUNCTION: PR_DestroyStack +** DESCRIPTION: +** Destroy the stack +** INPUTS: +** stack: pointer to the stack +** RETURN: +** PR_SUCCESS - if successfully deleted +** PR_FAILURE - if the stack is not empty +** PR_GetError will return +** PR_INVALID_STATE_ERROR - stack is not empty +*/ +NSPR_API(PRStatus) PR_DestroyStack(PRStack *stack); + +PR_END_EXTERN_C + +#endif /* pratom_h___ */ diff --git a/nsprpub/pr/include/prbit.h b/nsprpub/pr/include/prbit.h new file mode 100644 index 0000000000..50c65923af --- /dev/null +++ b/nsprpub/pr/include/prbit.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prbit_h___ +#define prbit_h___ + +#include "prtypes.h" +PR_BEGIN_EXTERN_C + +/* +** Replace compare/jump/add/shift sequence with compiler built-in/intrinsic +** functions. +*/ +#if defined(_WIN32) && (_MSC_VER >= 1300) && \ + (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || \ + defined(_M_ARM64)) +# include <intrin.h> +# pragma intrinsic(_BitScanForward,_BitScanReverse) +__forceinline static int __prBitScanForward32(unsigned int val) +{ + unsigned long idx; + _BitScanForward(&idx, (unsigned long)val); + return( (int)idx ); +} +__forceinline static int __prBitScanReverse32(unsigned int val) +{ + unsigned long idx; + _BitScanReverse(&idx, (unsigned long)val); + return( (int)(31-idx) ); +} +# define pr_bitscan_ctz32(val) __prBitScanForward32(val) +# define pr_bitscan_clz32(val) __prBitScanReverse32(val) +# define PR_HAVE_BUILTIN_BITSCAN32 +#elif ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__aarch64__)) +# define pr_bitscan_ctz32(val) __builtin_ctz(val) +# define pr_bitscan_clz32(val) __builtin_clz(val) +# define PR_HAVE_BUILTIN_BITSCAN32 +#endif /* MSVC || GCC */ + +/* +** A prbitmap_t is a long integer that can be used for bitmaps +*/ +typedef unsigned long prbitmap_t; + +#define PR_TEST_BIT(_map,_bit) \ + ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] & (1L << ((_bit) & (PR_BITS_PER_LONG-1)))) +#define PR_SET_BIT(_map,_bit) \ + ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] |= (1L << ((_bit) & (PR_BITS_PER_LONG-1)))) +#define PR_CLEAR_BIT(_map,_bit) \ + ((_map)[(_bit)>>PR_BITS_PER_LONG_LOG2] &= ~(1L << ((_bit) & (PR_BITS_PER_LONG-1)))) + +/* +** Compute the log of the least power of 2 greater than or equal to n +*/ +NSPR_API(PRIntn) PR_CeilingLog2(PRUint32 i); + +/* +** Compute the log of the greatest power of 2 less than or equal to n +*/ +NSPR_API(PRIntn) PR_FloorLog2(PRUint32 i); + +/* +** Macro version of PR_CeilingLog2: Compute the log of the least power of +** 2 greater than or equal to _n. The result is returned in _log2. +*/ +#ifdef PR_HAVE_BUILTIN_BITSCAN32 +#define PR_CEILING_LOG2(_log2,_n) \ + PR_BEGIN_MACRO \ + PRUint32 j_ = (PRUint32)(_n); \ + (_log2) = (j_ <= 1 ? 0 : 32 - pr_bitscan_clz32(j_ - 1)); \ + PR_END_MACRO +#else +#define PR_CEILING_LOG2(_log2,_n) \ + PR_BEGIN_MACRO \ + PRUint32 j_ = (PRUint32)(_n); \ + (_log2) = 0; \ + if ((j_) & ((j_)-1)) \ + (_log2) += 1; \ + if ((j_) >> 16) \ + (_log2) += 16, (j_) >>= 16; \ + if ((j_) >> 8) \ + (_log2) += 8, (j_) >>= 8; \ + if ((j_) >> 4) \ + (_log2) += 4, (j_) >>= 4; \ + if ((j_) >> 2) \ + (_log2) += 2, (j_) >>= 2; \ + if ((j_) >> 1) \ + (_log2) += 1; \ + PR_END_MACRO +#endif /* PR_HAVE_BUILTIN_BITSCAN32 */ + +/* +** Macro version of PR_FloorLog2: Compute the log of the greatest power of +** 2 less than or equal to _n. The result is returned in _log2. +** +** This is equivalent to finding the highest set bit in the word. +*/ +#ifdef PR_HAVE_BUILTIN_BITSCAN32 +#define PR_FLOOR_LOG2(_log2,_n) \ + PR_BEGIN_MACRO \ + PRUint32 j_ = (PRUint32)(_n); \ + (_log2) = 31 - pr_bitscan_clz32((j_) | 1); \ + PR_END_MACRO +#else +#define PR_FLOOR_LOG2(_log2,_n) \ + PR_BEGIN_MACRO \ + PRUint32 j_ = (PRUint32)(_n); \ + (_log2) = 0; \ + if ((j_) >> 16) \ + (_log2) += 16, (j_) >>= 16; \ + if ((j_) >> 8) \ + (_log2) += 8, (j_) >>= 8; \ + if ((j_) >> 4) \ + (_log2) += 4, (j_) >>= 4; \ + if ((j_) >> 2) \ + (_log2) += 2, (j_) >>= 2; \ + if ((j_) >> 1) \ + (_log2) += 1; \ + PR_END_MACRO +#endif /* PR_HAVE_BUILTIN_BITSCAN32 */ + +/* +** Macros for rotate left and right. The argument 'a' must be an unsigned +** 32-bit integer type such as PRUint32. +** +** There is no rotate operation in the C Language, so the construct +** (a << 4) | (a >> 28) is frequently used instead. Most compilers convert +** this to a rotate instruction, but MSVC doesn't without a little help. +** To get MSVC to generate a rotate instruction, we have to use the _rotl +** or _rotr intrinsic and use a pragma to make it inline. +** +** Note: MSVC in VS2005 will do an inline rotate instruction on the above +** construct. +*/ + +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \ + defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) +#include <stdlib.h> +#pragma intrinsic(_rotl, _rotr) +#define PR_ROTATE_LEFT32(a, bits) _rotl(a, bits) +#define PR_ROTATE_RIGHT32(a, bits) _rotr(a, bits) +#else +#define PR_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits)))) +#define PR_ROTATE_RIGHT32(a, bits) (((a) >> (bits)) | ((a) << (32 - (bits)))) +#endif + +PR_END_EXTERN_C +#endif /* prbit_h___ */ diff --git a/nsprpub/pr/include/prclist.h b/nsprpub/pr/include/prclist.h new file mode 100644 index 0000000000..a069176055 --- /dev/null +++ b/nsprpub/pr/include/prclist.h @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prclist_h___ +#define prclist_h___ + +#include "prtypes.h" + +typedef struct PRCListStr PRCList; + +/* +** Circular linked list +*/ +struct PRCListStr { + PRCList *next; + PRCList *prev; +}; + +/* +** Insert element "_e" into the list, before "_l". +*/ +#define PR_INSERT_BEFORE(_e,_l) \ + PR_BEGIN_MACRO \ + (_e)->next = (_l); \ + (_e)->prev = (_l)->prev; \ + (_l)->prev->next = (_e); \ + (_l)->prev = (_e); \ + PR_END_MACRO + +/* +** Insert element "_e" into the list, after "_l". +*/ +#define PR_INSERT_AFTER(_e,_l) \ + PR_BEGIN_MACRO \ + (_e)->next = (_l)->next; \ + (_e)->prev = (_l); \ + (_l)->next->prev = (_e); \ + (_l)->next = (_e); \ + PR_END_MACRO + +/* +** Return the element following element "_e" +*/ +#define PR_NEXT_LINK(_e) \ + ((_e)->next) +/* +** Return the element preceding element "_e" +*/ +#define PR_PREV_LINK(_e) \ + ((_e)->prev) + +/* +** Append an element "_e" to the end of the list "_l" +*/ +#define PR_APPEND_LINK(_e,_l) PR_INSERT_BEFORE(_e,_l) + +/* +** Insert an element "_e" at the head of the list "_l" +*/ +#define PR_INSERT_LINK(_e,_l) PR_INSERT_AFTER(_e,_l) + +/* Return the head/tail of the list */ +#define PR_LIST_HEAD(_l) (_l)->next +#define PR_LIST_TAIL(_l) (_l)->prev + +/* +** Remove the element "_e" from it's circular list. +*/ +#define PR_REMOVE_LINK(_e) \ + PR_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + PR_END_MACRO + +/* +** Remove the element "_e" from it's circular list. Also initializes the +** linkage. +*/ +#define PR_REMOVE_AND_INIT_LINK(_e) \ + PR_BEGIN_MACRO \ + (_e)->prev->next = (_e)->next; \ + (_e)->next->prev = (_e)->prev; \ + (_e)->next = (_e); \ + (_e)->prev = (_e); \ + PR_END_MACRO + +/* +** Return non-zero if the given circular list "_l" is empty, zero if the +** circular list is not empty +*/ +#define PR_CLIST_IS_EMPTY(_l) \ + ((_l)->next == (_l)) + +/* +** Initialize a circular list +*/ +#define PR_INIT_CLIST(_l) \ + PR_BEGIN_MACRO \ + (_l)->next = (_l); \ + (_l)->prev = (_l); \ + PR_END_MACRO + +#define PR_INIT_STATIC_CLIST(_l) \ + {(_l), (_l)} + +#endif /* prclist_h___ */ diff --git a/nsprpub/pr/include/prcmon.h b/nsprpub/pr/include/prcmon.h new file mode 100644 index 0000000000..69171138b2 --- /dev/null +++ b/nsprpub/pr/include/prcmon.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prcmon_h___ +#define prcmon_h___ + +/* +** Interface to cached monitors. Cached monitors use an address to find a +** given PR monitor. In this way a monitor can be associated with another +** object without preallocating a monitor for all objects. +** +** A hash table is used to quickly map addresses to individual monitors +** and the system automatically grows the hash table as needed. +** +** Cache monitors are about 5 times slower to use than uncached monitors. +*/ +#include "prmon.h" +#include "prinrval.h" + +PR_BEGIN_EXTERN_C + +/** +** Like PR_EnterMonitor except use the "address" to find a monitor in the +** monitor cache. If successful, returns the PRMonitor now associated +** with "address". Note that you must PR_CExitMonitor the address to +** release the monitor cache entry (otherwise the monitor cache will fill +** up). This call will return NULL if the monitor cache needs to be +** expanded and the system is out of memory. +*/ +NSPR_API(PRMonitor*) PR_CEnterMonitor(void *address); + +/* +** Like PR_ExitMonitor except use the "address" to find a monitor in the +** monitor cache. +*/ +NSPR_API(PRStatus) PR_CExitMonitor(void *address); + +/* +** Like PR_Wait except use the "address" to find a monitor in the +** monitor cache. +*/ +NSPR_API(PRStatus) PR_CWait(void *address, PRIntervalTime timeout); + +/* +** Like PR_Notify except use the "address" to find a monitor in the +** monitor cache. +*/ +NSPR_API(PRStatus) PR_CNotify(void *address); + +/* +** Like PR_NotifyAll except use the "address" to find a monitor in the +** monitor cache. +*/ +NSPR_API(PRStatus) PR_CNotifyAll(void *address); + +/* +** Set a callback to be invoked each time a monitor is recycled from the cache +** freelist, with the monitor's cache-key passed in address. +*/ +NSPR_API(void) PR_CSetOnMonitorRecycle(void (PR_CALLBACK *callback)(void *address)); + +PR_END_EXTERN_C + +#endif /* prcmon_h___ */ diff --git a/nsprpub/pr/include/prcountr.h b/nsprpub/pr/include/prcountr.h new file mode 100644 index 0000000000..1503d06d83 --- /dev/null +++ b/nsprpub/pr/include/prcountr.h @@ -0,0 +1,525 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prcountr_h___ +#define prcountr_h___ + +/*---------------------------------------------------------------------------- +** prcountr.h -- NSPR Instrumentation counters +** +** The NSPR Counter Feature provides a means to "count +** something." Counters can be dynamically defined, incremented, +** decremented, set, and deleted under application program +** control. +** +** The Counter Feature is intended to be used as instrumentation, +** not as operational data. If you need a counter for operational +** data, use native integral types. +** +** Counters are 32bit unsigned intergers. On overflow, a counter +** will wrap. No exception is recognized or reported. +** +** A counter can be dynamically created using a two level naming +** convention. A "handle" is returned when the counter is +** created. The counter can subsequently be addressed by its +** handle. An API is provided to get an existing counter's handle +** given the names with which it was originally created. +** Similarly, a counter's name can be retrieved given its handle. +** +** The counter naming convention is a two-level hierarchy. The +** QName is the higher level of the hierarchy; RName is the +** lower level. RNames can be thought of as existing within a +** QName. The same RName can exist within multiple QNames. QNames +** are unique. The NSPR Counter is not a near-zero overhead +** feature. Application designers should be aware of +** serialization issues when using the Counter API. Creating a +** counter locks a large asset, potentially causing a stall. This +** suggest that applications should create counters at component +** initialization, for example, and not create and destroy them +** willy-nilly. ... You have been warned. +** +** Incrementing and Adding to counters uses atomic operations. +** The performance of these operations will vary from platform +** to platform. On platforms where atomic operations are not +** supported the overhead may be substantial. +** +** When traversing the counter database with FindNext functions, +** the instantaneous values of any given counter is that at the +** moment of extraction. The state of the entire counter database +** may not be viewed as atomic. +** +** The counter interface may be disabled (No-Op'd) at compile +** time. When DEBUG is defined at compile time, the Counter +** Feature is compiled into NSPR and applications invoking it. +** When DEBUG is not defined, the counter macros compile to +** nothing. To force the Counter Feature to be compiled into an +** optimized build, define FORCE_NSPR_COUNTERS at compile time +** for both NSPR and the application intending to use it. +** +** Application designers should use the macro form of the Counter +** Feature methods to minimize performance impact in optimized +** builds. The macros normally compile to nothing on optimized +** builds. +** +** Application designers should be aware of the effects of +** debug and optimized build differences when using result of the +** Counter Feature macros in expressions. +** +** The Counter Feature is thread-safe and SMP safe. +** +** /lth. 09-Jun-1998. +*/ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** Opaque counter handle type. +** ... don't even think of looking in here. +** +*/ +typedef void * PRCounterHandle; + +#define PRCOUNTER_NAME_MAX 31 +#define PRCOUNTER_DESC_MAX 255 + + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_DEFINE_COUNTER() -- Define a PRCounterHandle +** +** DESCRIPTION: PR_DEFINE_COUNTER() is used to define a counter +** handle. +** +*/ +#define PR_DEFINE_COUNTER(name) PRCounterHandle name + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_INIT_COUNTER_HANDLE() -- Set the value of a PRCounterHandle +** +** DESCRIPTION: +** PR_INIT_COUNTER_HANDLE() sets the value of a PRCounterHandle +** to value. +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_INIT_COUNTER_HANDLE(handle,value)\ + (handle) = (PRCounterHandle)(value) +#else +#define PR_INIT_COUNTER_HANDLE(handle,value) +#endif + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_CreateCounter() -- Create a counter +** +** DESCRIPTION: PR_CreateCounter() creates a counter object and +** initializes it to zero. +** +** The macro form takes as its first argument the name of the +** PRCounterHandle to receive the handle returned from +** PR_CreateCounter(). +** +** INPUTS: +** qName: The QName for the counter object. The maximum length +** of qName is defined by PRCOUNTER_NAME_MAX +** +** rName: The RName for the counter object. The maximum length +** of qName is defined by PRCOUNTER_NAME_MAX +** +** descrioption: The description of the counter object. The +** maximum length of description is defined by +** PRCOUNTER_DESC_MAX. +** +** OUTPUTS: +** +** RETURNS: +** PRCounterHandle. +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_CREATE_COUNTER(handle,qName,rName,description)\ + (handle) = PR_CreateCounter((qName),(rName),(description)) +#else +#define PR_CREATE_COUNTER(handle,qName,rName,description) +#endif + +NSPR_API(PRCounterHandle) +PR_CreateCounter( + const char *qName, + const char *rName, + const char *description +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_DestroyCounter() -- Destroy a counter object. +** +** DESCRIPTION: PR_DestroyCounter() removes a counter and +** unregisters its handle from the counter database. +** +** INPUTS: +** handle: the PRCounterHandle of the counter to be destroyed. +** +** OUTPUTS: +** The counter is destroyed. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_DESTROY_COUNTER(handle) PR_DestroyCounter((handle)) +#else +#define PR_DESTROY_COUNTER(handle) +#endif + +NSPR_API(void) +PR_DestroyCounter( + PRCounterHandle handle +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetCounterHandleFromName() -- Retreive a +** counter's handle give its name. +** +** DESCRIPTION: PR_GetCounterHandleFromName() retreives a +** counter's handle from the counter database, given the name +** the counter was originally created with. +** +** INPUTS: +** qName: Counter's original QName. +** rName: Counter's original RName. +** +** OUTPUTS: +** +** RETURNS: +** PRCounterHandle or PRCounterError. +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_GET_COUNTER_HANDLE_FROM_NAME(handle,qName,rName)\ + (handle) = PR_GetCounterHandleFromName((qName),(rName)) +#else +#define PR_GET_COUNTER_HANDLE_FROM_NAME(handle,qName,rName) +#endif + +NSPR_API(PRCounterHandle) +PR_GetCounterHandleFromName( + const char *qName, + const char *rName +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetCounterNameFromHandle() -- Retreive a +** counter's name, given its handle. +** +** DESCRIPTION: PR_GetCounterNameFromHandle() retreives a +** counter's name given its handle. +** +** INPUTS: +** qName: Where to store a pointer to qName. +** rName: Where to store a pointer to rName. +** description: Where to store a pointer to description. +** +** OUTPUTS: Pointers to the Counter Feature's copies of the names +** used when the counters were created. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_GET_COUNTER_NAME_FROM_HANDLE(handle,qName,rName,description)\ + PR_GetCounterNameFromHandle((handle),(qName),(rName),(description)) +#else +#define PR_GET_COUNTER_NAME_FROM_HANDLE(handle,qName,rName,description ) +#endif + +NSPR_API(void) +PR_GetCounterNameFromHandle( + PRCounterHandle handle, + const char **qName, + const char **rName, + const char **description +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_IncrementCounter() -- Add one to the referenced +** counter. +** +** DESCRIPTION: Add one to the referenced counter. +** +** INPUTS: +** handle: The PRCounterHandle of the counter to be incremented +** +** OUTPUTS: The counter is incrementd. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_INCREMENT_COUNTER(handle) PR_IncrementCounter(handle) +#else +#define PR_INCREMENT_COUNTER(handle) +#endif + +NSPR_API(void) +PR_IncrementCounter( + PRCounterHandle handle +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_DecrementCounter() -- Subtract one from the +** referenced counter +** +** DESCRIPTION: Subtract one from the referenced counter. +** +** INPUTS: +** handle: The PRCounterHandle of the coutner to be +** decremented. +** +** OUTPUTS: the counter is decremented. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_DECREMENT_COUNTER(handle) PR_DecrementCounter(handle) +#else +#define PR_DECREMENT_COUNTER(handle) +#endif + +NSPR_API(void) +PR_DecrementCounter( + PRCounterHandle handle +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_AddToCounter() -- Add a value to a counter. +** +** DESCRIPTION: Add value to the counter referenced by handle. +** +** INPUTS: +** handle: the PRCounterHandle of the counter to be added to. +** +** value: the value to be added to the counter. +** +** OUTPUTS: new value for counter. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_ADD_TO_COUNTER(handle,value)\ + PR_AddToCounter((handle),(value)) +#else +#define PR_ADD_TO_COUNTER(handle,value) +#endif + +NSPR_API(void) +PR_AddToCounter( + PRCounterHandle handle, + PRUint32 value +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_SubtractFromCounter() -- A value is subtracted +** from a counter. +** +** DESCRIPTION: +** Subtract a value from a counter. +** +** INPUTS: +** handle: the PRCounterHandle of the counter to be subtracted +** from. +** +** value: the value to be subtracted from the counter. +** +** OUTPUTS: new value for counter +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_SUBTRACT_FROM_COUNTER(handle,value)\ + PR_SubtractFromCounter((handle),(value)) +#else +#define PR_SUBTRACT_FROM_COUNTER(handle,value) +#endif + +NSPR_API(void) +PR_SubtractFromCounter( + PRCounterHandle handle, + PRUint32 value +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetCounter() -- Retreive the value of a counter +** +** DESCRIPTION: +** Retreive the value of a counter. +** +** INPUTS: +** handle: the PR_CounterHandle of the counter to be retreived +** +** OUTPUTS: +** +** RETURNS: The value of the referenced counter +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_GET_COUNTER(counter,handle)\ + (counter) = PR_GetCounter((handle)) +#else +#define PR_GET_COUNTER(counter,handle) 0 +#endif + +NSPR_API(PRUint32) +PR_GetCounter( + PRCounterHandle handle +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_SetCounter() -- Replace the content of counter +** with value. +** +** DESCRIPTION: The contents of the referenced counter are +** replaced by value. +** +** INPUTS: +** handle: the PRCounterHandle of the counter whose contents +** are to be replaced. +** +** value: the new value of the counter. +** +** OUTPUTS: +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_SET_COUNTER(handle,value) PR_SetCounter((handle),(value)) +#else +#define PR_SET_COUNTER(handle,value) +#endif + +NSPR_API(void) +PR_SetCounter( + PRCounterHandle handle, + PRUint32 value +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_FindNextCounterQname() -- Retreive the next QName counter +** handle iterator +** +** DESCRIPTION: +** PR_FindNextCounterQname() retreives the first or next Qname +** the counter data base, depending on the value of handle. When +** handle is NULL, the function attempts to retreive the first +** QName handle in the database. When handle is a handle previosly +** retreived QName handle, then the function attempts to retreive +** the next QName handle. +** +** INPUTS: +** handle: PRCounterHandle or NULL. +** +** OUTPUTS: returned +** +** RETURNS: PRCounterHandle or NULL when no more QName counter +** handles are present. +** +** RESTRICTIONS: +** A concurrent PR_CreateCounter() or PR_DestroyCounter() may +** cause unpredictable results. +** +** A PRCounterHandle returned from this function may only be used +** in another PR_FindNextCounterQname() function call; other +** operations may cause unpredictable results. +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_FIND_NEXT_COUNTER_QNAME(next,handle)\ + (next) = PR_FindNextCounterQname((handle)) +#else +#define PR_FIND_NEXT_COUNTER_QNAME(next,handle) NULL +#endif + +NSPR_API(PRCounterHandle) +PR_FindNextCounterQname( + PRCounterHandle handle +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_FindNextCounterRname() -- Retreive the next RName counter +** handle iterator +** +** DESCRIPTION: +** PR_FindNextCounterRname() retreives the first or next RNname +** handle from the counter data base, depending on the +** value of handle. When handle is NULL, the function attempts to +** retreive the first RName handle in the database. When handle is +** a handle previosly retreived RName handle, then the function +** attempts to retreive the next RName handle. +** +** INPUTS: +** handle: PRCounterHandle or NULL. +** qhandle: PRCounterHandle of a previously aquired via +** PR_FIND_NEXT_QNAME_HANDLE() +** +** OUTPUTS: returned +** +** RETURNS: PRCounterHandle or NULL when no more RName counter +** handles are present. +** +** RESTRICTIONS: +** A concurrent PR_CreateCounter() or PR_DestroyCounter() may +** cause unpredictable results. +** +** A PRCounterHandle returned from this function may only be used +** in another PR_FindNextCounterRname() function call; other +** operations may cause unpredictable results. +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_COUNTERS) +#define PR_FIND_NEXT_COUNTER_RNAME(next,rhandle,qhandle)\ + (next) = PR_FindNextCounterRname((rhandle),(qhandle)) +#else +#define PR_FIND_NEXT_COUNTER_RNAME(next,rhandle,qhandle) +#endif + +NSPR_API(PRCounterHandle) +PR_FindNextCounterRname( + PRCounterHandle rhandle, + PRCounterHandle qhandle +); + +PR_END_EXTERN_C + +#endif /* prcountr_h___ */ diff --git a/nsprpub/pr/include/prcvar.h b/nsprpub/pr/include/prcvar.h new file mode 100644 index 0000000000..413a3646ac --- /dev/null +++ b/nsprpub/pr/include/prcvar.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prcvar_h___ +#define prcvar_h___ + +#include "prlock.h" +#include "prinrval.h" + +PR_BEGIN_EXTERN_C + +typedef struct PRCondVar PRCondVar; + +/* +** Create a new condition variable. +** +** "lock" is the lock used to protect the condition variable. +** +** Condition variables are synchronization objects that threads can use +** to wait for some condition to occur. +** +** This may fail if memory is tight or if some operating system resource +** is low. In such cases, a NULL will be returned. +*/ +NSPR_API(PRCondVar*) PR_NewCondVar(PRLock *lock); + +/* +** Destroy a condition variable. There must be no thread +** waiting on the condvar. The caller is responsible for guaranteeing +** that the condvar is no longer in use. +** +*/ +NSPR_API(void) PR_DestroyCondVar(PRCondVar *cvar); + +/* +** The thread that waits on a condition is blocked in a "waiting on +** condition" state until another thread notifies the condition or a +** caller specified amount of time expires. The lock associated with +** the condition variable will be released, which must have be held +** prior to the call to wait. +** +** Logically a notified thread is moved from the "waiting on condition" +** state and made "ready." When scheduled, it will attempt to reacquire +** the lock that it held when wait was called. +** +** The timeout has two well known values, PR_INTERVAL_NO_TIMEOUT and +** PR_INTERVAL_NO_WAIT. The former value requires that a condition be +** notified (or the thread interrupted) before it will resume from the +** wait. If the timeout has a value of PR_INTERVAL_NO_WAIT, the effect +** is to release the lock, possibly causing a rescheduling within the +** runtime, then immediately attempting to reacquire the lock and resume. +** +** Any other value for timeout will cause the thread to be rescheduled +** either due to explicit notification or an expired interval. The latter +** must be determined by treating time as one part of the monitored data +** being protected by the lock and tested explicitly for an expired +** interval. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable or the thread was interrupted (PR_Interrupt()). +** The particular reason can be extracted with PR_GetError(). +*/ +NSPR_API(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout); + +/* +** Notify ONE thread that is currently waiting on 'cvar'. Which thread is +** dependent on the implementation of the runtime. Common sense would dictate +** that all threads waiting on a single condition have identical semantics, +** therefore which one gets notified is not significant. +** +** The calling thead must hold the lock that protects the condition, as +** well as the invariants that are tightly bound to the condition, when +** notify is called. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable. +*/ +NSPR_API(PRStatus) PR_NotifyCondVar(PRCondVar *cvar); + +/* +** Notify all of the threads waiting on the condition variable. The order +** that the threads are notified is indeterminant. The lock that protects +** the condition must be held. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable. +*/ +NSPR_API(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar); + +PR_END_EXTERN_C + +#endif /* prcvar_h___ */ diff --git a/nsprpub/pr/include/prdtoa.h b/nsprpub/pr/include/prdtoa.h new file mode 100644 index 0000000000..312a9d60ad --- /dev/null +++ b/nsprpub/pr/include/prdtoa.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prdtoa_h___ +#define prdtoa_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** PR_strtod() returns as a double-precision floating-point number +** the value represented by the character string pointed to by +** s00. The string is scanned up to the first unrecognized +** character. +**a +** If the value of se is not (char **)NULL, a pointer to +** the character terminating the scan is returned in the location pointed +** to by se. If no number can be formed, se is set to s00, and +** zero is returned. +*/ +NSPR_API(PRFloat64) +PR_strtod(const char *s00, char **se); + +/* +** PR_cnvtf() +** conversion routines for floating point +** prcsn - number of digits of precision to generate floating +** point value. +*/ +NSPR_API(void) PR_cnvtf(char *buf, PRIntn bufsz, PRIntn prcsn, PRFloat64 fval); + +/* +** PR_dtoa() converts double to a string. +** +** ARGUMENTS: +** If rve is not null, *rve is set to point to the end of the return value. +** If d is +-Infinity or NaN, then *decpt is set to 9999. +** +** mode: +** 0 ==> shortest string that yields d when read in +** and rounded to nearest. +*/ +NSPR_API(PRStatus) PR_dtoa(PRFloat64 d, PRIntn mode, PRIntn ndigits, + PRIntn *decpt, PRIntn *sign, char **rve, char *buf, PRSize bufsize); + +PR_END_EXTERN_C + +#endif /* prdtoa_h___ */ diff --git a/nsprpub/pr/include/prenv.h b/nsprpub/pr/include/prenv.h new file mode 100644 index 0000000000..550f05c936 --- /dev/null +++ b/nsprpub/pr/include/prenv.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prenv_h___ +#define prenv_h___ + +#include "prtypes.h" + +/*******************************************************************************/ +/*******************************************************************************/ +/****************** THESE FUNCTIONS MAY NOT BE THREAD SAFE *********************/ +/*******************************************************************************/ +/*******************************************************************************/ + +PR_BEGIN_EXTERN_C + +/* +** PR_GetEnv() -- Retrieve value of environment variable +** +** Description: +** PR_GetEnv() is modeled on Unix getenv(). +** +** +** Inputs: +** var -- The name of the environment variable +** +** Returns: +** The value of the environment variable 'var' or NULL if +** the variable is undefined. +** +** Restrictions: +** You'd think that a POSIX getenv(), putenv() would be +** consistently implemented everywhere. Surprise! It is not. On +** some platforms, a putenv() where the argument is of +** the form "name" causes the named environment variable to +** be un-set; that is: a subsequent getenv() returns NULL. On +** other platforms, the putenv() fails, on others, it is a +** no-op. Similarly, a putenv() where the argument is of the +** form "name=" causes the named environment variable to be +** un-set; a subsequent call to getenv() returns NULL. On +** other platforms, a subsequent call to getenv() returns a +** pointer to a null-string (a byte of zero). +** +** PR_GetEnv(), PR_SetEnv() provide a consistent behavior +** across all supported platforms. There are, however, some +** restrictions and some practices you must use to achieve +** consistent results everywhere. +** +** When manipulating the environment there is no way to un-set +** an environment variable across all platforms. We suggest +** you interpret the return of a pointer to null-string to +** mean the same as a return of NULL from PR_GetEnv(). +** +** A call to PR_SetEnv() where the parameter is of the form +** "name" will return PR_FAILURE; the environment remains +** unchanged. A call to PR_SetEnv() where the parameter is +** of the form "name=" may un-set the envrionment variable on +** some platforms; on others it may set the value of the +** environment variable to the null-string. +** +** For example, to test for NULL return or return of the +** null-string from PR_GetEnv(), use the following code +** fragment: +** +** char *val = PR_GetEnv("foo"); +** if ((NULL == val) || ('\0' == *val)) { +** ... interpret this as un-set ... +** } +** +** The caller must ensure that the string passed +** to PR_SetEnv() is persistent. That is: The string should +** not be on the stack, where it can be overwritten +** on return from the function calling PR_SetEnv(). +** Similarly, the string passed to PR_SetEnv() must not be +** overwritten by other actions of the process. ... Some +** platforms use the string by reference rather than copying +** it into the environment space. ... You have been warned! +** +** Use of platform-native functions that manipulate the +** environment (getenv(), putenv(), +** SetEnvironmentVariable(), etc.) must not be used with +** NSPR's similar functions. The platform-native functions +** may not be thread safe and/or may operate on different +** conceptual environment space than that operated upon by +** NSPR's functions or other environment manipulating +** functions on the same platform. (!) +** +*/ +NSPR_API(char*) PR_GetEnv(const char *var); + +/* +** PR_GetEnvSecure() -- get a security-sensitive environment variable +** +** Description: +** +** PR_GetEnvSecure() is similar to PR_GetEnv(), but it returns NULL if +** the program was run with elevated privilege (e.g., setuid or setgid +** on Unix). This can be used for cases like log file paths which +** could otherwise be used for privilege escalation. Note that some +** platforms may have platform-specific privilege elevation mechanisms +** not recognized by this function; see the implementation for details. +*/ +NSPR_API(char*) PR_GetEnvSecure(const char *var); + +/* +** PR_SetEnv() -- set, unset or change an environment variable +** +** Description: +** PR_SetEnv() is modeled on the Unix putenv() function. +** +** Inputs: +** string -- pointer to a caller supplied +** constant, persistent string of the form name=value. Where +** name is the name of the environment variable to be set or +** changed; value is the value assigned to the variable. +** +** Returns: +** PRStatus. +** +** Restrictions: +** See the Restrictions documented in the description of +** PR_GetEnv() in this header file. +** +** +*/ +NSPR_API(PRStatus) PR_SetEnv(const char *string); + +/* +** PR_DuplicateEnvironment() -- Obtain a copy of the environment. +** +** Description: +** PR_DuplicateEnvironment() copies the environment so that it can be +** modified without changing the current process's environment, and +** then passed to interfaces such as POSIX execve(). In particular, +** this avoids needing to allocate memory or take locks in the child +** after a fork(); neither of these is allowed by POSIX after a +** multithreaded process calls fork(), and PR_SetEnv does both. +** +** Inputs: +** none +** +** Returns: +** A pointer to a null-terminated array of null-terminated strings, +** like the traditional global variable "environ". The array and +** the strings are allocated with PR_Malloc(), and it is the +** caller's responsibility to free them. +** +** In case of memory allocation failure, or if the operating system +** doesn't support reading the entire environment through the global +** variable "environ" or similar, returns NULL instead. +** +** Restrictions: +** Similarly to PR_GetEnv(), this function may not interoperate as +** expected with the operating system's native environment accessors. +*/ +NSPR_API(char **) PR_DuplicateEnvironment(void); + +PR_END_EXTERN_C + +#endif /* prenv_h___ */ diff --git a/nsprpub/pr/include/prerr.h b/nsprpub/pr/include/prerr.h new file mode 100644 index 0000000000..8512329e19 --- /dev/null +++ b/nsprpub/pr/include/prerr.h @@ -0,0 +1,249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prerr_h___ +#define prerr_h___ + +/* + * + * prerr.h + * This file is automatically generated; please do not edit it. + */ + +/* Memory allocation attempt failed */ +#define PR_OUT_OF_MEMORY_ERROR (-6000L) + +/* Invalid file descriptor */ +#define PR_BAD_DESCRIPTOR_ERROR (-5999L) + +/* The operation would have blocked */ +#define PR_WOULD_BLOCK_ERROR (-5998L) + +/* Invalid memory address argument */ +#define PR_ACCESS_FAULT_ERROR (-5997L) + +/* Invalid function for file type */ +#define PR_INVALID_METHOD_ERROR (-5996L) + +/* Invalid memory address argument */ +#define PR_ILLEGAL_ACCESS_ERROR (-5995L) + +/* Some unknown error has occurred */ +#define PR_UNKNOWN_ERROR (-5994L) + +/* Operation interrupted by another thread */ +#define PR_PENDING_INTERRUPT_ERROR (-5993L) + +/* function not implemented */ +#define PR_NOT_IMPLEMENTED_ERROR (-5992L) + +/* I/O function error */ +#define PR_IO_ERROR (-5991L) + +/* I/O operation timed out */ +#define PR_IO_TIMEOUT_ERROR (-5990L) + +/* I/O operation on busy file descriptor */ +#define PR_IO_PENDING_ERROR (-5989L) + +/* The directory could not be opened */ +#define PR_DIRECTORY_OPEN_ERROR (-5988L) + +/* Invalid function argument */ +#define PR_INVALID_ARGUMENT_ERROR (-5987L) + +/* Network address not available (in use?) */ +#define PR_ADDRESS_NOT_AVAILABLE_ERROR (-5986L) + +/* Network address type not supported */ +#define PR_ADDRESS_NOT_SUPPORTED_ERROR (-5985L) + +/* Already connected */ +#define PR_IS_CONNECTED_ERROR (-5984L) + +/* Network address is invalid */ +#define PR_BAD_ADDRESS_ERROR (-5983L) + +/* Local Network address is in use */ +#define PR_ADDRESS_IN_USE_ERROR (-5982L) + +/* Connection refused by peer */ +#define PR_CONNECT_REFUSED_ERROR (-5981L) + +/* Network address is presently unreachable */ +#define PR_NETWORK_UNREACHABLE_ERROR (-5980L) + +/* Connection attempt timed out */ +#define PR_CONNECT_TIMEOUT_ERROR (-5979L) + +/* Network file descriptor is not connected */ +#define PR_NOT_CONNECTED_ERROR (-5978L) + +/* Failure to load dynamic library */ +#define PR_LOAD_LIBRARY_ERROR (-5977L) + +/* Failure to unload dynamic library */ +#define PR_UNLOAD_LIBRARY_ERROR (-5976L) + +/* Symbol not found in any of the loaded dynamic libraries */ +#define PR_FIND_SYMBOL_ERROR (-5975L) + +/* Insufficient system resources */ +#define PR_INSUFFICIENT_RESOURCES_ERROR (-5974L) + +/* A directory lookup on a network address has failed */ +#define PR_DIRECTORY_LOOKUP_ERROR (-5973L) + +/* Attempt to access a TPD key that is out of range */ +#define PR_TPD_RANGE_ERROR (-5972L) + +/* Process open FD table is full */ +#define PR_PROC_DESC_TABLE_FULL_ERROR (-5971L) + +/* System open FD table is full */ +#define PR_SYS_DESC_TABLE_FULL_ERROR (-5970L) + +/* Network operation attempted on non-network file descriptor */ +#define PR_NOT_SOCKET_ERROR (-5969L) + +/* TCP-specific function attempted on a non-TCP file descriptor */ +#define PR_NOT_TCP_SOCKET_ERROR (-5968L) + +/* TCP file descriptor is already bound */ +#define PR_SOCKET_ADDRESS_IS_BOUND_ERROR (-5967L) + +/* Access Denied */ +#define PR_NO_ACCESS_RIGHTS_ERROR (-5966L) + +/* The requested operation is not supported by the platform */ +#define PR_OPERATION_NOT_SUPPORTED_ERROR (-5965L) + +/* The host operating system does not support the protocol requested */ +#define PR_PROTOCOL_NOT_SUPPORTED_ERROR (-5964L) + +/* Access to the remote file has been severed */ +#define PR_REMOTE_FILE_ERROR (-5963L) + +/* The value requested is too large to be stored in the data buffer provided */ +#define PR_BUFFER_OVERFLOW_ERROR (-5962L) + +/* TCP connection reset by peer */ +#define PR_CONNECT_RESET_ERROR (-5961L) + +/* Unused */ +#define PR_RANGE_ERROR (-5960L) + +/* The operation would have deadlocked */ +#define PR_DEADLOCK_ERROR (-5959L) + +/* The file is already locked */ +#define PR_FILE_IS_LOCKED_ERROR (-5958L) + +/* Write would result in file larger than the system allows */ +#define PR_FILE_TOO_BIG_ERROR (-5957L) + +/* The device for storing the file is full */ +#define PR_NO_DEVICE_SPACE_ERROR (-5956L) + +/* Unused */ +#define PR_PIPE_ERROR (-5955L) + +/* Unused */ +#define PR_NO_SEEK_DEVICE_ERROR (-5954L) + +/* Cannot perform a normal file operation on a directory */ +#define PR_IS_DIRECTORY_ERROR (-5953L) + +/* Symbolic link loop */ +#define PR_LOOP_ERROR (-5952L) + +/* File name is too long */ +#define PR_NAME_TOO_LONG_ERROR (-5951L) + +/* File not found */ +#define PR_FILE_NOT_FOUND_ERROR (-5950L) + +/* Cannot perform directory operation on a normal file */ +#define PR_NOT_DIRECTORY_ERROR (-5949L) + +/* Cannot write to a read-only file system */ +#define PR_READ_ONLY_FILESYSTEM_ERROR (-5948L) + +/* Cannot delete a directory that is not empty */ +#define PR_DIRECTORY_NOT_EMPTY_ERROR (-5947L) + +/* Cannot delete or rename a file object while the file system is busy */ +#define PR_FILESYSTEM_MOUNTED_ERROR (-5946L) + +/* Cannot rename a file to a file system on another device */ +#define PR_NOT_SAME_DEVICE_ERROR (-5945L) + +/* The directory object in the file system is corrupted */ +#define PR_DIRECTORY_CORRUPTED_ERROR (-5944L) + +/* Cannot create or rename a filename that already exists */ +#define PR_FILE_EXISTS_ERROR (-5943L) + +/* Directory is full. No additional filenames may be added */ +#define PR_MAX_DIRECTORY_ENTRIES_ERROR (-5942L) + +/* The required device was in an invalid state */ +#define PR_INVALID_DEVICE_STATE_ERROR (-5941L) + +/* The device is locked */ +#define PR_DEVICE_IS_LOCKED_ERROR (-5940L) + +/* No more entries in the directory */ +#define PR_NO_MORE_FILES_ERROR (-5939L) + +/* Encountered end of file */ +#define PR_END_OF_FILE_ERROR (-5938L) + +/* Seek error */ +#define PR_FILE_SEEK_ERROR (-5937L) + +/* The file is busy */ +#define PR_FILE_IS_BUSY_ERROR (-5936L) + +/* The I/O operation was aborted */ +#define PR_OPERATION_ABORTED_ERROR (-5935L) + +/* Operation is still in progress (probably a non-blocking connect) */ +#define PR_IN_PROGRESS_ERROR (-5934L) + +/* Operation has already been initiated (probably a non-blocking connect) */ +#define PR_ALREADY_INITIATED_ERROR (-5933L) + +/* The wait group is empty */ +#define PR_GROUP_EMPTY_ERROR (-5932L) + +/* Object state improper for request */ +#define PR_INVALID_STATE_ERROR (-5931L) + +/* Network is down */ +#define PR_NETWORK_DOWN_ERROR (-5930L) + +/* Socket shutdown */ +#define PR_SOCKET_SHUTDOWN_ERROR (-5929L) + +/* Connection aborted */ +#define PR_CONNECT_ABORTED_ERROR (-5928L) + +/* Host is unreachable */ +#define PR_HOST_UNREACHABLE_ERROR (-5927L) + +/* The library is not loaded */ +#define PR_LIBRARY_NOT_LOADED_ERROR (-5926L) + +/* The one-time function was previously called and failed. Its error code is no longer available */ +#define PR_CALL_ONCE_ERROR (-5925L) + +/* Placeholder for the end of the list */ +#define PR_MAX_ERROR (-5924L) + +extern void nspr_InitializePRErrorTable(void); +#define ERROR_TABLE_BASE_nspr (-6000L) + +#endif /* prerr_h___ */ diff --git a/nsprpub/pr/include/prerror.h b/nsprpub/pr/include/prerror.h new file mode 100644 index 0000000000..f445aa90aa --- /dev/null +++ b/nsprpub/pr/include/prerror.h @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prerror_h___ +#define prerror_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +typedef PRInt32 PRErrorCode; + +#define PR_NSPR_ERROR_BASE -6000 + +#include "prerr.h" + +/* +** Set error will preserve an error condition within a thread context. +** The values stored are the NSPR (platform independent) translation of +** the error. Also, if available, the platform specific oserror is stored. +** If there is no appropriate OS error number, a zero my be supplied. +*/ +NSPR_API(void) PR_SetError(PRErrorCode errorCode, PRInt32 oserr); + +/* +** The text value specified may be NULL. If it is not NULL and the text length +** is zero, the string is assumed to be a null terminated C string. Otherwise +** the text is assumed to be the length specified and possibly include NULL +** characters (e.g., a multi-national string). +** +** The text will be copied into to thread structure and remain there +** until the next call to PR_SetError. +*/ +NSPR_API(void) PR_SetErrorText( + PRIntn textLength, const char *text); + +/* +** Return the current threads last set error code. +*/ +NSPR_API(PRErrorCode) PR_GetError(void); + +/* +** Return the current threads last set os error code. This is used for +** machine specific code that desires the underlying os error. +*/ +NSPR_API(PRInt32) PR_GetOSError(void); + +/* +** Get the length of the error text. If a zero is returned, then there +** is no text. Otherwise, the value returned is sufficient to contain +** the error text currently available. +*/ +NSPR_API(PRInt32) PR_GetErrorTextLength(void); + +/* +** Copy the current threads current error text. Then actual number of bytes +** copied is returned as the result. If the result is zero, the 'text' area +** is unaffected. +*/ +NSPR_API(PRInt32) PR_GetErrorText(char *text); + + +/* +Copyright (C) 1987, 1988 Student Information Processing Board of the +Massachusetts Institute of Technology. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be +used in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. M.I.T. and the M.I.T. S.I.P.B. +make no representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied warranty. +*/ + + +/* + * NOTE: + * The interfaces for error-code-translation described in the rest of + * this file are preliminary in the 3.1 release of nspr and are subject + * to change in future releases. + */ + +/* +** Description: Localizable error code to string function. +** +** +** NSPR provides a mechanism for converting an error code to a +** descriptive string, in a caller-specified language. +** +** Error codes themselves are 32 bit (signed) integers. Typically, +** the high order 24 bits are an identifier of which error table the +** error code is from, and the low order 8 bits are a sequential error +** number within the table. NSPR supports error tables whose first +** error code is not a multiple of 256, such error code assignments +** should be avoided when possible. +** +** Error table 0 is defined to match the UNIX system call error table +** (sys_errlist); this allows errno values to be used directly in the +** library. Other error table numbers are typically formed by +** compacting together the first four characters of the error table +** name. The mapping between characters in the name and numeric +** values in the error code are defined in a system-independent +** fashion, so that two systems that can pass integral values between +** them can reliably pass error codes without loss of meaning; this +** should work even if the character sets used are not the +** same. (However, if this is to be done, error table 0 should be +** avoided, since the local system call error tables may differ.) +** +** Libraries defining error codes need only provide a table mapping +** error code numbers to names and default English descriptions, +** calling a routine to install the table, making it ``known'' to NSPR +** library. Once installed, a table may not be removed. Any error +** code the library generates can be converted to the corresponding +** error message. There is also a default format for error codes +** accidentally returned before making the table known, which is of +** the form "unknown code foo 32", where "foo" would be the name of +** the table. +** +** Normally, the error code conversion routine only supports the +** languages "i-default" and "en", returning the error-table-provided +** English description for both languages. The application may +** provide a localization plugin, allowing support for additional +** languages. +** +**/ + +/**********************************************************************/ +/************************* TYPES AND CONSTANTS ************************/ +/**********************************************************************/ + +/* + * PRLanguageCode -- + * + * NSPR represents a language code as a non-negative integer. + * Languages 0 is always "i-default" the language you get without + * explicit negotiation. Language 1 is always "en", English + * which has been explicitly negotiated. Additional language + * codes are defined by an application-provided localization plugin. + */ +typedef PRUint32 PRLanguageCode; +#define PR_LANGUAGE_I_DEFAULT 0 /* i-default, the default language */ +#define PR_LANGUAGE_EN 1 /* English, explicitly negotiated */ + +/* + * struct PRErrorMessage -- + * + * An error message in an error table. + */ +struct PRErrorMessage { + const char * name; /* Macro name for error */ + const char * en_text; /* Default English text */ +}; + +/* + * struct PRErrorTable -- + * + * An error table, provided by a library. + */ +struct PRErrorTable { + const struct PRErrorMessage * msgs; /* Array of error information */ + const char *name; /* Name of error table source */ + PRErrorCode base; /* Error code for first error in table */ + int n_msgs; /* Number of codes in table */ +}; + +/* + * struct PRErrorCallbackPrivate -- + * + * A private structure for the localization plugin + */ +struct PRErrorCallbackPrivate; + +/* + * struct PRErrorCallbackTablePrivate -- + * + * A data structure under which the localization plugin may store information, + * associated with an error table, that is private to itself. + */ +struct PRErrorCallbackTablePrivate; + +/* + * PRErrorCallbackLookupFn -- + * + * A function of PRErrorCallbackLookupFn type is a localization + * plugin callback which converts an error code into a description + * in the requested language. The callback is provided the + * appropriate error table, private data for the plugin and the table. + * The callback returns the appropriate UTF-8 encoded description, or NULL + * if no description can be found. + */ +typedef const char * +PRErrorCallbackLookupFn(PRErrorCode code, PRLanguageCode language, + const struct PRErrorTable *table, + struct PRErrorCallbackPrivate *cb_private, + struct PRErrorCallbackTablePrivate *table_private); + +/* + * PRErrorCallbackNewTableFn -- + * + * A function PRErrorCallbackNewTableFn type is a localization plugin + * callback which is called once with each error table registered + * with NSPR. The callback is provided with the error table and + * the plugin's private structure. The callback returns any table private + * data it wishes to associate with the error table. Does not need to be thread + * safe. + */ +typedef struct PRErrorCallbackTablePrivate * +PRErrorCallbackNewTableFn(const struct PRErrorTable *table, + struct PRErrorCallbackPrivate *cb_private); + +/**********************************************************************/ +/****************************** FUNCTIONS *****************************/ +/**********************************************************************/ + +/*********************************************************************** +** FUNCTION: PR_ErrorToString +** DESCRIPTION: +** Returns the UTF-8 message for an error code in +** the requested language. May return the message +** in the default language if a translation in the requested +** language is not available. The returned string is +** valid for the duration of the process. Never returns NULL. +** +***********************************************************************/ +NSPR_API(const char *) PR_ErrorToString(PRErrorCode code, + PRLanguageCode language); + + +/*********************************************************************** +** FUNCTION: PR_ErrorToName +** DESCRIPTION: +** Returns the macro name for an error code, or NULL +** if the error code is not known. The returned string is +** valid for the duration of the process. +** +** Does not work for error table 0, the system error codes. +** +***********************************************************************/ +NSPR_API(const char *) PR_ErrorToName(PRErrorCode code); + + +/*********************************************************************** +** FUNCTION: PR_ErrorLanguages +** DESCRIPTION: +** Returns the RFC 1766 language tags for the language +** codes PR_ErrorToString() supports. The returned array is valid +** for the duration of the process. Never returns NULL. The first +** item in the returned array is the language tag for PRLanguageCode 0, +** the second is for PRLanguageCode 1, and so on. The array is terminated +** with a null pointer. +** +***********************************************************************/ +NSPR_API(const char * const *) PR_ErrorLanguages(void); + + +/*********************************************************************** +** FUNCTION: PR_ErrorInstallTable +** DESCRIPTION: +** Registers an error table with NSPR. Must be done exactly once per +** table. Memory pointed to by `table' must remain valid for the life +** of the process. +** +** NOT THREAD SAFE! +** +***********************************************************************/ +NSPR_API(PRErrorCode) PR_ErrorInstallTable(const struct PRErrorTable *table); + + +/*********************************************************************** +** FUNCTION: PR_ErrorInstallCallback +** DESCRIPTION: +** Registers an error localization plugin with NSPR. May be called +** at most one time. `languages' contains the language codes supported +** by this plugin. Languages 0 and 1 must be "i-default" and "en" +** respectively. `lookup' and `newtable' contain pointers to +** the plugin callback functions. `cb_private' contains any information +** private to the plugin functions. +** +** NOT THREAD SAFE! +** +***********************************************************************/ +NSPR_API(void) PR_ErrorInstallCallback(const char * const * languages, + PRErrorCallbackLookupFn *lookup, + PRErrorCallbackNewTableFn *newtable, + struct PRErrorCallbackPrivate *cb_private); + +PR_END_EXTERN_C + +#endif /* prerror_h___ */ diff --git a/nsprpub/pr/include/prinet.h b/nsprpub/pr/include/prinet.h new file mode 100644 index 0000000000..b743ef63ef --- /dev/null +++ b/nsprpub/pr/include/prinet.h @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: prinet.h + * Description: + * Header file used to find the system header files for socket support[1]. + * This file serves the following purposes: + * - A cross-platform, "get-everything" socket header file. On + * Unix, socket support is scattered in several header files, + * while Windows has a "get-everything" socket header file[2]. + * - NSPR needs the following macro definitions and function + * prototype declarations from these header files: + * AF_INET + * INADDR_ANY, INADDR_LOOPBACK, INADDR_BROADCAST + * ntohl(), ntohs(), htonl(), ntons(). + * NSPR does not define its own versions of these macros and + * functions. It simply uses the native versions, which have + * the same names on all supported platforms. + * This file is intended to be included by NSPR public header + * files, such as prio.h. One should not include this file directly. + * + * Notes: + * 1. This file should have been an internal header. Please do not + * depend on it to pull in the system header files you need. + * 2. WARNING: This file is no longer cross-platform as it is a no-op + * for WIN32! See the comment in the WIN32 section for details. + */ + +#ifndef prinet_h__ +#define prinet_h__ + +#if defined(XP_UNIX) || defined(XP_OS2) +#include <sys/types.h> +#include <sys/socket.h> /* AF_INET */ +#include <netinet/in.h> /* INADDR_ANY, ..., ntohl(), ... */ +#ifdef XP_OS2 +#include <sys/ioctl.h> +#endif +#ifdef XP_UNIX +#ifdef AIX +/* + * On AIX 4.3, the header <arpa/inet.h> refers to struct + * ether_addr and struct sockaddr_dl that are not declared. + * The following struct declarations eliminate the compiler + * warnings. + */ +struct ether_addr; +struct sockaddr_dl; +#endif /* AIX */ +#include <arpa/inet.h> +#endif /* XP_UNIX */ +#include <netdb.h> + +#if defined(BSDI) || defined(QNX) +#include <rpc/types.h> /* the only place that defines INADDR_LOOPBACK */ +#endif + +/* + * OS/2 hack. For some reason INADDR_LOOPBACK is not defined in the + * socket headers. + */ +#if defined(OS2) && !defined(INADDR_LOOPBACK) +#define INADDR_LOOPBACK 0x7f000001 +#endif + +/* + * Prototypes of ntohl() etc. are declared in <machine/endian.h> + * on these platforms. + */ +#if defined(BSDI) +#include <machine/endian.h> +#endif + +/* On Android, ntohl() etc. are declared in <sys/endian.h>. */ +#ifdef __ANDROID__ +#include <sys/endian.h> +#endif + +#elif defined(WIN32) + +/* + * Do not include any system header files. + * + * Originally we were including <windows.h>. It slowed down the + * compilation of files that included NSPR headers, so we removed + * the <windows.h> inclusion at customer's request, which created + * an unfortunate inconsistency with other platforms. + */ + +#else + +#error Unknown platform + +#endif + +#endif /* prinet_h__ */ diff --git a/nsprpub/pr/include/prinit.h b/nsprpub/pr/include/prinit.h new file mode 100644 index 0000000000..9f3be5c061 --- /dev/null +++ b/nsprpub/pr/include/prinit.h @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prinit_h___ +#define prinit_h___ + +#include "prthread.h" +#include "prtypes.h" +#include "prwin16.h" +#include <stdio.h> + +PR_BEGIN_EXTERN_C + +/************************************************************************/ +/**************************IDENTITY AND VERSIONING***********************/ +/************************************************************************/ + +/* +** NSPR's name, this should persist until at least the turn of the +** century. +*/ +#define PR_NAME "NSPR" + +/* +** NSPR's version is used to determine the likelihood that the version you +** used to build your component is anywhere close to being compatible with +** what is in the underlying library. +** +** The format of the version string is +** "<major version>.<minor version>[.<patch level>] [<Beta>]" +*/ +#define PR_VERSION "4.35" +#define PR_VMAJOR 4 +#define PR_VMINOR 35 +#define PR_VPATCH 0 +#define PR_BETA PR_FALSE + +/* +** PRVersionCheck +** +** The basic signature of the function that is called to provide version +** checking. The result will be a boolean that indicates the likelihood +** that the underling library will perform as the caller expects. +** +** The only argument is a string, which should be the verson identifier +** of the library in question. That string will be compared against an +** equivalent string that represents the actual build version of the +** exporting library. +** +** The result will be the logical union of the directly called library +** and all dependent libraries. +*/ + +typedef PRBool (*PRVersionCheck)(const char*); + +/* +** PR_VersionCheck +** +** NSPR's existance proof of the version check function. +** +** Note that NSPR has no cooperating dependencies. +*/ + +NSPR_API(PRBool) PR_VersionCheck(const char *importedVersion); + +/* + * Returns a const string of the NSPR library version. + */ +NSPR_API(const char*) PR_GetVersion(void); + + +/************************************************************************/ +/*******************************INITIALIZATION***************************/ +/************************************************************************/ + +/* +** Initialize the runtime. Attach a thread object to the currently +** executing native thread of type "type". +** +** The specificaiton of 'maxPTDs' is ignored. +*/ +NSPR_API(void) PR_Init( + PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs); + +/* +** And alternate form of initialization, one that may become the default if +** not the only mechanism, provides a method to get the NSPR runtime init- +** ialized and place NSPR between the caller and the runtime library. This +** allows main() to be treated as any other thread root function, signalling +** its compeletion by returning and allowing the runtime to coordinate the +** completion of the other threads of the runtime. +** +** The priority of the main (or primordial) thread will be PR_PRIORITY_NORMAL. +** The thread may adjust its own priority by using PR_SetPriority(), though +** at this time the support for priorities is somewhat weak. +** +** The specificaiton of 'maxPTDs' is ignored. +** +** The value returned by PR_Initialize is the value returned from the root +** function, 'prmain'. +*/ + +typedef PRIntn (PR_CALLBACK *PRPrimordialFn)(PRIntn argc, char **argv); + +NSPR_API(PRIntn) PR_Initialize( + PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs); + +/* +** Return PR_TRUE if PR_Init has already been called. +*/ +NSPR_API(PRBool) PR_Initialized(void); + +/* + * Perform a graceful shutdown of NSPR. PR_Cleanup() may be called by + * the primordial thread near the end of the main() function. + * + * PR_Cleanup() attempts to synchronize the natural termination of + * process. It does that by blocking the caller, if and only if it is + * the primordial thread, until the number of user threads has dropped + * to zero. When the primordial thread returns from main(), the process + * will immediately and silently exit. That is, it will (if necessary) + * forcibly terminate any existing threads and exit without significant + * blocking and there will be no error messages or core files. + * + * PR_Cleanup() returns PR_SUCCESS if NSPR is successfully shutdown, + * or PR_FAILURE if the calling thread of this function is not the + * primordial thread. + */ +NSPR_API(PRStatus) PR_Cleanup(void); + +/* +** Disable Interrupts +** Disables timer signals used for pre-emptive scheduling. +*/ +NSPR_API(void) PR_DisableClockInterrupts(void); + +/* +** Enables Interrupts +** Enables timer signals used for pre-emptive scheduling. +*/ +NSPR_API(void) PR_EnableClockInterrupts(void); + +/* +** Block Interrupts +** Blocks the timer signal used for pre-emptive scheduling +*/ +NSPR_API(void) PR_BlockClockInterrupts(void); + +/* +** Unblock Interrupts +** Unblocks the timer signal used for pre-emptive scheduling +*/ +NSPR_API(void) PR_UnblockClockInterrupts(void); + +/* +** Create extra virtual processor threads. Generally used with MP systems. +*/ +NSPR_API(void) PR_SetConcurrency(PRUintn numCPUs); + +/* +** Control the method and size of the file descriptor (PRFileDesc*) +** cache used by the runtime. Setting 'high' to zero is for performance, +** any other value probably for debugging (see memo on FD caching). +*/ +NSPR_API(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high); + +/* + * Cause an immediate, nongraceful, forced termination of the process. + * It takes a PRIntn argument, which is the exit status code of the + * process. + */ +NSPR_API(void) PR_ProcessExit(PRIntn status); + +/* +** Abort the process in a non-graceful manner. This will cause a core file, +** call to the debugger or other moral equivalent as well as causing the +** entire process to stop. +*/ +NSPR_API(void) PR_Abort(void); + +/* + **************************************************************** + * + * Module initialization: + * + **************************************************************** + */ + +typedef struct PRCallOnceType { + PRIntn initialized; + PRInt32 inProgress; + PRStatus status; +} PRCallOnceType; + +typedef PRStatus (PR_CALLBACK *PRCallOnceFN)(void); + +typedef PRStatus (PR_CALLBACK *PRCallOnceWithArgFN)(void *arg); + +NSPR_API(PRStatus) PR_CallOnce( + PRCallOnceType *once, + PRCallOnceFN func +); + +NSPR_API(PRStatus) PR_CallOnceWithArg( + PRCallOnceType *once, + PRCallOnceWithArgFN func, + void *arg +); + + +PR_END_EXTERN_C + +#endif /* prinit_h___ */ diff --git a/nsprpub/pr/include/prinrval.h b/nsprpub/pr/include/prinrval.h new file mode 100644 index 0000000000..7797291d63 --- /dev/null +++ b/nsprpub/pr/include/prinrval.h @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prinrval.h +** Description: API to interval timing functions of NSPR. +** +** +** NSPR provides interval times that are independent of network time +** of day values. Interval times are (in theory) accurate regardless +** of host processing requirements and also very cheap to acquire. It +** is expected that getting an interval time while in a synchronized +** function (holding one's lock). +**/ + +#if !defined(prinrval_h) +#define prinrval_h + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/**********************************************************************/ +/************************* TYPES AND CONSTANTS ************************/ +/**********************************************************************/ + +typedef PRUint32 PRIntervalTime; + +/*********************************************************************** +** DEFINES: PR_INTERVAL_MIN +** PR_INTERVAL_MAX +** DESCRIPTION: +** These two constants define the range (in ticks / second) of the +** platform dependent type, PRIntervalTime. These constants bound both +** the period and the resolution of a PRIntervalTime. +***********************************************************************/ +#define PR_INTERVAL_MIN 1000UL +#define PR_INTERVAL_MAX 100000UL + +/*********************************************************************** +** DEFINES: PR_INTERVAL_NO_WAIT +** PR_INTERVAL_NO_TIMEOUT +** DESCRIPTION: +** Two reserved constants are defined in the PRIntervalTime namespace. +** They are used to indicate that the process should wait no time (return +** immediately) or wait forever (never time out), respectively. +** Note: PR_INTERVAL_NO_TIMEOUT passed as input to PR_Connect is +** interpreted as use the OS's connect timeout. +** +***********************************************************************/ +#define PR_INTERVAL_NO_WAIT 0UL +#define PR_INTERVAL_NO_TIMEOUT 0xffffffffUL + +/**********************************************************************/ +/****************************** FUNCTIONS *****************************/ +/**********************************************************************/ + +/*********************************************************************** +** FUNCTION: PR_IntervalNow +** DESCRIPTION: +** Return the value of NSPR's free running interval timer. That timer +** can be used to establish epochs and determine intervals (be computing +** the difference between two times). +** INPUTS: void +** OUTPUTS: void +** RETURN: PRIntervalTime +** +** SIDE EFFECTS: +** None +** RESTRICTIONS: +** The units of PRIntervalTime are platform dependent. They are chosen +** such that they are appropriate for the host OS, yet provide sufficient +** resolution and period to be useful to clients. +** MEMORY: N/A +** ALGORITHM: Platform dependent +***********************************************************************/ +NSPR_API(PRIntervalTime) PR_IntervalNow(void); + +/*********************************************************************** +** FUNCTION: PR_TicksPerSecond +** DESCRIPTION: +** Return the number of ticks per second for PR_IntervalNow's clock. +** The value will be in the range [PR_INTERVAL_MIN..PR_INTERVAL_MAX]. +** INPUTS: void +** OUTPUTS: void +** RETURN: PRUint32 +** +** SIDE EFFECTS: +** None +** RESTRICTIONS: +** None +** MEMORY: N/A +** ALGORITHM: N/A +***********************************************************************/ +NSPR_API(PRUint32) PR_TicksPerSecond(void); + +/*********************************************************************** +** FUNCTION: PR_SecondsToInterval +** PR_MillisecondsToInterval +** PR_MicrosecondsToInterval +** DESCRIPTION: +** Convert standard clock units to platform dependent intervals. +** INPUTS: PRUint32 +** OUTPUTS: void +** RETURN: PRIntervalTime +** +** SIDE EFFECTS: +** None +** RESTRICTIONS: +** Conversion may cause overflow, which is not reported. +** MEMORY: N/A +** ALGORITHM: N/A +***********************************************************************/ +NSPR_API(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds); +NSPR_API(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli); +NSPR_API(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro); + +/*********************************************************************** +** FUNCTION: PR_IntervalToSeconds +** PR_IntervalToMilliseconds +** PR_IntervalToMicroseconds +** DESCRIPTION: +** Convert platform dependent intervals to standard clock units. +** INPUTS: PRIntervalTime +** OUTPUTS: void +** RETURN: PRUint32 +** +** SIDE EFFECTS: +** None +** RESTRICTIONS: +** Conversion may cause overflow, which is not reported. +** MEMORY: N/A +** ALGORITHM: N/A +***********************************************************************/ +NSPR_API(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks); +NSPR_API(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks); +NSPR_API(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks); + +PR_END_EXTERN_C + + +#endif /* !defined(prinrval_h) */ + +/* prinrval.h */ diff --git a/nsprpub/pr/include/prio.h b/nsprpub/pr/include/prio.h new file mode 100644 index 0000000000..8b331a324c --- /dev/null +++ b/nsprpub/pr/include/prio.h @@ -0,0 +1,2016 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: prio.h + * + * Description: PR i/o related stuff, such as file system access, file + * i/o, socket i/o, etc. + */ + +#ifndef prio_h___ +#define prio_h___ + +#include "prlong.h" +#include "prtime.h" +#include "prinrval.h" +#include "prinet.h" + +PR_BEGIN_EXTERN_C + +/* Typedefs */ +typedef struct PRDir PRDir; +typedef struct PRDirEntry PRDirEntry; +#ifdef MOZ_UNICODE +typedef struct PRDirUTF16 PRDirUTF16; +typedef struct PRDirEntryUTF16 PRDirEntryUTF16; +#endif /* MOZ_UNICODE */ +typedef struct PRFileDesc PRFileDesc; +typedef struct PRFileInfo PRFileInfo; +typedef struct PRFileInfo64 PRFileInfo64; +typedef union PRNetAddr PRNetAddr; +typedef struct PRIOMethods PRIOMethods; +typedef struct PRPollDesc PRPollDesc; +typedef struct PRFilePrivate PRFilePrivate; +typedef struct PRSendFileData PRSendFileData; + +/* +*************************************************************************** +** The file descriptor. +** This is the primary structure to represent any active open socket, +** whether it be a normal file or a network connection. Such objects +** are stackable (or layerable). Each layer may have its own set of +** method pointers and context private to that layer. All each layer +** knows about its neighbors is how to get to their method table. +*************************************************************************** +*/ + +typedef PRIntn PRDescIdentity; /* see: Layering file descriptors */ + +struct PRFileDesc { + const PRIOMethods *methods; /* the I/O methods table */ + PRFilePrivate *secret; /* layer dependent data */ + PRFileDesc *lower, *higher; /* pointers to adjacent layers */ + void (PR_CALLBACK *dtor)(PRFileDesc *fd); + /* A destructor function for layer */ + PRDescIdentity identity; /* Identity of this particular layer */ +}; + +/* +*************************************************************************** +** PRTransmitFileFlags +** +** Flags for PR_TransmitFile. Pass PR_TRANSMITFILE_CLOSE_SOCKET to +** PR_TransmitFile if the connection should be closed after the file +** is transmitted. +*************************************************************************** +*/ +typedef enum PRTransmitFileFlags { + PR_TRANSMITFILE_KEEP_OPEN = 0, /* socket is left open after file + * is transmitted. */ + PR_TRANSMITFILE_CLOSE_SOCKET = 1 /* socket is closed after file + * is transmitted. */ +} PRTransmitFileFlags; + +/* +************************************************************************** +** Macros for PRNetAddr +** +** Address families: PR_AF_INET, PR_AF_INET6, PR_AF_LOCAL +** IP addresses: PR_INADDR_ANY, PR_INADDR_LOOPBACK, PR_INADDR_BROADCAST +************************************************************************** +*/ + +#ifdef WIN32 + +#define PR_AF_INET 2 +#define PR_AF_LOCAL 1 +#define PR_INADDR_ANY (unsigned long)0x00000000 +#define PR_INADDR_LOOPBACK 0x7f000001 +#define PR_INADDR_BROADCAST (unsigned long)0xffffffff + +#else /* WIN32 */ + +#define PR_AF_INET AF_INET +#define PR_AF_LOCAL AF_UNIX +#define PR_INADDR_ANY INADDR_ANY +#define PR_INADDR_LOOPBACK INADDR_LOOPBACK +#define PR_INADDR_BROADCAST INADDR_BROADCAST + +#endif /* WIN32 */ + +/* +** Define PR_AF_INET6 in prcpucfg.h with the same +** value as AF_INET6 on platforms with IPv6 support. +** Otherwise define it here. +*/ +#ifndef PR_AF_INET6 +#define PR_AF_INET6 100 +#endif + +#define PR_AF_INET_SDP 101 +#define PR_AF_INET6_SDP 102 + +#ifndef PR_AF_UNSPEC +#define PR_AF_UNSPEC 0 +#endif + +/* +************************************************************************** +** A network address +** +** Only Internet Protocol (IPv4 and IPv6) addresses are supported. +** The address family must always represent IPv4 (AF_INET, probably == 2) +** or IPv6 (AF_INET6). +************************************************************************** +*************************************************************************/ + +struct PRIPv6Addr { + union { + PRUint8 _S6_u8[16]; + PRUint16 _S6_u16[8]; + PRUint32 _S6_u32[4]; + PRUint64 _S6_u64[2]; + } _S6_un; +}; +#define pr_s6_addr _S6_un._S6_u8 +#define pr_s6_addr16 _S6_un._S6_u16 +#define pr_s6_addr32 _S6_un._S6_u32 +#define pr_s6_addr64 _S6_un._S6_u64 + +typedef struct PRIPv6Addr PRIPv6Addr; + +union PRNetAddr { + struct { + PRUint16 family; /* address family (0x00ff maskable) */ + char data[14]; /* raw address data */ + } raw; + struct { + PRUint16 family; /* address family (AF_INET) */ + PRUint16 port; /* port number */ + PRUint32 ip; /* The actual 32 bits of address */ + char pad[8]; + } inet; + struct { + PRUint16 family; /* address family (AF_INET6) */ + 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; +#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_WIN) + struct { /* Unix domain socket address */ + PRUint16 family; /* address family (AF_UNIX) */ +#ifdef XP_OS2 + char path[108]; /* null-terminated pathname */ + /* bind fails if size is not 108. */ +#else + char path[104]; /* null-terminated pathname */ +#endif + } local; +#endif +}; + +/* +*************************************************************************** +** PRSockOption +** +** The file descriptors can have predefined options set after they file +** descriptor is created to change their behavior. Only the options in +** the following enumeration are supported. +*************************************************************************** +*/ +typedef enum PRSockOption +{ + PR_SockOpt_Nonblocking, /* nonblocking io */ + PR_SockOpt_Linger, /* linger on close if data present */ + PR_SockOpt_Reuseaddr, /* allow local address reuse */ + PR_SockOpt_Keepalive, /* keep connections alive */ + PR_SockOpt_RecvBufferSize, /* receive buffer size */ + PR_SockOpt_SendBufferSize, /* send buffer size */ + + PR_SockOpt_IpTimeToLive, /* time to live */ + PR_SockOpt_IpTypeOfService, /* type of service and precedence */ + + PR_SockOpt_AddMember, /* add an IP group membership */ + PR_SockOpt_DropMember, /* drop an IP group membership */ + PR_SockOpt_McastInterface, /* multicast interface address */ + PR_SockOpt_McastTimeToLive, /* multicast timetolive */ + PR_SockOpt_McastLoopback, /* multicast loopback */ + + PR_SockOpt_NoDelay, /* don't delay send to coalesce packets */ + PR_SockOpt_MaxSegment, /* maximum segment size */ + PR_SockOpt_Broadcast, /* enable broadcast */ + PR_SockOpt_Reuseport, /* allow local address & port reuse on + * platforms that support it */ + PR_SockOpt_DontFrag, /* Do not fragment flag */ + PR_SockOpt_Last +} PRSockOption; + +typedef struct PRLinger { + PRBool polarity; /* Polarity of the option's setting */ + PRIntervalTime linger; /* Time to linger before closing */ +} PRLinger; + +typedef struct PRMcastRequest { + PRNetAddr mcaddr; /* IP multicast address of group */ + PRNetAddr ifaddr; /* local IP address of interface */ +} PRMcastRequest; + +typedef struct PRSocketOptionData +{ + PRSockOption option; + union + { + PRUintn ip_ttl; /* IP time to live */ + PRUintn mcast_ttl; /* IP multicast time to live */ + PRUintn tos; /* IP type of service and precedence */ + PRBool non_blocking; /* Non-blocking (network) I/O */ + PRBool reuse_addr; /* Allow local address reuse */ + PRBool reuse_port; /* Allow local address & port reuse on + * platforms that support it */ + PRBool dont_fragment; /* Do not fragment flag */ + PRBool keep_alive; /* Keep connections alive */ + PRBool mcast_loopback; /* IP multicast loopback */ + PRBool no_delay; /* Don't delay send to coalesce packets */ + PRBool broadcast; /* Enable broadcast */ + PRSize max_segment; /* Maximum segment size */ + PRSize recv_buffer_size; /* Receive buffer size */ + PRSize send_buffer_size; /* Send buffer size */ + PRLinger linger; /* Time to linger on close if data present */ + PRMcastRequest add_member; /* add an IP group membership */ + PRMcastRequest drop_member; /* Drop an IP group membership */ + PRNetAddr mcast_if; /* multicast interface address */ + } value; +} PRSocketOptionData; + +/* +*************************************************************************** +** PRIOVec +** +** The I/O vector is used by the write vector method to describe the areas +** that are affected by the ouput operation. +*************************************************************************** +*/ +typedef struct PRIOVec { + char *iov_base; + int iov_len; +} PRIOVec; + +/* +*************************************************************************** +** Discover what type of socket is being described by the file descriptor. +*************************************************************************** +*/ +typedef enum PRDescType +{ + PR_DESC_FILE = 1, + PR_DESC_SOCKET_TCP = 2, + PR_DESC_SOCKET_UDP = 3, + PR_DESC_LAYERED = 4, + PR_DESC_PIPE = 5 +} PRDescType; + +typedef enum PRSeekWhence { + PR_SEEK_SET = 0, + PR_SEEK_CUR = 1, + PR_SEEK_END = 2 +} PRSeekWhence; + +NSPR_API(PRDescType) PR_GetDescType(PRFileDesc *file); + +/* +*************************************************************************** +** PRIOMethods +** +** The I/O methods table provides procedural access to the functions of +** the file descriptor. It is the responsibility of a layer implementor +** to provide suitable functions at every entry point. If a layer provides +** no functionality, it should call the next lower(higher) function of the +** same name (e.g., return fd->lower->method->close(fd->lower)); +** +** Not all functions are implemented for all types of files. In cases where +** that is true, the function will return a error indication with an error +** code of PR_INVALID_METHOD_ERROR. +*************************************************************************** +*/ + +typedef PRStatus (PR_CALLBACK *PRCloseFN)(PRFileDesc *fd); +typedef PRInt32 (PR_CALLBACK *PRReadFN)(PRFileDesc *fd, void *buf, PRInt32 amount); +typedef PRInt32 (PR_CALLBACK *PRWriteFN)(PRFileDesc *fd, const void *buf, PRInt32 amount); +typedef PRInt32 (PR_CALLBACK *PRAvailableFN)(PRFileDesc *fd); +typedef PRInt64 (PR_CALLBACK *PRAvailable64FN)(PRFileDesc *fd); +typedef PRStatus (PR_CALLBACK *PRFsyncFN)(PRFileDesc *fd); +typedef PROffset32 (PR_CALLBACK *PRSeekFN)(PRFileDesc *fd, PROffset32 offset, PRSeekWhence how); +typedef PROffset64 (PR_CALLBACK *PRSeek64FN)(PRFileDesc *fd, PROffset64 offset, PRSeekWhence how); +typedef PRStatus (PR_CALLBACK *PRFileInfoFN)(PRFileDesc *fd, PRFileInfo *info); +typedef PRStatus (PR_CALLBACK *PRFileInfo64FN)(PRFileDesc *fd, PRFileInfo64 *info); +typedef PRInt32 (PR_CALLBACK *PRWritevFN)( + PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, + PRIntervalTime timeout); +typedef PRStatus (PR_CALLBACK *PRConnectFN)( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout); +typedef PRFileDesc* (PR_CALLBACK *PRAcceptFN) ( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout); +typedef PRStatus (PR_CALLBACK *PRBindFN)(PRFileDesc *fd, const PRNetAddr *addr); +typedef PRStatus (PR_CALLBACK *PRListenFN)(PRFileDesc *fd, PRIntn backlog); +typedef PRStatus (PR_CALLBACK *PRShutdownFN)(PRFileDesc *fd, PRIntn how); +typedef PRInt32 (PR_CALLBACK *PRRecvFN)( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); +typedef PRInt32 (PR_CALLBACK *PRSendFN) ( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); +typedef PRInt32 (PR_CALLBACK *PRRecvfromFN)( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout); +typedef PRInt32 (PR_CALLBACK *PRSendtoFN)( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout); +typedef PRInt16 (PR_CALLBACK *PRPollFN)( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags); +typedef PRInt32 (PR_CALLBACK *PRAcceptreadFN)( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime t); +typedef PRInt32 (PR_CALLBACK *PRTransmitfileFN)( + PRFileDesc *sd, PRFileDesc *fd, const void *headers, + PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime t); +typedef PRStatus (PR_CALLBACK *PRGetsocknameFN)(PRFileDesc *fd, PRNetAddr *addr); +typedef PRStatus (PR_CALLBACK *PRGetpeernameFN)(PRFileDesc *fd, PRNetAddr *addr); +typedef PRStatus (PR_CALLBACK *PRGetsocketoptionFN)( + PRFileDesc *fd, PRSocketOptionData *data); +typedef PRStatus (PR_CALLBACK *PRSetsocketoptionFN)( + PRFileDesc *fd, const PRSocketOptionData *data); +typedef PRInt32 (PR_CALLBACK *PRSendfileFN)( + PRFileDesc *networkSocket, PRSendFileData *sendData, + PRTransmitFileFlags flags, PRIntervalTime timeout); +typedef PRStatus (PR_CALLBACK *PRConnectcontinueFN)( + PRFileDesc *fd, PRInt16 out_flags); +typedef PRIntn (PR_CALLBACK *PRReservedFN)(PRFileDesc *fd); + +struct PRIOMethods { + PRDescType file_type; /* Type of file represented (tos) */ + PRCloseFN close; /* close file and destroy descriptor */ + PRReadFN read; /* read up to specified bytes into buffer */ + PRWriteFN write; /* write specified bytes from buffer */ + PRAvailableFN available; /* determine number of bytes available */ + PRAvailable64FN available64; /* ditto, 64 bit */ + PRFsyncFN fsync; /* flush all buffers to permanent store */ + PRSeekFN seek; /* position the file to the desired place */ + PRSeek64FN seek64; /* ditto, 64 bit */ + PRFileInfoFN fileInfo; /* Get information about an open file */ + PRFileInfo64FN fileInfo64; /* ditto, 64 bit */ + PRWritevFN writev; /* Write segments as described by iovector */ + PRConnectFN connect; /* Connect to the specified (net) address */ + PRAcceptFN accept; /* Accept a connection for a (net) peer */ + PRBindFN bind; /* Associate a (net) address with the fd */ + PRListenFN listen; /* Prepare to listen for (net) connections */ + PRShutdownFN shutdown; /* Shutdown a (net) connection */ + PRRecvFN recv; /* Solicit up the the specified bytes */ + PRSendFN send; /* Send all the bytes specified */ + PRRecvfromFN recvfrom; /* Solicit (net) bytes and report source */ + PRSendtoFN sendto; /* Send bytes to (net) address specified */ + PRPollFN poll; /* Test the fd to see if it is ready */ + PRAcceptreadFN acceptread; /* Accept and read on a new (net) fd */ + PRTransmitfileFN transmitfile; /* Transmit at entire file */ + PRGetsocknameFN getsockname; /* Get (net) address associated with fd */ + PRGetpeernameFN getpeername; /* Get peer's (net) address */ + PRReservedFN reserved_fn_6; /* reserved for future use */ + PRReservedFN reserved_fn_5; /* reserved for future use */ + PRGetsocketoptionFN getsocketoption; + /* Get current setting of specified option */ + PRSetsocketoptionFN setsocketoption; + /* Set value of specified option */ + PRSendfileFN sendfile; /* Send a (partial) file with header/trailer*/ + PRConnectcontinueFN connectcontinue; + /* Continue a nonblocking connect */ + PRReservedFN reserved_fn_3; /* reserved for future use */ + PRReservedFN reserved_fn_2; /* reserved for future use */ + PRReservedFN reserved_fn_1; /* reserved for future use */ + PRReservedFN reserved_fn_0; /* reserved for future use */ +}; + +/* + ************************************************************************** + * FUNCTION: PR_GetSpecialFD + * DESCRIPTION: Get the file descriptor that represents the standard input, + * output, or error stream. + * INPUTS: + * PRSpecialFD id + * A value indicating the type of stream desired: + * PR_StandardInput: standard input + * PR_StandardOuput: standard output + * PR_StandardError: standard error + * OUTPUTS: none + * RETURNS: PRFileDesc * + * If the argument is valid, PR_GetSpecialFD returns a file descriptor + * that represents the corresponding standard I/O stream. Otherwise, + * PR_GetSpecialFD returns NULL and sets error PR_INVALID_ARGUMENT_ERROR. + ************************************************************************** + */ + +typedef enum PRSpecialFD +{ + PR_StandardInput, /* standard input */ + PR_StandardOutput, /* standard output */ + PR_StandardError /* standard error */ +} PRSpecialFD; + +NSPR_API(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD id); + +#define PR_STDIN PR_GetSpecialFD(PR_StandardInput) +#define PR_STDOUT PR_GetSpecialFD(PR_StandardOutput) +#define PR_STDERR PR_GetSpecialFD(PR_StandardError) + +/* + ************************************************************************** + * Layering file descriptors + * + * File descriptors may be layered. Each layer has it's own identity. + * Identities are allocated by the runtime and are to be associated + * (by the layer implementor) with all layers that are of that type. + * It is then possible to scan the chain of layers and find a layer + * that one recongizes and therefore predict that it will implement + * a desired protocol. + * + * There are three well-known identities: + * PR_INVALID_IO_LAYER => an invalid layer identity, for error return + * PR_TOP_IO_LAYER => the identity of the top of the stack + * PR_NSPR_IO_LAYER => the identity used by NSPR proper + * PR_TOP_IO_LAYER may be used as a shorthand for identifying the topmost + * layer of an existing stack. Ie., the following two constructs are + * equivalent. + * + * rv = PR_PushIOLayer(stack, PR_TOP_IO_LAYER, my_layer); + * rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), my_layer) + * + * A string may be associated with the creation of the identity. It + * will be copied by the runtime. If queried the runtime will return + * a reference to that copied string (not yet another copy). There + * is no facility for deleting an identity. + ************************************************************************** + */ + +#define PR_IO_LAYER_HEAD (PRDescIdentity)-3 +#define PR_INVALID_IO_LAYER (PRDescIdentity)-1 +#define PR_TOP_IO_LAYER (PRDescIdentity)-2 +#define PR_NSPR_IO_LAYER (PRDescIdentity)0 + +NSPR_API(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name); +NSPR_API(const char*) PR_GetNameForIdentity(PRDescIdentity ident); +NSPR_API(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd); +NSPR_API(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd_stack, PRDescIdentity id); + +/* + ************************************************************************** + * PR_GetDefaultIOMethods: Accessing the default methods table. + * You may get a pointer to the default methods table by calling this function. + * You may then select any elements from that table with which to build your + * layer's methods table. You may NOT modify the table directly. + ************************************************************************** + */ +NSPR_API(const PRIOMethods *) PR_GetDefaultIOMethods(void); + +/* + ************************************************************************** + * Creating a layer + * + * A new layer may be allocated by calling PR_CreateIOLayerStub(). The + * file descriptor returned will contain the pointer to the methods table + * provided. The runtime will not modify the table nor test its correctness. + ************************************************************************** + */ +NSPR_API(PRFileDesc*) PR_CreateIOLayerStub( + PRDescIdentity ident, const PRIOMethods *methods); + +/* + ************************************************************************** + * Creating a layer + * + * A new stack may be created by calling PR_CreateIOLayer(). The + * file descriptor returned will point to the top of the stack, which has + * the layer 'fd' as the topmost layer. + * + * NOTE: This function creates a new style stack, which has a fixed, dummy + * header. The old style stack, created by a call to PR_PushIOLayer, + * results in modifying contents of the top layer of the stack, when + * pushing and popping layers of the stack. + ************************************************************************** + */ +NSPR_API(PRFileDesc*) PR_CreateIOLayer(PRFileDesc* fd); + +/* + ************************************************************************** + * Pushing a layer + * + * A file descriptor (perhaps allocated using PR_CreateIOLayerStub()) may + * be pushed into an existing stack of file descriptors at any point the + * caller deems appropriate. The new layer will be inserted into the stack + * just above the layer with the indicated identity. + * + * Note: Even if the identity parameter indicates the top-most layer of + * the stack, the value of the file descriptor describing the original + * stack will not change. + ************************************************************************** + */ +NSPR_API(PRStatus) PR_PushIOLayer( + PRFileDesc *fd_stack, PRDescIdentity id, PRFileDesc *layer); + +/* + ************************************************************************** + * Popping a layer + * + * A layer may be popped from a stack by indicating the identity of the + * layer to be removed. If found, a pointer to the removed object will + * be returned to the caller. The object then becomes the responsibility + * of the caller. + * + * Note: Even if the identity indicates the top layer of the stack, the + * reference returned will not be the file descriptor for the stack and + * that file descriptor will remain valid. + ************************************************************************** + */ +NSPR_API(PRFileDesc*) PR_PopIOLayer(PRFileDesc *fd_stack, PRDescIdentity id); + +/* + ************************************************************************** + * FUNCTION: PR_Open + * DESCRIPTION: Open a file for reading, writing, or both. + * INPUTS: + * const char *name + * The path name of the file to be opened + * PRIntn flags + * The file status flags. + * It is a bitwise OR of the following bit flags (only one of + * the first three flags below may be used): + * PR_RDONLY Open for reading only. + * PR_WRONLY Open for writing only. + * PR_RDWR Open for reading and writing. + * PR_CREATE_FILE If the file does not exist, the file is created + * If the file exists, this flag has no effect. + * PR_SYNC If set, each write will wait for both the file data + * and file status to be physically updated. + * PR_APPEND The file pointer is set to the end of + * the file prior to each write. + * PR_TRUNCATE If the file exists, its length is truncated to 0. + * PR_EXCL With PR_CREATE_FILE, if the file does not exist, + * the file is created. If the file already + * exists, no action and NULL is returned + * + * PRIntn mode + * The access permission bits of the file mode, if the file is + * created when PR_CREATE_FILE is on. + * OUTPUTS: None + * RETURNS: PRFileDesc * + * If the file is successfully opened, + * returns a pointer to the PRFileDesc + * created for the newly opened file. + * Returns a NULL pointer if the open + * failed. + * SIDE EFFECTS: + * RESTRICTIONS: + * MEMORY: + * The return value, if not NULL, points to a dynamically allocated + * PRFileDesc object. + * ALGORITHM: + ************************************************************************** + */ + +/* Open flags */ +#define PR_RDONLY 0x01 +#define PR_WRONLY 0x02 +#define PR_RDWR 0x04 +#define PR_CREATE_FILE 0x08 +#define PR_APPEND 0x10 +#define PR_TRUNCATE 0x20 +#define PR_SYNC 0x40 +#define PR_EXCL 0x80 + +/* +** File modes .... +** +** CAVEAT: 'mode' is currently only applicable on UNIX platforms. +** The 'mode' argument may be ignored by PR_Open on other platforms. +** +** 00400 Read by owner. +** 00200 Write by owner. +** 00100 Execute (search if a directory) by owner. +** 00040 Read by group. +** 00020 Write by group. +** 00010 Execute by group. +** 00004 Read by others. +** 00002 Write by others +** 00001 Execute by others. +** +*/ + +NSPR_API(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode); + +/* + ************************************************************************** + * FUNCTION: PR_OpenFile + * DESCRIPTION: + * Open a file for reading, writing, or both. + * PR_OpenFile has the same prototype as PR_Open but implements + * the specified file mode where possible. + ************************************************************************** + */ + +/* File mode bits */ +#define PR_IRWXU 00700 /* read, write, execute/search by owner */ +#define PR_IRUSR 00400 /* read permission, owner */ +#define PR_IWUSR 00200 /* write permission, owner */ +#define PR_IXUSR 00100 /* execute/search permission, owner */ +#define PR_IRWXG 00070 /* read, write, execute/search by group */ +#define PR_IRGRP 00040 /* read permission, group */ +#define PR_IWGRP 00020 /* write permission, group */ +#define PR_IXGRP 00010 /* execute/search permission, group */ +#define PR_IRWXO 00007 /* read, write, execute/search by others */ +#define PR_IROTH 00004 /* read permission, others */ +#define PR_IWOTH 00002 /* write permission, others */ +#define PR_IXOTH 00001 /* execute/search permission, others */ + +NSPR_API(PRFileDesc*) PR_OpenFile( + const char *name, PRIntn flags, PRIntn mode); + +#ifdef MOZ_UNICODE +/* + * EXPERIMENTAL: This function may be removed in a future release. + */ +NSPR_API(PRFileDesc*) PR_OpenFileUTF16( + const PRUnichar *name, PRIntn flags, PRIntn mode); +#endif /* MOZ_UNICODE */ + +/* + ************************************************************************** + * FUNCTION: PR_Close + * DESCRIPTION: + * Close a file or socket. + * INPUTS: + * PRFileDesc *fd + * a pointer to a PRFileDesc. + * OUTPUTS: + * None. + * RETURN: + * PRStatus + * SIDE EFFECTS: + * RESTRICTIONS: + * None. + * MEMORY: + * The dynamic memory pointed to by the argument fd is freed. + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_Close(PRFileDesc *fd); + +/* + ************************************************************************** + * FUNCTION: PR_Read + * DESCRIPTION: + * Read bytes from a file or socket. + * The operation will block until either an end of stream indication is + * encountered, some positive number of bytes are transferred, or there + * is an error. No more than 'amount' bytes will be transferred. + * INPUTS: + * PRFileDesc *fd + * pointer to the PRFileDesc object for the file or socket + * void *buf + * pointer to a buffer to hold the data read in. + * PRInt32 amount + * the size of 'buf' (in bytes) + * OUTPUTS: + * RETURN: + * PRInt32 + * a positive number indicates the number of bytes actually read in. + * 0 means end of file is reached or the network connection is closed. + * -1 indicates a failure. The reason for the failure is obtained + * by calling PR_GetError(). + * SIDE EFFECTS: + * data is written into the buffer pointed to by 'buf'. + * RESTRICTIONS: + * None. + * MEMORY: + * N/A + * ALGORITHM: + * N/A + ************************************************************************** + */ + +NSPR_API(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount); + +/* + *************************************************************************** + * FUNCTION: PR_Write + * DESCRIPTION: + * Write a specified number of bytes to a file or socket. The thread + * invoking this function blocks until all the data is written. + * INPUTS: + * PRFileDesc *fd + * pointer to a PRFileDesc object that refers to a file or socket + * const void *buf + * pointer to the buffer holding the data + * PRInt32 amount + * amount of data in bytes to be written from the buffer + * OUTPUTS: + * None. + * RETURN: PRInt32 + * A positive number indicates the number of bytes successfully written. + * A -1 is an indication that the operation failed. The reason + * for the failure is obtained by calling PR_GetError(). + *************************************************************************** + */ + +NSPR_API(PRInt32) PR_Write(PRFileDesc *fd,const void *buf,PRInt32 amount); + +/* + *************************************************************************** + * FUNCTION: PR_Writev + * DESCRIPTION: + * Write data to a socket. The data is organized in a PRIOVec array. The + * operation will block until all the data is written or the operation + * fails. + * INPUTS: + * PRFileDesc *fd + * Pointer that points to a PRFileDesc object for a socket. + * const PRIOVec *iov + * An array of PRIOVec. PRIOVec is a struct with the following + * two fields: + * char *iov_base; + * int iov_len; + * PRInt32 iov_size + * Number of elements in the iov array. The value of this + * argument must not be greater than PR_MAX_IOVECTOR_SIZE. + * If it is, the method will fail (PR_BUFFER_OVERFLOW_ERROR). + * PRIntervalTime timeout + * Time limit for completion of the entire write operation. + * OUTPUTS: + * None + * RETURN: + * A positive number indicates the number of bytes successfully written. + * A -1 is an indication that the operation failed. The reason + * for the failure is obtained by calling PR_GetError(). + *************************************************************************** + */ + +#define PR_MAX_IOVECTOR_SIZE 16 /* 'iov_size' must be <= */ + +NSPR_API(PRInt32) PR_Writev( + PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, + PRIntervalTime timeout); + +/* + *************************************************************************** + * FUNCTION: PR_Delete + * DESCRIPTION: + * Delete a file from the filesystem. The operation may fail if the + * file is open. + * INPUTS: + * const char *name + * Path name of the file to be deleted. + * OUTPUTS: + * None. + * RETURN: PRStatus + * The function returns PR_SUCCESS if the file is successfully + * deleted, otherwise it returns PR_FAILURE. + *************************************************************************** + */ + +NSPR_API(PRStatus) PR_Delete(const char *name); + +/**************************************************************************/ + +typedef enum PRFileType +{ + PR_FILE_FILE = 1, + PR_FILE_DIRECTORY = 2, + PR_FILE_OTHER = 3 +} PRFileType; + +struct PRFileInfo { + PRFileType type; /* Type of file */ + PROffset32 size; /* Size, in bytes, of file's contents */ + PRTime creationTime; /* Creation time per definition of PRTime */ + PRTime modifyTime; /* Last modification time per definition of PRTime */ +}; + +struct PRFileInfo64 { + PRFileType type; /* Type of file */ + PROffset64 size; /* Size, in bytes, of file's contents */ + PRTime creationTime; /* Creation time per definition of PRTime */ + PRTime modifyTime; /* Last modification time per definition of PRTime */ +}; + +/**************************************************************************** + * FUNCTION: PR_GetFileInfo, PR_GetFileInfo64 + * DESCRIPTION: + * Get the information about the file with the given path name. This is + * applicable only to NSFileDesc describing 'file' types (see + * INPUTS: + * const char *fn + * path name of the file + * OUTPUTS: + * PRFileInfo *info + * Information about the given file is written into the file + * information object pointer to by 'info'. + * RETURN: PRStatus + * PR_GetFileInfo returns PR_SUCCESS if file information is successfully + * obtained, otherwise it returns PR_FAILURE. + *************************************************************************** + */ + +NSPR_API(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info); +NSPR_API(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info); + +#ifdef MOZ_UNICODE +/* + * EXPERIMENTAL: This function may be removed in a future release. + */ +NSPR_API(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info); +#endif /* MOZ_UNICODE */ + +/* + ************************************************************************** + * FUNCTION: PR_GetOpenFileInfo, PR_GetOpenFileInfo64 + * DESCRIPTION: + * Get information about an open file referred to by the + * given PRFileDesc object. + * INPUTS: + * const PRFileDesc *fd + * A reference to a valid, open file. + * OUTPUTS: + * Same as PR_GetFileInfo, PR_GetFileInfo64 + * RETURN: PRStatus + * PR_GetFileInfo returns PR_SUCCESS if file information is successfully + * obtained, otherwise it returns PR_FAILURE. + *************************************************************************** + */ + +NSPR_API(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info); +NSPR_API(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info); + +/* + ************************************************************************** + * FUNCTION: PR_Rename + * DESCRIPTION: + * Rename a file from the old name 'from' to the new name 'to'. + * INPUTS: + * const char *from + * The old name of the file to be renamed. + * const char *to + * The new name of the file. + * OUTPUTS: + * None. + * RETURN: PRStatus + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_Rename(const char *from, const char *to); + +/* + ************************************************************************* + * FUNCTION: PR_Access + * DESCRIPTION: + * Determine accessibility of a file. + * INPUTS: + * const char *name + * path name of the file + * PRAccessHow how + * specifies which access permission to check for. + * It can be one of the following values: + * PR_ACCESS_READ_OK Test for read permission + * PR_ACCESS_WRITE_OK Test for write permission + * PR_ACCESS_EXISTS Check existence of file + * OUTPUTS: + * None. + * RETURN: PRStatus + * PR_SUCCESS is returned if the requested access is permitted. + * Otherwise, PR_FAILURE is returned. Additional information + * regarding the reason for the failure may be retrieved from + * PR_GetError(). + ************************************************************************* + */ + +typedef enum PRAccessHow { + PR_ACCESS_EXISTS = 1, + PR_ACCESS_WRITE_OK = 2, + PR_ACCESS_READ_OK = 3 +} PRAccessHow; + +NSPR_API(PRStatus) PR_Access(const char *name, PRAccessHow how); + +/* + ************************************************************************* + * FUNCTION: PR_Seek, PR_Seek64 + * DESCRIPTION: + * Moves read-write file offset + * INPUTS: + * PRFileDesc *fd + * Pointer to a PRFileDesc object. + * PROffset32, PROffset64 offset + * Specifies a value, in bytes, that is used in conjunction + * with the 'whence' parameter to set the file pointer. A + * negative value causes seeking in the reverse direction. + * PRSeekWhence whence + * Specifies how to interpret the 'offset' parameter in setting + * the file pointer associated with the 'fd' parameter. + * Values for the 'whence' parameter are: + * PR_SEEK_SET Sets the file pointer to the value of the + * 'offset' parameter + * PR_SEEK_CUR Sets the file pointer to its current location + * plus the value of the offset parameter. + * PR_SEEK_END Sets the file pointer to the size of the + * file plus the value of the offset parameter. + * OUTPUTS: + * None. + * RETURN: PROffset32, PROffset64 + * Upon successful completion, the resulting pointer location, + * measured in bytes from the beginning of the file, is returned. + * If the PR_Seek() function fails, the file offset remains + * unchanged, and the returned value is -1. The error code can + * then be retrieved via PR_GetError(). + ************************************************************************* + */ + +NSPR_API(PROffset32) PR_Seek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence); +NSPR_API(PROffset64) PR_Seek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence); + +/* + ************************************************************************ + * FUNCTION: PR_Available + * DESCRIPTION: + * Determine the amount of data in bytes available for reading + * in the given file or socket. + * INPUTS: + * PRFileDesc *fd + * Pointer to a PRFileDesc object that refers to a file or + * socket. + * OUTPUTS: + * None + * RETURN: PRInt32, PRInt64 + * Upon successful completion, PR_Available returns the number of + * bytes beyond the current read pointer that is available for + * reading. Otherwise, it returns a -1 and the reason for the + * failure can be retrieved via PR_GetError(). + ************************************************************************ + */ + +NSPR_API(PRInt32) PR_Available(PRFileDesc *fd); +NSPR_API(PRInt64) PR_Available64(PRFileDesc *fd); + +/* + ************************************************************************ + * FUNCTION: PR_Sync + * DESCRIPTION: + * Sync any buffered data for a fd to its backing device (disk). + * INPUTS: + * PRFileDesc *fd + * Pointer to a PRFileDesc object that refers to a file or + * socket + * OUTPUTS: + * None + * RETURN: PRStatus + * PR_SUCCESS is returned if the requested access is permitted. + * Otherwise, PR_FAILURE is returned. + ************************************************************************ + */ + +NSPR_API(PRStatus) PR_Sync(PRFileDesc *fd); + +/************************************************************************/ + +struct PRDirEntry { + const char *name; /* name of entry, relative to directory name */ +}; + +#ifdef MOZ_UNICODE +struct PRDirEntryUTF16 { + const PRUnichar *name; /* name of entry in UTF16, relative to + * directory name */ +}; +#endif /* MOZ_UNICODE */ + +#if !defined(NO_NSPR_10_SUPPORT) +#define PR_DirName(dirEntry) (dirEntry->name) +#endif + +/* + ************************************************************************* + * FUNCTION: PR_OpenDir + * DESCRIPTION: + * Open the directory by the given name + * INPUTS: + * const char *name + * path name of the directory to be opened + * OUTPUTS: + * None + * RETURN: PRDir * + * If the directory is sucessfully opened, a PRDir object is + * dynamically allocated and a pointer to it is returned. + * If the directory cannot be opened, a NULL pointer is returned. + * MEMORY: + * Upon successful completion, the return value points to + * dynamically allocated memory. + ************************************************************************* + */ + +NSPR_API(PRDir*) PR_OpenDir(const char *name); + +#ifdef MOZ_UNICODE +/* + * EXPERIMENTAL: This function may be removed in a future release. + */ +NSPR_API(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name); +#endif /* MOZ_UNICODE */ + +/* + ************************************************************************* + * FUNCTION: PR_ReadDir + * DESCRIPTION: + * INPUTS: + * PRDir *dir + * pointer to a PRDir object that designates an open directory + * PRDirFlags flags + * PR_SKIP_NONE Do not skip any files + * PR_SKIP_DOT Skip the directory entry "." that + * represents the current directory + * PR_SKIP_DOT_DOT Skip the directory entry ".." that + * represents the parent directory. + * PR_SKIP_BOTH Skip both '.' and '..' + * PR_SKIP_HIDDEN Skip hidden files + * OUTPUTS: + * RETURN: PRDirEntry* + * Returns a pointer to the next entry in the directory. Returns + * a NULL pointer upon reaching the end of the directory or when an + * error occurs. The actual reason can be retrieved via PR_GetError(). + ************************************************************************* + */ + +typedef enum PRDirFlags { + PR_SKIP_NONE = 0x0, + PR_SKIP_DOT = 0x1, + PR_SKIP_DOT_DOT = 0x2, + PR_SKIP_BOTH = 0x3, + PR_SKIP_HIDDEN = 0x4 +} PRDirFlags; + +NSPR_API(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags); + +#ifdef MOZ_UNICODE +/* + * EXPERIMENTAL: This function may be removed in a future release. + */ +NSPR_API(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags); +#endif /* MOZ_UNICODE */ + +/* + ************************************************************************* + * FUNCTION: PR_CloseDir + * DESCRIPTION: + * Close the specified directory. + * INPUTS: + * PRDir *dir + * The directory to be closed. + * OUTPUTS: + * None + * RETURN: PRStatus + * If successful, will return a status of PR_SUCCESS. Otherwise + * a value of PR_FAILURE. The reason for the failure may be re- + * trieved using PR_GetError(). + ************************************************************************* + */ + +NSPR_API(PRStatus) PR_CloseDir(PRDir *dir); + +#ifdef MOZ_UNICODE +/* + * EXPERIMENTAL: This function may be removed in a future release. + */ +NSPR_API(PRStatus) PR_CloseDirUTF16(PRDirUTF16 *dir); +#endif /* MOZ_UNICODE */ + +/* + ************************************************************************* + * FUNCTION: PR_MkDir + * DESCRIPTION: + * Create a new directory with the given name and access mode. + * INPUTS: + * const char *name + * The name of the directory to be created. All the path components + * up to but not including the leaf component must already exist. + * PRIntn mode + * See 'mode' definiton in PR_Open(). + * OUTPUTS: + * None + * RETURN: PRStatus + * If successful, will return a status of PR_SUCCESS. Otherwise + * a value of PR_FAILURE. The reason for the failure may be re- + * trieved using PR_GetError(). + ************************************************************************* + */ + +NSPR_API(PRStatus) PR_MkDir(const char *name, PRIntn mode); + +/* + ************************************************************************* + * FUNCTION: PR_MakeDir + * DESCRIPTION: + * Create a new directory with the given name and access mode. + * PR_MakeDir has the same prototype as PR_MkDir but implements + * the specified access mode where possible. + ************************************************************************* + */ + +NSPR_API(PRStatus) PR_MakeDir(const char *name, PRIntn mode); + +/* + ************************************************************************* + * FUNCTION: PR_RmDir + * DESCRIPTION: + * Remove a directory by the given name. + * INPUTS: + * const char *name + * The name of the directory to be removed. All the path components + * must already exist. Only the leaf component will be removed. + * OUTPUTS: + * None + * RETURN: PRStatus + * If successful, will return a status of PR_SUCCESS. Otherwise + * a value of PR_FAILURE. The reason for the failure may be re- + * trieved using PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_RmDir(const char *name); + +/* + ************************************************************************* + * FUNCTION: PR_NewUDPSocket + * DESCRIPTION: + * Create a new UDP socket. + * INPUTS: + * None + * OUTPUTS: + * None + * 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 creation of a new UDP socket failed. + * + ************************************************************************** + */ + +NSPR_API(PRFileDesc*) PR_NewUDPSocket(void); + +/* + ************************************************************************* + * FUNCTION: PR_NewTCPSocket + * DESCRIPTION: + * Create a new TCP socket. + * INPUTS: + * None + * 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. + * + ************************************************************************** + */ + +NSPR_API(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. + * + ************************************************************************** + */ + +NSPR_API(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. + * + ************************************************************************** + */ + +NSPR_API(PRFileDesc*) PR_OpenTCPSocket(PRIntn af); + +/* + ************************************************************************* + * FUNCTION: PR_Connect + * DESCRIPTION: + * Initiate a connection on a socket. + * INPUTS: + * PRFileDesc *fd + * Points to a PRFileDesc object representing a socket + * PRNetAddr *addr + * Specifies the address of the socket in its own communication + * space. + * PRIntervalTime timeout + * The function uses the lesser of the provided timeout and + * the OS's connect timeout. In particular, if you specify + * PR_INTERVAL_NO_TIMEOUT as the timeout, the OS's connection + * time limit will be used. + * + * OUTPUTS: + * None + * RETURN: PRStatus + * Upon successful completion of connection initiation, PR_Connect + * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further + * failure information can be obtained by calling PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_Connect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout); + +/* + ************************************************************************* + * FUNCTION: PR_ConnectContinue + * DESCRIPTION: + * Continue a nonblocking connect. After a nonblocking connect + * is initiated with PR_Connect() (which fails with + * PR_IN_PROGRESS_ERROR), one should call PR_Poll() on the socket, + * with the in_flags PR_POLL_WRITE | PR_POLL_EXCEPT. When + * PR_Poll() returns, one calls PR_ConnectContinue() on the + * socket to determine whether the nonblocking connect has + * completed or is still in progress. Repeat the PR_Poll(), + * PR_ConnectContinue() sequence until the nonblocking connect + * has completed. + * INPUTS: + * PRFileDesc *fd + * the file descriptor representing a socket + * PRInt16 out_flags + * the out_flags field of the poll descriptor returned by + * PR_Poll() + * RETURN: PRStatus + * If the nonblocking connect has successfully completed, + * PR_ConnectContinue returns PR_SUCCESS. If PR_ConnectContinue() + * returns PR_FAILURE, call PR_GetError(): + * - PR_IN_PROGRESS_ERROR: the nonblocking connect is still in + * progress and has not completed yet. The caller should poll + * on the file descriptor for the in_flags + * PR_POLL_WRITE|PR_POLL_EXCEPT and retry PR_ConnectContinue + * later when PR_Poll() returns. + * - Other errors: the nonblocking connect has failed with this + * error code. + */ + +NSPR_API(PRStatus) PR_ConnectContinue(PRFileDesc *fd, PRInt16 out_flags); + +/* + ************************************************************************* + * THIS FUNCTION IS DEPRECATED. USE PR_ConnectContinue INSTEAD. + * + * FUNCTION: PR_GetConnectStatus + * DESCRIPTION: + * Get the completion status of a nonblocking connect. After + * a nonblocking connect is initiated with PR_Connect() (which + * fails with PR_IN_PROGRESS_ERROR), one should call PR_Poll() + * on the socket, with the in_flags PR_POLL_WRITE | PR_POLL_EXCEPT. + * When PR_Poll() returns, one calls PR_GetConnectStatus on the + * PRPollDesc structure to determine whether the nonblocking + * connect has succeeded or failed. + * INPUTS: + * const PRPollDesc *pd + * Pointer to a PRPollDesc whose fd member is the socket, + * and in_flags must contain PR_POLL_WRITE and PR_POLL_EXCEPT. + * PR_Poll() should have been called and set the out_flags. + * RETURN: PRStatus + * If the nonblocking connect has successfully completed, + * PR_GetConnectStatus returns PR_SUCCESS. If PR_GetConnectStatus() + * returns PR_FAILURE, call PR_GetError(): + * - PR_IN_PROGRESS_ERROR: the nonblocking connect is still in + * progress and has not completed yet. + * - Other errors: the nonblocking connect has failed with this + * error code. + */ + +NSPR_API(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd); + +/* + ************************************************************************* + * FUNCTION: PR_Accept + * DESCRIPTION: + * Accept a connection on a socket. + * INPUTS: + * PRFileDesc *fd + * Points to a PRFileDesc object representing the rendezvous socket + * on which the caller is willing to accept new connections. + * PRIntervalTime timeout + * Time limit for completion of the accept operation. + * OUTPUTS: + * PRNetAddr *addr + * Returns the address of the connecting entity in its own + * communication space. It may be NULL. + * RETURN: PRFileDesc* + * Upon successful acceptance of a connection, PR_Accept + * returns a valid file descriptor. Otherwise, it returns NULL. + * Further failure information can be obtained by calling PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRFileDesc*) PR_Accept( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout); + +/* + ************************************************************************* + * FUNCTION: PR_Bind + * DESCRIPTION: + * Bind an address to a socket. + * INPUTS: + * PRFileDesc *fd + * Points to a PRFileDesc object representing a socket. + * PRNetAddr *addr + * Specifies the address to which the socket will be bound. + * OUTPUTS: + * None + * RETURN: PRStatus + * Upon successful binding of an address to a socket, PR_Bind + * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further + * failure information can be obtained by calling PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr); + +/* + ************************************************************************* + * FUNCTION: PR_Listen + * DESCRIPTION: + * Listen for connections on a socket. + * INPUTS: + * PRFileDesc *fd + * Points to a PRFileDesc object representing a socket that will be + * used to listen for new connections. + * PRIntn backlog + * Specifies the maximum length of the queue of pending connections. + * OUTPUTS: + * None + * RETURN: PRStatus + * Upon successful completion of listen request, PR_Listen + * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further + * failure information can be obtained by calling PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog); + +/* + ************************************************************************* + * FUNCTION: PR_Shutdown + * DESCRIPTION: + * Shut down part of a full-duplex connection on a socket. + * INPUTS: + * PRFileDesc *fd + * Points to a PRFileDesc object representing a connected socket. + * PRIntn how + * Specifies the kind of disallowed operations on the socket. + * PR_SHUTDOWN_RCV - Further receives will be disallowed + * PR_SHUTDOWN_SEND - Further sends will be disallowed + * PR_SHUTDOWN_BOTH - Further sends and receives will be disallowed + * OUTPUTS: + * None + * RETURN: PRStatus + * Upon successful completion of shutdown request, PR_Shutdown + * returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further + * failure information can be obtained by calling PR_GetError(). + ************************************************************************** + */ + +typedef enum PRShutdownHow +{ + PR_SHUTDOWN_RCV = 0, /* disallow further receives */ + PR_SHUTDOWN_SEND = 1, /* disallow further sends */ + PR_SHUTDOWN_BOTH = 2 /* disallow further receives and sends */ +} PRShutdownHow; + +NSPR_API(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how); + +/* + ************************************************************************* + * FUNCTION: PR_Recv + * DESCRIPTION: + * Receive a specified number of bytes from a connected socket. + * The operation will block until some positive number of bytes are + * transferred, a time out has occurred, or there is an error. + * No more than 'amount' bytes will be transferred. + * INPUTS: + * PRFileDesc *fd + * points to a PRFileDesc object representing a socket. + * void *buf + * pointer to a buffer to hold the data received. + * PRInt32 amount + * the size of 'buf' (in bytes) + * PRIntn flags + * must be zero or PR_MSG_PEEK. + * PRIntervalTime timeout + * Time limit for completion of the receive operation. + * OUTPUTS: + * None + * RETURN: PRInt32 + * a positive number indicates the number of bytes actually received. + * 0 means the network connection is closed. + * -1 indicates a failure. The reason for the failure is obtained + * by calling PR_GetError(). + ************************************************************************** + */ + +#define PR_MSG_PEEK 0x2 + +NSPR_API(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); + +/* + ************************************************************************* + * FUNCTION: PR_Send + * DESCRIPTION: + * Send a specified number of bytes from a connected socket. + * The operation will block until all bytes are + * processed, a time out has occurred, or there is an error. + * INPUTS: + * PRFileDesc *fd + * points to a PRFileDesc object representing a socket. + * void *buf + * pointer to a buffer from where the data is sent. + * PRInt32 amount + * the size of 'buf' (in bytes) + * PRIntn flags + * (OBSOLETE - must always be zero) + * PRIntervalTime timeout + * Time limit for completion of the send operation. + * OUTPUTS: + * None + * RETURN: PRInt32 + * A positive number indicates the number of bytes successfully processed. + * This number must always equal 'amount'. A -1 is an indication that the + * operation failed. The reason for the failure is obtained by calling + * PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); + +/* + ************************************************************************* + * FUNCTION: PR_RecvFrom + * DESCRIPTION: + * Receive up to a specified number of bytes from socket which may + * or may not be connected. + * The operation will block until one or more bytes are + * transferred, a time out has occurred, or there is an error. + * No more than 'amount' bytes will be transferred. + * INPUTS: + * PRFileDesc *fd + * points to a PRFileDesc object representing a socket. + * void *buf + * pointer to a buffer to hold the data received. + * PRInt32 amount + * the size of 'buf' (in bytes) + * PRIntn flags + * (OBSOLETE - must always be zero) + * PRNetAddr *addr + * Specifies the address of the sending peer. It may be NULL. + * PRIntervalTime timeout + * Time limit for completion of the receive operation. + * OUTPUTS: + * None + * RETURN: PRInt32 + * a positive number indicates the number of bytes actually received. + * 0 means the network connection is closed. + * -1 indicates a failure. The reason for the failure is obtained + * by calling PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRInt32) PR_RecvFrom( + PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRNetAddr *addr, PRIntervalTime timeout); + +/* + ************************************************************************* + * FUNCTION: PR_SendTo + * DESCRIPTION: + * Send a specified number of bytes from an unconnected socket. + * The operation will block until all bytes are + * sent, a time out has occurred, or there is an error. + * INPUTS: + * PRFileDesc *fd + * points to a PRFileDesc object representing an unconnected socket. + * void *buf + * pointer to a buffer from where the data is sent. + * PRInt32 amount + * the size of 'buf' (in bytes) + * PRIntn flags + * (OBSOLETE - must always be zero) + * PRNetAddr *addr + * Specifies the address of the peer. +.* PRIntervalTime timeout + * Time limit for completion of the send operation. + * OUTPUTS: + * None + * RETURN: PRInt32 + * A positive number indicates the number of bytes successfully sent. + * -1 indicates a failure. The reason for the failure is obtained + * by calling PR_GetError(). + ************************************************************************** + */ + +NSPR_API(PRInt32) PR_SendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRIntervalTime timeout); + +/* +************************************************************************* +** FUNCTION: PR_TransmitFile +** DESCRIPTION: +** Transmitfile sends a complete file (sourceFile) across a socket +** (networkSocket). If headers is non-NULL, the headers will be sent across +** the socket prior to sending the file. +** +** Optionally, the PR_TRANSMITFILE_CLOSE_SOCKET flag may be passed to +** transmitfile. This flag specifies that transmitfile should close the +** socket after sending the data. +** +** INPUTS: +** PRFileDesc *networkSocket +** The socket to send data over +** PRFileDesc *sourceFile +** The file to send +** const void *headers +** A pointer to headers to be sent before sending data +** PRInt32 hlen +** length of header buffers in bytes. +** 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 transmit 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(). +************************************************************************** +*/ + +NSPR_API(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(). +************************************************************************** +*/ + +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 */ +}; + + +NSPR_API(PRInt32) PR_SendFile( + PRFileDesc *networkSocket, PRSendFileData *sendData, + PRTransmitFileFlags flags, PRIntervalTime timeout); + +/* +************************************************************************* +** FUNCTION: PR_AcceptRead +** DESCRIPTION: +** AcceptRead accepts a new connection, returns the newly created +** socket's descriptor and also returns the connecting peer's address. +** AcceptRead, as its name suggests, also receives the first block of data +** sent by the peer. +** +** INPUTS: +** PRFileDesc *listenSock +** A socket descriptor that has been called with the PR_Listen() +** function, also known as the rendezvous socket. +** void *buf +** A pointer to a buffer to receive data sent by the client. This +** buffer must be large enough to receive <amount> bytes of data +** and two PRNetAddr structures, plus an extra 32 bytes. See: +** PR_ACCEPT_READ_BUF_OVERHEAD. +** PRInt32 amount +** The number of bytes of client data to receive. Does not include +** the size of the PRNetAddr structures. If 0, no data will be read +** from the client. +** PRIntervalTime timeout +** The timeout interval only applies to the read portion of the +** operation. PR_AcceptRead will block indefinitely until the +** connection is accepted; the read will timeout after the timeout +** interval elapses. +** OUTPUTS: +** PRFileDesc **acceptedSock +** The file descriptor for the newly connected socket. This parameter +** will only be valid if the function return does not indicate failure. +** PRNetAddr **peerAddr, +** The address of the remote socket. This parameter will only be +** valid if the function return does not indicate failure. The +** returned address is not guaranteed to be properly aligned. +** +** RETURNS: +** The number of bytes read from the client or -1 on failure. The reason +** for the failure is obtained by calling PR_GetError(). +************************************************************************** +**/ +/* define buffer overhead constant. Add this value to the user's +** data length when allocating a buffer to accept data. +** Example: +** #define USER_DATA_SIZE 10 +** char buf[USER_DATA_SIZE + PR_ACCEPT_READ_BUF_OVERHEAD]; +** bytesRead = PR_AcceptRead( s, fd, &a, &p, USER_DATA_SIZE, ...); +*/ +#define PR_ACCEPT_READ_BUF_OVERHEAD (32+(2*sizeof(PRNetAddr))) + +NSPR_API(PRInt32) PR_AcceptRead( + PRFileDesc *listenSock, PRFileDesc **acceptedSock, + PRNetAddr **peerAddr, void *buf, PRInt32 amount, PRIntervalTime timeout); + +/* +************************************************************************* +** FUNCTION: PR_NewTCPSocketPair +** DESCRIPTION: +** Create a new TCP socket pair. The returned descriptors can be used +** interchangeably; they are interconnected full-duplex descriptors: data +** written to one can be read from the other and vice-versa. +** +** INPUTS: +** None +** OUTPUTS: +** PRFileDesc *fds[2] +** The file descriptor pair for the newly created TCP sockets. +** RETURN: PRStatus +** Upon successful completion of TCP socket pair, PR_NewTCPSocketPair +** returns PR_SUCCESS. Otherwise, it returns PR_FAILURE. Further +** failure information can be obtained by calling PR_GetError(). +** XXX can we implement this on windoze and mac? +************************************************************************** +**/ +NSPR_API(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2]); + +/* +************************************************************************* +** FUNCTION: PR_GetSockName +** DESCRIPTION: +** Get socket name. Return the network address for this socket. +** +** INPUTS: +** PRFileDesc *fd +** Points to a PRFileDesc object representing the socket. +** OUTPUTS: +** PRNetAddr *addr +** Returns the address of the socket in its own communication space. +** RETURN: PRStatus +** Upon successful completion, PR_GetSockName returns PR_SUCCESS. +** Otherwise, it returns PR_FAILURE. Further failure information can +** be obtained by calling PR_GetError(). +************************************************************************** +**/ +NSPR_API(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr); + +/* +************************************************************************* +** FUNCTION: PR_GetPeerName +** DESCRIPTION: +** Get name of the connected peer. Return the network address for the +** connected peer socket. +** +** INPUTS: +** PRFileDesc *fd +** Points to a PRFileDesc object representing the connected peer. +** OUTPUTS: +** PRNetAddr *addr +** Returns the address of the connected peer in its own communication +** space. +** RETURN: PRStatus +** Upon successful completion, PR_GetPeerName returns PR_SUCCESS. +** Otherwise, it returns PR_FAILURE. Further failure information can +** be obtained by calling PR_GetError(). +************************************************************************** +**/ +NSPR_API(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr); + +NSPR_API(PRStatus) PR_GetSocketOption( + PRFileDesc *fd, PRSocketOptionData *data); + +NSPR_API(PRStatus) PR_SetSocketOption( + PRFileDesc *fd, const PRSocketOptionData *data); + +/* + ********************************************************************* + * + * File descriptor inheritance + * + ********************************************************************* + */ + +/* + ************************************************************************ + * FUNCTION: PR_SetFDInheritable + * DESCRIPTION: + * Set the inheritance attribute of a file descriptor. + * + * INPUTS: + * PRFileDesc *fd + * Points to a PRFileDesc object. + * PRBool inheritable + * If PR_TRUE, the file descriptor fd is set to be inheritable + * by a child process. If PR_FALSE, the file descriptor is set + * to be not inheritable by a child process. + * RETURN: PRStatus + * Upon successful completion, PR_SetFDInheritable returns PR_SUCCESS. + * Otherwise, it returns PR_FAILURE. Further failure information can + * be obtained by calling PR_GetError(). + ************************************************************************* + */ +NSPR_API(PRStatus) PR_SetFDInheritable( + PRFileDesc *fd, + PRBool inheritable); + +/* + ************************************************************************ + * FUNCTION: PR_GetInheritedFD + * DESCRIPTION: + * Get an inherited file descriptor with the specified name. + * + * INPUTS: + * const char *name + * The name of the inherited file descriptor. + * RETURN: PRFileDesc * + * Upon successful completion, PR_GetInheritedFD returns the + * inherited file descriptor with the specified name. Otherwise, + * it returns NULL. Further failure information can be obtained + * by calling PR_GetError(). + ************************************************************************* + */ +NSPR_API(PRFileDesc *) PR_GetInheritedFD(const char *name); + +/* + ********************************************************************* + * + * Memory-mapped files + * + ********************************************************************* + */ + +typedef struct PRFileMap PRFileMap; + +/* + * protection options for read and write accesses of a file mapping + */ +typedef enum PRFileMapProtect { + PR_PROT_READONLY, /* read only */ + PR_PROT_READWRITE, /* readable, and write is shared */ + PR_PROT_WRITECOPY /* readable, and write is private (copy-on-write) */ +} PRFileMapProtect; + +NSPR_API(PRFileMap *) PR_CreateFileMap( + PRFileDesc *fd, + PRInt64 size, + PRFileMapProtect prot); + +/* + * return the alignment (in bytes) of the offset argument to PR_MemMap + */ +NSPR_API(PRInt32) PR_GetMemMapAlignment(void); + +NSPR_API(void *) PR_MemMap( + PRFileMap *fmap, + PROffset64 offset, /* must be aligned and sized according to the + * return value of PR_GetMemMapAlignment() */ + PRUint32 len); + +NSPR_API(PRStatus) PR_MemUnmap(void *addr, PRUint32 len); + +NSPR_API(PRStatus) PR_CloseFileMap(PRFileMap *fmap); + +/* + * Synchronously flush the given memory-mapped address range of the given open + * file to disk. The function does not return until all modified data have + * been written to disk. + * + * On some platforms, the function will call PR_Sync(fd) internally if it is + * necessary for flushing modified data to disk synchronously. + */ +NSPR_API(PRStatus) PR_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len); + +/* + ****************************************************************** + * + * Interprocess communication + * + ****************************************************************** + */ + +/* + * Creates an anonymous pipe and returns file descriptors for the + * read and write ends of the pipe. + */ + +NSPR_API(PRStatus) PR_CreatePipe( + PRFileDesc **readPipe, + PRFileDesc **writePipe +); + +/************************************************************************/ +/************** The following definitions are for poll ******************/ +/************************************************************************/ + +struct PRPollDesc { + PRFileDesc* fd; + PRInt16 in_flags; + PRInt16 out_flags; +}; + +/* +** Bit values for PRPollDesc.in_flags or PRPollDesc.out_flags. Binary-or +** these together to produce the desired poll request. +*/ + +#if defined(_PR_POLL_BACKCOMPAT) + +#include <poll.h> +#define PR_POLL_READ POLLIN +#define PR_POLL_WRITE POLLOUT +#define PR_POLL_EXCEPT POLLPRI +#define PR_POLL_ERR POLLERR /* only in out_flags */ +#define PR_POLL_NVAL POLLNVAL /* only in out_flags when fd is bad */ +#define PR_POLL_HUP POLLHUP /* only in out_flags */ + +#else /* _PR_POLL_BACKCOMPAT */ + +#define PR_POLL_READ 0x1 +#define PR_POLL_WRITE 0x2 +#define PR_POLL_EXCEPT 0x4 +#define PR_POLL_ERR 0x8 /* only in out_flags */ +#define PR_POLL_NVAL 0x10 /* only in out_flags when fd is bad */ +#define PR_POLL_HUP 0x20 /* only in out_flags */ + +#endif /* _PR_POLL_BACKCOMPAT */ + +/* +************************************************************************* +** FUNCTION: PR_Poll +** DESCRIPTION: +** +** The call returns as soon as I/O is ready on one or more of the underlying +** socket objects. A count of the number of ready descriptors is +** returned unless a timeout occurs in which case zero is returned. +** +** PRPollDesc.fd should be set to a pointer to a PRFileDesc object +** representing a socket. This field can be set to NULL to indicate to +** PR_Poll that this PRFileDesc object should be ignored. +** PRPollDesc.in_flags should be set to the desired request +** (read/write/except or some combination). Upon successful return from +** this call PRPollDesc.out_flags will be set to indicate what kind of +** i/o can be performed on the respective descriptor. PR_Poll() uses the +** out_flags fields as scratch variables during the call. If PR_Poll() +** returns 0 or -1, the out_flags fields do not contain meaningful values +** and must not be used. +** +** INPUTS: +** PRPollDesc *pds A pointer to an array of PRPollDesc +** +** PRIntn npds The number of elements in the array +** If this argument is zero PR_Poll is +** equivalent to a PR_Sleep(timeout). +** +** PRIntervalTime timeout Amount of time the call will block waiting +** for I/O to become ready. If this time expires +** w/o any I/O becoming ready, the result will +** be zero. +** +** OUTPUTS: None +** RETURN: +** PRInt32 Number of PRPollDesc's with events or zero +** if the function timed out or -1 on failure. +** The reason for the failure is obtained by +** calling PR_GetError(). +************************************************************************** +*/ +NSPR_API(PRInt32) PR_Poll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout); + +/* +************************************************************************** +** +** Pollable events +** +** A pollable event is a special kind of file descriptor. +** The only I/O operation you can perform on a pollable event +** is to poll it with the PR_POLL_READ flag. You can't +** read from or write to a pollable event. +** +** The purpose of a pollable event is to combine event waiting +** with I/O waiting in a single PR_Poll call. Pollable events +** are implemented using a pipe or a pair of TCP sockets +** connected via the loopback address, therefore setting and +** waiting for pollable events are expensive operating system +** calls. Do not use pollable events for general thread +** synchronization. Use condition variables instead. +** +** A pollable event has two states: set and unset. Events +** are not queued, so there is no notion of an event count. +** A pollable event is either set or unset. +** +** A new pollable event is created by a PR_NewPollableEvent +** call and is initially in the unset state. +** +** PR_WaitForPollableEvent blocks the calling thread until +** the pollable event is set, and then it atomically unsets +** the pollable event before it returns. +** +** To set a pollable event, call PR_SetPollableEvent. +** +** One can call PR_Poll with the PR_POLL_READ flag on a pollable +** event. When the pollable event is set, PR_Poll returns with +** the PR_POLL_READ flag set in the out_flags. +** +** To close a pollable event, call PR_DestroyPollableEvent +** (not PR_Close). +** +************************************************************************** +*/ + +NSPR_API(PRFileDesc *) PR_NewPollableEvent(void); + +NSPR_API(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event); + +NSPR_API(PRStatus) PR_SetPollableEvent(PRFileDesc *event); + +NSPR_API(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event); + +PR_END_EXTERN_C + +#endif /* prio_h___ */ diff --git a/nsprpub/pr/include/pripcsem.h b/nsprpub/pr/include/pripcsem.h new file mode 100644 index 0000000000..f5a524da1a --- /dev/null +++ b/nsprpub/pr/include/pripcsem.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * 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" +#include "prio.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 */ + +NSPR_API(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. + */ + +NSPR_API(PRStatus) PR_WaitSemaphore(PRSem *sem); + +/* + * PR_PostSemaphore -- + * + * Increment the value of the named semaphore by 1. + */ + +NSPR_API(PRStatus) PR_PostSemaphore(PRSem *sem); + +/* + * PR_CloseSemaphore -- + * + * Close a named semaphore handle. + */ + +NSPR_API(PRStatus) PR_CloseSemaphore(PRSem *sem); + +/* + * PR_DeleteSemaphore -- + * + * Remove a named semaphore from the system. + */ + +NSPR_API(PRStatus) PR_DeleteSemaphore(const char *name); + +PR_END_EXTERN_C + +#endif /* pripcsem_h___ */ diff --git a/nsprpub/pr/include/private/Makefile.in b/nsprpub/pr/include/private/Makefile.in new file mode 100644 index 0000000000..db0c2f1e24 --- /dev/null +++ b/nsprpub/pr/include/private/Makefile.in @@ -0,0 +1,29 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +RELEASE_HEADERS = pprio.h pprthred.h prpriv.h +RELEASE_HEADERS := $(addprefix $(srcdir)/, $(RELEASE_HEADERS)) +RELEASE_HEADERS_DEST = $(RELEASE_INCLUDE_DIR)/private + +HEADERS = $(RELEASE_HEADERS) $(srcdir)/pprmwait.h $(srcdir)/primpl.h + +include_subdir = private + +include $(topsrcdir)/config/rules.mk + +export:: $(RELEASE_HEADERS) + $(INSTALL) -m 444 $(RELEASE_HEADERS) $(dist_includedir)/private diff --git a/nsprpub/pr/include/private/pprio.h b/nsprpub/pr/include/private/pprio.h new file mode 100644 index 0000000000..be8252b887 --- /dev/null +++ b/nsprpub/pr/include/private/pprio.h @@ -0,0 +1,242 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: pprio.h +** +** Description: Private definitions for I/O related structures +*/ + +#ifndef pprio_h___ +#define pprio_h___ + +#include "prtypes.h" +#include "prio.h" + +PR_BEGIN_EXTERN_C + +/************************************************************************/ +/************************************************************************/ + +#ifdef _WIN64 +typedef __int64 PROsfd; +#else +typedef PRInt32 PROsfd; +#endif + +/* Return the method tables for files, tcp sockets and udp sockets */ +NSPR_API(const PRIOMethods*) PR_GetFileMethods(void); +NSPR_API(const PRIOMethods*) PR_GetTCPMethods(void); +NSPR_API(const PRIOMethods*) PR_GetUDPMethods(void); +NSPR_API(const PRIOMethods*) PR_GetPipeMethods(void); + +/* +** Convert a NSPR socket handle to a native socket handle. +** +** Using this function makes your code depend on the properties of the +** current NSPR implementation, which may change (although extremely +** unlikely because of NSPR's backward compatibility requirement). Avoid +** using it if you can. +** +** If you use this function, you need to understand what NSPR does to +** the native handle. For example, NSPR puts native socket handles in +** non-blocking mode or associates them with an I/O completion port (the +** WINNT build configuration only). Your use of the native handle should +** not interfere with NSPR's use of the native handle. If your code +** changes the configuration of the native handle, (e.g., changes it to +** blocking or closes it), NSPR will not work correctly. +*/ +NSPR_API(PROsfd) PR_FileDesc2NativeHandle(PRFileDesc *); +NSPR_API(void) PR_ChangeFileDescNativeHandle(PRFileDesc *, PROsfd); +NSPR_API(PRFileDesc*) PR_AllocFileDesc(PROsfd osfd, + const PRIOMethods *methods); +NSPR_API(void) PR_FreeFileDesc(PRFileDesc *fd); +/* +** Import an existing OS file to NSPR. +*/ +NSPR_API(PRFileDesc*) PR_ImportFile(PROsfd osfd); +NSPR_API(PRFileDesc*) PR_ImportPipe(PROsfd osfd); +NSPR_API(PRFileDesc*) PR_ImportTCPSocket(PROsfd osfd); +NSPR_API(PRFileDesc*) PR_ImportUDPSocket(PROsfd osfd); + + +/* + ************************************************************************* + * FUNCTION: PR_CreateSocketPollFd + * DESCRIPTION: + * Create a PRFileDesc wrapper for a native socket handle, for use with + * PR_Poll only + * INPUTS: + * None + * OUTPUTS: + * None + * RETURN: PRFileDesc* + * Upon successful completion, PR_CreateSocketPollFd returns a pointer + * to the PRFileDesc created for the native socket handle + * Returns a NULL pointer if the create of a new PRFileDesc failed + * + ************************************************************************** + */ + +NSPR_API(PRFileDesc*) PR_CreateSocketPollFd(PROsfd osfd); + +/* + ************************************************************************* + * FUNCTION: PR_DestroySocketPollFd + * DESCRIPTION: + * Destroy the PRFileDesc wrapper created by PR_CreateSocketPollFd + * INPUTS: + * None + * OUTPUTS: + * None + * RETURN: PRFileDesc* + * Upon successful completion, PR_DestroySocketPollFd returns + * PR_SUCCESS, else PR_FAILURE + * + ************************************************************************** + */ + +NSPR_API(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd); + + +/* +** Macros for PR_Socket +** +** Socket types: PR_SOCK_STREAM, PR_SOCK_DGRAM +*/ + +#ifdef WIN32 + +#define PR_SOCK_STREAM 1 +#define PR_SOCK_DGRAM 2 + +#else /* WIN32 */ + +#define PR_SOCK_STREAM SOCK_STREAM +#define PR_SOCK_DGRAM SOCK_DGRAM + +#endif /* WIN32 */ + +/* +** Create a new Socket; this function is obsolete. +*/ +NSPR_API(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto); + +/* FUNCTION: PR_LockFile +** DESCRIPTION: +** Lock a file for exclusive access. +** RETURNS: +** PR_SUCCESS when the lock is held +** PR_FAILURE otherwise +*/ +NSPR_API(PRStatus) PR_LockFile(PRFileDesc *fd); + +/* FUNCTION: PR_TLockFile +** DESCRIPTION: +** Test and Lock a file for exclusive access. Do not block if the +** file cannot be locked immediately. +** RETURNS: +** PR_SUCCESS when the lock is held +** PR_FAILURE otherwise +*/ +NSPR_API(PRStatus) PR_TLockFile(PRFileDesc *fd); + +/* FUNCTION: PR_UnlockFile +** DESCRIPTION: +** Unlock a file which has been previously locked successfully by this +** process. +** RETURNS: +** PR_SUCCESS when the lock is released +** PR_FAILURE otherwise +*/ +NSPR_API(PRStatus) PR_UnlockFile(PRFileDesc *fd); + +/* +** Emulate acceptread by accept and recv. +*/ +NSPR_API(PRInt32) PR_EmulateAcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout); + +/* +** Emulate sendfile by reading from the file and writing to the socket. +** The file is memory-mapped if memory-mapped files are supported. +*/ +NSPR_API(PRInt32) PR_EmulateSendFile( + PRFileDesc *networkSocket, PRSendFileData *sendData, + PRTransmitFileFlags flags, PRIntervalTime timeout); + +#ifdef WIN32 +/* FUNCTION: PR_NTFast_AcceptRead +** DESCRIPTION: +** NT has the notion of an "accept context", which is only needed in +** order to make certain calls. By default, a socket connected via +** AcceptEx can only do a limited number of things without updating +** the acceptcontext. The generic version of PR_AcceptRead always +** updates the accept context. This version does not. +**/ +NSPR_API(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime t); + +typedef void (*_PR_AcceptTimeoutCallback)(void *); + +/* FUNCTION: PR_NTFast_AcceptRead_WithTimeoutCallback +** DESCRIPTION: +** The AcceptEx call combines the accept with the read function. However, +** our daemon threads need to be able to wakeup and reliably flush their +** log buffers if the Accept times out. However, with the current blocking +** interface to AcceptRead, there is no way for us to timeout the Accept; +** this is because when we timeout the Read, we can close the newly +** socket and continue; but when we timeout the accept itself, there is no +** new socket to timeout. So instead, this version of the function is +** provided. After the initial timeout period elapses on the accept() +** portion of the function, it will call the callback routine and then +** continue the accept. If the timeout occurs on the read, it will +** close the connection and return error. +*/ +NSPR_API(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback( + PRFileDesc *sd, + PRFileDesc **nd, + PRNetAddr **raddr, + void *buf, + PRInt32 amount, + PRIntervalTime t, + _PR_AcceptTimeoutCallback callback, + void *callback_arg); + +/* FUNCTION: PR_NTFast_Accept +** DESCRIPTION: +** NT has the notion of an "accept context", which is only needed in +** order to make certain calls. By default, a socket connected via +** AcceptEx can only do a limited number of things without updating +** the acceptcontext. The generic version of PR_Accept always +** updates the accept context. This version does not. +**/ +NSPR_API(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr, + PRIntervalTime timeout); + +/* FUNCTION: PR_NTFast_Update +** DESCRIPTION: +** For sockets accepted with PR_NTFast_Accept or PR_NTFastAcceptRead, +** this function will update the accept context for those sockets, +** so that the socket can make general purpose socket calls. +** Without calling this, the only operations supported on the socket +** Are PR_Read, PR_Write, PR_Transmitfile, and PR_Close. +*/ +NSPR_API(void) PR_NTFast_UpdateAcceptContext(PRFileDesc *acceptSock, + PRFileDesc *listenSock); + + +/* FUNCTION: PR_NT_CancelIo +** DESCRIPTION: +** Cancel IO operations on fd. +*/ +NSPR_API(PRStatus) PR_NT_CancelIo(PRFileDesc *fd); + + +#endif /* WIN32 */ + +PR_END_EXTERN_C + +#endif /* pprio_h___ */ diff --git a/nsprpub/pr/include/private/pprmwait.h b/nsprpub/pr/include/private/pprmwait.h new file mode 100644 index 0000000000..3e4057c5f4 --- /dev/null +++ b/nsprpub/pr/include/private/pprmwait.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(_PPRMWAIT_H) +#else +#define _PPRMWAIT_H + +#include "prlock.h" +#include "prcvar.h" +#include "prclist.h" +#include "prthread.h" + +#define MAX_POLLING_INTERVAL 100 +#define _PR_POLL_COUNT_FUDGE 64 +#define _PR_DEFAULT_HASH_LENGTH 59 + +/* + * Our hash table resolves collisions by open addressing with + * double hashing. See Cormen, Leiserson, and Rivest, + * Introduction to Algorithms, p. 232, The MIT Press, 1990. + */ + +#define _MW_HASH(a, m) ((((PRUptrdiff)(a) >> 4) ^ ((PRUptrdiff)(a) >> 10)) % (m)) +#define _MW_HASH2(a, m) (1 + ((((PRUptrdiff)(a) >> 4) ^ ((PRUptrdiff)(a) >> 10)) % (m - 2))) +#define _MW_ABORTED(_rv) \ + ((PR_FAILURE == (_rv)) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) + +typedef enum {_prmw_success, _prmw_rehash, _prmw_error} _PR_HashStory; + +typedef struct _PRWaiterHash +{ + PRUint16 count; /* current number in hash table */ + PRUint16 length; /* current size of the hash table */ + PRRecvWait *recv_wait; /* hash table of receive wait objects */ +} _PRWaiterHash; + +typedef enum {_prmw_running, _prmw_stopping, _prmw_stopped} PRMWGroupState; + +struct PRWaitGroup +{ + PRCList group_link; /* all groups are linked to each other */ + PRCList io_ready; /* list of I/O requests that are ready */ + PRMWGroupState state; /* state of this group (so we can shut down) */ + + PRLock *ml; /* lock for synchronizing this wait group */ + PRCondVar *io_taken; /* calling threads notify when they take I/O */ + PRCondVar *io_complete; /* calling threads wait here for completions */ + PRCondVar *new_business; /* polling thread waits here more work */ + PRCondVar *mw_manage; /* used to manage group lists */ + PRThread* poller; /* thread that's actually doing the poll() */ + PRUint16 waiting_threads; /* number of threads waiting for recv */ + PRUint16 polling_count; /* number of elements in the polling list */ + PRUint32 p_timestamp; /* pseudo-time group had element removed */ + PRPollDesc *polling_list; /* list poller builds for polling */ + PRIntervalTime last_poll; /* last time we polled */ + _PRWaiterHash *waiter; /* pointer to hash table of wait receive objects */ + +#ifdef WINNT + /* + * On NT, idle threads are responsible for getting completed i/o. + * They need to add completed i/o to the io_ready list. Since + * idle threads cannot use nspr locks, we have to use an md lock + * to protect the io_ready list. + */ + _MDLock mdlock; /* protect io_ready, waiter, and wait_list */ + PRCList wait_list; /* used in place of io_complete. reuse + * waitQLinks in the PRThread structure. */ +#endif /* WINNT */ +}; + +/********************************************************************** +*********************************************************************** +******************** Wait group enumerations ************************** +*********************************************************************** +**********************************************************************/ +typedef struct _PRGlobalState +{ + PRCList group_list; /* master of the group list */ + PRWaitGroup *group; /* the default (NULL) group */ +} _PRGlobalState; + +#ifdef WINNT +extern PRStatus NT_HashRemoveInternal(PRWaitGroup *group, PRFileDesc *fd); +#endif + +typedef enum {_PR_ENUM_UNSEALED=0, _PR_ENUM_SEALED=0x0eadface} _PREnumSeal; + +struct PRMWaitEnumerator +{ + PRWaitGroup *group; /* group this enumerator is bound to */ + PRThread *thread; /* thread in midst of an enumeration */ + _PREnumSeal seal; /* trying to detect deleted objects */ + PRUint32 p_timestamp; /* when enumeration was (re)started */ + PRRecvWait **waiter; /* pointer into hash table */ + PRUintn index; /* position in hash table */ + void *pad[4]; /* some room to grow */ +}; + +#endif /* defined(_PPRMWAIT_H) */ + +/* pprmwait.h */ diff --git a/nsprpub/pr/include/private/pprthred.h b/nsprpub/pr/include/private/pprthred.h new file mode 100644 index 0000000000..9b7c3c1995 --- /dev/null +++ b/nsprpub/pr/include/private/pprthred.h @@ -0,0 +1,311 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef pprthred_h___ +#define pprthred_h___ + +/* +** API for PR private functions. These calls are to be used by internal +** developers only. +*/ +#include "nspr.h" + +#if defined(XP_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_WIN +#include <os2.h> +#endif + +PR_BEGIN_EXTERN_C + +/*--------------------------------------------------------------------------- +** THREAD PRIVATE FUNCTIONS +---------------------------------------------------------------------------*/ + +/* +** Associate a thread object with an existing native thread. +** "type" is the type of thread object to attach +** "priority" is the priority to assign to the thread +** "stack" defines the shape of the threads stack +** +** This can return NULL if some kind of error occurs, or if memory is +** tight. This call invokes "start(obj,arg)" and returns when the +** function returns. The thread object is automatically destroyed. +** +** This call is not normally needed unless you create your own native +** thread. PR_Init does this automatically for the primordial thread. +*/ +NSPR_API(PRThread*) PR_AttachThread(PRThreadType type, + PRThreadPriority priority, + PRThreadStack *stack); + +/* +** Detach the nspr thread from the currently executing native thread. +** The thread object will be destroyed and all related data attached +** to it. The exit procs will be invoked. +** +** This call is not normally needed unless you create your own native +** thread. PR_Exit will automatially detach the nspr thread object +** created by PR_Init for the primordial thread. +** +** This call returns after the nspr thread object is destroyed. +*/ +NSPR_API(void) PR_DetachThread(void); + +/* +** Get the id of the named thread. Each thread is assigned a unique id +** when it is created or attached. +*/ +NSPR_API(PRUint32) PR_GetThreadID(PRThread *thread); + +/* +** Set the procedure that is called when a thread is dumped. The procedure +** will be applied to the argument, arg, when called. Setting the procedure +** to NULL effectively removes it. +*/ +typedef void (*PRThreadDumpProc)(PRFileDesc *fd, PRThread *t, void *arg); +NSPR_API(void) PR_SetThreadDumpProc( + PRThread* thread, PRThreadDumpProc dump, void *arg); + +/* +** Get this thread's affinity mask. The affinity mask is a 32 bit quantity +** marking a bit for each processor this process is allowed to run on. +** The processor mask is returned in the mask argument. +** The least-significant-bit represents processor 0. +** +** Returns 0 on success, -1 on failure. +*/ +NSPR_API(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask); + +/* +** Set this thread's affinity mask. +** +** Returns 0 on success, -1 on failure. +*/ +NSPR_API(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ); + +/* +** Set the default CPU Affinity mask. +** +*/ +NSPR_API(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask); + +/* +** Show status of all threads to standard error output. +*/ +NSPR_API(void) PR_ShowStatus(void); + +/* +** Set thread recycle mode to on (1) or off (0) +*/ +NSPR_API(void) PR_SetThreadRecycleMode(PRUint32 flag); + + +/*--------------------------------------------------------------------------- +** THREAD PRIVATE FUNCTIONS FOR GARBAGE COLLECTIBLE THREADS +---------------------------------------------------------------------------*/ + +/* +** Only Garbage collectible threads participate in resume all, suspend all and +** enumeration operations. They are also different during creation when +** platform specific action may be needed (For example, all Solaris GC able +** threads are bound threads). +*/ + +/* +** Same as PR_CreateThread except that the thread is marked as garbage +** collectible. +*/ +NSPR_API(PRThread*) PR_CreateThreadGCAble(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); + +/* +** Same as PR_AttachThread except that the thread being attached is marked as +** garbage collectible. +*/ +NSPR_API(PRThread*) PR_AttachThreadGCAble(PRThreadType type, + PRThreadPriority priority, + PRThreadStack *stack); + +/* +** Mark the thread as garbage collectible. +*/ +NSPR_API(void) PR_SetThreadGCAble(void); + +/* +** Unmark the thread as garbage collectible. +*/ +NSPR_API(void) PR_ClearThreadGCAble(void); + +/* +** This routine prevents all other GC able threads from running. This call is needed by +** the garbage collector. +*/ +NSPR_API(void) PR_SuspendAll(void); + +/* +** This routine unblocks all other GC able threads that were suspended from running by +** PR_SuspendAll(). This call is needed by the garbage collector. +*/ +NSPR_API(void) PR_ResumeAll(void); + +/* +** Return the thread stack pointer of the given thread. +** Needed by the garbage collector. +*/ +NSPR_API(void *) PR_GetSP(PRThread *thread); + +/* +** Save the registers that the GC would find interesting into the thread +** "t". isCurrent will be non-zero if the thread state that is being +** saved is the currently executing thread. Return the address of the +** first register to be scanned as well as the number of registers to +** scan in "np". +** +** If "isCurrent" is non-zero then it is allowed for the thread context +** area to be used as scratch storage to hold just the registers +** necessary for scanning. +** +** This function simply calls the internal function _MD_HomeGCRegisters(). +*/ +NSPR_API(PRWord *) PR_GetGCRegisters(PRThread *t, int isCurrent, int *np); + +/* +** (Get|Set)ExecutionEnvironent +** +** Used by Java to associate it's execution environment so garbage collector +** can find it. If return is NULL, then it's probably not a collectable thread. +** +** There's no locking required around these calls. +*/ +NSPR_API(void*) GetExecutionEnvironment(PRThread *thread); +NSPR_API(void) SetExecutionEnvironment(PRThread* thread, void *environment); + +/* +** Enumeration function that applies "func(thread,i,arg)" to each active +** thread in the process. The enumerator returns PR_SUCCESS if the enumeration +** should continue, any other value is considered failure, and enumeration +** stops, returning the failure value from PR_EnumerateThreads. +** Needed by the garbage collector. +*/ +typedef PRStatus (PR_CALLBACK *PREnumerator)(PRThread *t, int i, void *arg); +NSPR_API(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg); + +/* +** Signature of a thread stack scanning function. It is applied to every +** contiguous group of potential pointers within a thread. Count denotes the +** number of pointers. +*/ +typedef PRStatus +(PR_CALLBACK *PRScanStackFun)(PRThread* t, + void** baseAddr, PRUword count, void* closure); + +/* +** Applies scanFun to all contiguous groups of potential pointers +** within a thread. This includes the stack, registers, and thread-local +** data. If scanFun returns a status value other than PR_SUCCESS the scan +** is aborted, and the status value is returned. +*/ +NSPR_API(PRStatus) +PR_ThreadScanStackPointers(PRThread* t, + PRScanStackFun scanFun, void* scanClosure); + +/* +** Calls PR_ThreadScanStackPointers for every thread. +*/ +NSPR_API(PRStatus) +PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure); + +/* +** Returns a conservative estimate on the amount of stack space left +** on a thread in bytes, sufficient for making decisions about whether +** to continue recursing or not. +*/ +NSPR_API(PRUword) +PR_GetStackSpaceLeft(PRThread* t); + +/*--------------------------------------------------------------------------- +** THREAD CPU PRIVATE FUNCTIONS +---------------------------------------------------------------------------*/ + +/* +** Get a pointer to the primordial CPU. +*/ +NSPR_API(struct _PRCPU *) _PR_GetPrimordialCPU(void); + +/*--------------------------------------------------------------------------- +** THREAD SYNCHRONIZATION PRIVATE FUNCTIONS +---------------------------------------------------------------------------*/ + +/* +** Create a new named monitor (named for debugging purposes). +** Monitors are re-entrant locks with a built-in condition variable. +** +** This may fail if memory is tight or if some operating system resource +** is low. +*/ +NSPR_API(PRMonitor*) PR_NewNamedMonitor(const char* name); + +/* +** Test and then lock the lock if it's not already locked by some other +** thread. Return PR_FALSE if some other thread owned the lock at the +** time of the call. +*/ +NSPR_API(PRBool) PR_TestAndLock(PRLock *lock); + +/* +** Test and then enter the mutex associated with the monitor if it's not +** already entered by some other thread. Return PR_FALSE if some other +** thread owned the mutex at the time of the call. +*/ +NSPR_API(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon); + +/* +** Return the number of times that the current thread has entered the +** mutex. Returns zero if the current thread has not entered the mutex. +*/ +NSPR_API(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon); + +/* +** Just like PR_CEnterMonitor except that if the monitor is owned by +** another thread NULL is returned. +*/ +NSPR_API(PRMonitor*) PR_CTestAndEnterMonitor(void *address); + +/*--------------------------------------------------------------------------- +** PLATFORM-SPECIFIC INITIALIZATION FUNCTIONS +---------------------------------------------------------------------------*/ +#if defined(XP_OS2) +/* +** These functions need to be called at the start and end of a thread. +** An EXCEPTIONREGISTRATIONRECORD must be declared on the stack and its +** address passed to the two functions. +*/ +NSPR_API(void) PR_OS2_SetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* e); +NSPR_API(void) PR_OS2_UnsetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* e); +#endif /* XP_OS2 */ + +/* I think PR_GetMonitorEntryCount is useless. All you really want is this... */ +#define PR_InMonitor(m) (PR_GetMonitorEntryCount(m) > 0) + +/*--------------------------------------------------------------------------- +** Special X-Lock hack for client +---------------------------------------------------------------------------*/ + +#ifdef XP_UNIX +extern void _PR_XLock(void); +extern void _PR_XUnlock(void); +extern PRBool _PR_XIsLocked(void); +#endif /* XP_UNIX */ + +PR_END_EXTERN_C + +#endif /* pprthred_h___ */ diff --git a/nsprpub/pr/include/private/primpl.h b/nsprpub/pr/include/private/primpl.h new file mode 100644 index 0000000000..3f5a150470 --- /dev/null +++ b/nsprpub/pr/include/private/primpl.h @@ -0,0 +1,2149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef primpl_h___ +#define primpl_h___ + +#if defined(_PR_PTHREADS) +#include <pthread.h> +#endif + +#ifdef WIN32 +/* + * Allow use of functions and symbols first defined in Win2k. + */ +#if !defined(WINVER) || (WINVER < 0x0500) +#undef WINVER +#define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#endif /* WIN32 */ + +#include "nspr.h" +#include "prpriv.h" + +typedef struct PRSegment PRSegment; + +#include "md/prosdep.h" +#include "obsolete/probslet.h" + +#ifdef _PR_HAVE_POSIX_SEMAPHORES +#include <semaphore.h> +#elif defined(_PR_HAVE_SYSV_SEMAPHORES) +#include <sys/sem.h> +#endif + +#ifdef HAVE_SYSCALL +#include <sys/syscall.h> +#endif + +/************************************************************************* +***** A Word about Model Dependent Function Naming Convention *********** +*************************************************************************/ + +/* +NSPR 2.0 must implement its function across a range of platforms +including: MAC, Windows/16, Windows/95, Windows/NT, and several +variants of Unix. Each implementation shares common code as well +as having platform dependent portions. This standard describes how +the model dependent portions are to be implemented. + +In header file pr/include/primpl.h, each publicly declared +platform dependent function is declared as: + +NSPR_API void _PR_MD_FUNCTION( long arg1, long arg2 ); +#define _PR_MD_FUNCTION _MD_FUNCTION + +In header file pr/include/md/<platform>/_<platform>.h, +each #define'd macro is redefined as one of: + +#define _MD_FUNCTION <blanks> +#define _MD_FUNCTION <expanded macro> +#define _MD_FUNCTION <osFunction> +#define _MD_FUNCTION <_MD_Function> + +Where: + +<blanks> is no definition at all. In this case, the function is not implemented +and is never called for this platform. +For example: +#define _MD_INIT_CPUS() + +<expanded macro> is a C language macro expansion. +For example: +#define _MD_CLEAN_THREAD(_thread) \ + PR_BEGIN_MACRO \ + PR_DestroyCondVar(_thread->md.asyncIOCVar); \ + PR_DestroyLock(_thread->md.asyncIOLock); \ + PR_END_MACRO + +<osFunction> is some function implemented by the host operating system. +For example: +#define _MD_EXIT exit + +<_MD_function> is the name of a function implemented for this platform in +pr/src/md/<platform>/<soruce>.c file. +For example: +#define _MD_GETFILEINFO _MD_GetFileInfo + +In <source>.c, the implementation is: +PR_IMPLEMENT(PRInt32) _MD_GetFileInfo(const char *fn, PRFileInfo *info); +*/ + +PR_BEGIN_EXTERN_C + +typedef struct _MDLock _MDLock; +typedef struct _MDCVar _MDCVar; +typedef struct _MDSegment _MDSegment; +typedef struct _MDThread _MDThread; +typedef struct _MDThreadStack _MDThreadStack; +typedef struct _MDSemaphore _MDSemaphore; +typedef struct _MDDir _MDDir; +#ifdef MOZ_UNICODE +typedef struct _MDDirUTF16 _MDDirUTF16; +#endif /* MOZ_UNICODE */ +typedef struct _MDFileDesc _MDFileDesc; +typedef struct _MDProcess _MDProcess; +typedef struct _MDFileMap _MDFileMap; + +#if defined(_PR_PTHREADS) + +/* +** The following definitions are unique to implementing NSPR using pthreads. +** Since pthreads defines most of the thread and thread synchronization +** stuff, this is a pretty small set. +*/ + +#define PT_CV_NOTIFIED_LENGTH 6 +typedef struct _PT_Notified _PT_Notified; +struct _PT_Notified +{ + PRIntn length; /* # of used entries in this structure */ + struct + { + PRCondVar *cv; /* the condition variable notified */ + PRIntn times; /* and the number of times notified */ + } cv[PT_CV_NOTIFIED_LENGTH]; + _PT_Notified *link; /* link to another of these | NULL */ +}; + +/* + * bits defined for pthreads 'state' field + */ +#define PT_THREAD_DETACHED 0x01 /* thread can't be joined */ +#define PT_THREAD_GLOBAL 0x02 /* a global thread (unlikely) */ +#define PT_THREAD_SYSTEM 0x04 /* system (not user) thread */ +#define PT_THREAD_PRIMORD 0x08 /* this is the primordial thread */ +#define PT_THREAD_ABORTED 0x10 /* thread has been interrupted */ +#define PT_THREAD_GCABLE 0x20 /* thread is garbage collectible */ +#define PT_THREAD_SUSPENDED 0x40 /* thread has been suspended */ +#define PT_THREAD_FOREIGN 0x80 /* thread is not one of ours */ +#define PT_THREAD_BOUND 0x100 /* a bound-global thread */ + +#define _PT_THREAD_INTERRUPTED(thr) \ + (!(thr->interrupt_blocked) && (thr->state & PT_THREAD_ABORTED)) +#define _PT_THREAD_BLOCK_INTERRUPT(thr) \ + (thr->interrupt_blocked = 1) +#define _PT_THREAD_UNBLOCK_INTERRUPT(thr) \ + (thr->interrupt_blocked = 0) + +#define _PT_IS_GCABLE_THREAD(thr) ((thr)->state & PT_THREAD_GCABLE) + +/* +** Possible values for thread's suspend field +** Note that the first two can be the same as they are really mutually exclusive, +** i.e. both cannot be happening at the same time. We have two symbolic names +** just as a mnemonic. +**/ +#define PT_THREAD_RESUMED 0x80 /* thread has been resumed */ +#define PT_THREAD_SETGCABLE 0x100 /* set the GCAble flag */ + +#if defined(DEBUG) + +typedef struct PTDebug +{ + PRTime timeStarted; + PRUintn locks_created, locks_destroyed; + PRUintn locks_acquired, locks_released; + PRUintn cvars_created, cvars_destroyed; + PRUintn cvars_notified, delayed_cv_deletes; +} PTDebug; + +#endif /* defined(DEBUG) */ + +NSPR_API(void) PT_FPrintStats(PRFileDesc *fd, const char *msg); + +/* + * On Linux and its derivatives POSIX priority scheduling works only for + * real-time threads. On those platforms we set thread's nice values + * instead which requires us to track kernel thread IDs for each POSIX + * thread we create. + */ +#if defined(LINUX) && defined(HAVE_SETPRIORITY) && \ + ((defined(HAVE_SYSCALL) && defined(SYS_gettid)) || defined(HAVE_GETTID)) +#define _PR_NICE_PRIORITY_SCHEDULING +#endif + +#else /* defined(_PR_PTHREADS) */ + +NSPR_API(void) PT_FPrintStats(PRFileDesc *fd, const char *msg); + +/* +** This section is contains those parts needed to implement NSPR on +** platforms in general. One would assume that the pthreads implementation +** included lots of the same types, at least conceptually. +*/ + +/* + * Local threads only. No multiple CPU support and hence all the + * following routines are no-op. + */ +#ifdef _PR_LOCAL_THREADS_ONLY + +#define _PR_MD_SUSPEND_THREAD(thread) +#define _PR_MD_RESUME_THREAD(thread) +#define _PR_MD_SUSPEND_CPU(cpu) +#define _PR_MD_RESUME_CPU(cpu) +#define _PR_MD_BEGIN_SUSPEND_ALL() +#define _PR_MD_END_SUSPEND_ALL() +#define _PR_MD_BEGIN_RESUME_ALL() +#define _PR_MD_END_RESUME_ALL() +#define _PR_MD_INIT_ATTACHED_THREAD(thread) PR_FAILURE + +#endif + +typedef struct _PRCPUQueue _PRCPUQueue; +typedef struct _PRCPU _PRCPU; +typedef struct _MDCPU _MDCPU; + +struct _PRCPUQueue { + _MDLock runQLock; /* lock for the run + wait queues */ + _MDLock sleepQLock; /* lock for the run + wait queues */ + _MDLock miscQLock; /* lock for the run + wait queues */ + + PRCList runQ[PR_PRIORITY_LAST + 1]; /* run queue for this CPU */ + PRUint32 runQReadyMask; + PRCList sleepQ; + PRIntervalTime sleepQmax; + PRCList pauseQ; + PRCList suspendQ; + PRCList waitingToJoinQ; + + PRUintn numCPUs; /* number of CPUs using this Q */ +}; + +struct _PRCPU { + PRCList links; /* link list of CPUs */ + PRUint32 id; /* id for this CPU */ + + union { + PRInt32 bits; + PRUint8 missed[4]; + } u; + PRIntn where; /* index into u.missed */ + PRPackedBool paused; /* cpu is paused */ + PRPackedBool exit; /* cpu should exit */ + + PRThread *thread; /* native thread for this CPUThread */ + PRThread *idle_thread; /* user-level idle thread for this CPUThread */ + + PRIntervalTime last_clock; /* the last time we went into + * _PR_ClockInterrupt() on this CPU + */ + + _PRCPUQueue *queue; + + _MDCPU md; +}; + +typedef struct _PRInterruptTable { + const char *name; + PRUintn missed_bit; + void (*handler)(void); +} _PRInterruptTable; + +#define _PR_CPU_PTR(_qp) \ + ((_PRCPU*) ((char*) (_qp) - offsetof(_PRCPU,links))) + +#if !defined(WIN32) && !defined(XP_OS2) \ + && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)) +#define _MD_GET_ATTACHED_THREAD() (_PR_MD_CURRENT_THREAD()) +#endif + +#ifdef _PR_LOCAL_THREADS_ONLY + +NSPR_API(struct _PRCPU *) _pr_currentCPU; +NSPR_API(PRThread *) _pr_currentThread; +NSPR_API(PRThread *) _pr_lastThread; +NSPR_API(PRInt32) _pr_intsOff; + +#define _MD_CURRENT_CPU() (_pr_currentCPU) +#define _MD_SET_CURRENT_CPU(_cpu) (_pr_currentCPU = (_cpu)) +#define _MD_CURRENT_THREAD() (_pr_currentThread) +#define _MD_SET_CURRENT_THREAD(_thread) (_pr_currentThread = (_thread)) +#define _MD_LAST_THREAD() (_pr_lastThread) +#define _MD_SET_LAST_THREAD(t) (_pr_lastThread = t) + +#define _MD_GET_INTSOFF() (_pr_intsOff) +#define _MD_SET_INTSOFF(_val) (_pr_intsOff = _val) + + +/* The unbalanced curly braces in these two macros are intentional */ +#define _PR_LOCK_HEAP() { PRIntn _is; if (_pr_currentCPU) _PR_INTSOFF(_is); +#define _PR_UNLOCK_HEAP() if (_pr_currentCPU) _PR_INTSON(_is); } + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +extern PRInt32 _native_threads_only; + +#if defined(_PR_GLOBAL_THREADS_ONLY) + +#define _MD_GET_INTSOFF() 0 +#define _MD_SET_INTSOFF(_val) +#define _PR_INTSOFF(_is) +#define _PR_FAST_INTSON(_is) +#define _PR_INTSON(_is) +#define _PR_THREAD_LOCK(_thread) +#define _PR_THREAD_UNLOCK(_thread) +#define _PR_RUNQ_LOCK(cpu) +#define _PR_RUNQ_UNLOCK(cpu) +#define _PR_SLEEPQ_LOCK(thread) +#define _PR_SLEEPQ_UNLOCK(thread) +#define _PR_MISCQ_LOCK(thread) +#define _PR_MISCQ_UNLOCK(thread) +#define _PR_CPU_LIST_LOCK() +#define _PR_CPU_LIST_UNLOCK() + +#define _PR_ADD_RUNQ(_thread, _cpu, _pri) +#define _PR_DEL_RUNQ(_thread) +#define _PR_ADD_SLEEPQ(_thread, _timeout) +#define _PR_DEL_SLEEPQ(_thread, _propogate) +#define _PR_ADD_JOINQ(_thread, _cpu) +#define _PR_DEL_JOINQ(_thread) +#define _PR_ADD_SUSPENDQ(_thread, _cpu) +#define _PR_DEL_SUSPENDQ(_thread) + +#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU) + +#define _PR_IS_NATIVE_THREAD(thread) 1 +#define _PR_IS_NATIVE_THREAD_SUPPORTED() 1 + +#else + +#define _PR_INTSOFF(_is) \ + PR_BEGIN_MACRO \ + (_is) = _PR_MD_GET_INTSOFF(); \ + _PR_MD_SET_INTSOFF(1); \ + PR_END_MACRO + +#define _PR_FAST_INTSON(_is) \ + PR_BEGIN_MACRO \ + _PR_MD_SET_INTSOFF(_is); \ + PR_END_MACRO + +#define _PR_INTSON(_is) \ + PR_BEGIN_MACRO \ + if ((_is == 0) && (_PR_MD_CURRENT_CPU())->u.bits) \ + _PR_IntsOn((_PR_MD_CURRENT_CPU())); \ + _PR_MD_SET_INTSOFF(_is); \ + PR_END_MACRO + +#ifdef _PR_LOCAL_THREADS_ONLY + +#define _PR_IS_NATIVE_THREAD(thread) 0 +#define _PR_THREAD_LOCK(_thread) +#define _PR_THREAD_UNLOCK(_thread) +#define _PR_RUNQ_LOCK(cpu) +#define _PR_RUNQ_UNLOCK(cpu) +#define _PR_SLEEPQ_LOCK(thread) +#define _PR_SLEEPQ_UNLOCK(thread) +#define _PR_MISCQ_LOCK(thread) +#define _PR_MISCQ_UNLOCK(thread) +#define _PR_CPU_LIST_LOCK() +#define _PR_CPU_LIST_UNLOCK() + +#define _PR_ADD_RUNQ(_thread, _cpu, _pri) \ + PR_BEGIN_MACRO \ + PR_APPEND_LINK(&(_thread)->links, &_PR_RUNQ(_cpu)[_pri]); \ + _PR_RUNQREADYMASK(_cpu) |= (1L << _pri); \ + PR_END_MACRO + +#define _PR_DEL_RUNQ(_thread) \ + PR_BEGIN_MACRO \ + _PRCPU *_cpu = _thread->cpu; \ + PRInt32 _pri = _thread->priority; \ + PR_REMOVE_LINK(&(_thread)->links); \ + if (PR_CLIST_IS_EMPTY(&_PR_RUNQ(_cpu)[_pri])) \ + _PR_RUNQREADYMASK(_cpu) &= ~(1L << _pri); \ + PR_END_MACRO + +#define _PR_ADD_SLEEPQ(_thread, _timeout) \ + _PR_AddSleepQ(_thread, _timeout); + +#define _PR_DEL_SLEEPQ(_thread, _propogate) \ + _PR_DelSleepQ(_thread, _propogate); + +#define _PR_ADD_JOINQ(_thread, _cpu) \ + PR_APPEND_LINK(&(_thread)->links, &_PR_WAITINGTOJOINQ(_cpu)); + +#define _PR_DEL_JOINQ(_thread) \ + PR_REMOVE_LINK(&(_thread)->links); + +#define _PR_ADD_SUSPENDQ(_thread, _cpu) \ + PR_APPEND_LINK(&(_thread)->links, &_PR_SUSPENDQ(_cpu)); + +#define _PR_DEL_SUSPENDQ(_thread) \ + PR_REMOVE_LINK(&(_thread)->links); + +#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU) + +#define _PR_IS_NATIVE_THREAD_SUPPORTED() 0 + +#else /* _PR_LOCAL_THREADS_ONLY */ + +/* These are for the "combined" thread model */ + +#define _PR_THREAD_LOCK(_thread) \ + _PR_MD_LOCK(&(_thread)->threadLock); + +#define _PR_THREAD_UNLOCK(_thread) \ + _PR_MD_UNLOCK(&(_thread)->threadLock); + +#define _PR_RUNQ_LOCK(_cpu) \ + PR_BEGIN_MACRO \ + _PR_MD_LOCK(&(_cpu)->queue->runQLock );\ + PR_END_MACRO + +#define _PR_RUNQ_UNLOCK(_cpu) \ + PR_BEGIN_MACRO \ + _PR_MD_UNLOCK(&(_cpu)->queue->runQLock );\ + PR_END_MACRO + +#define _PR_SLEEPQ_LOCK(_cpu) \ + _PR_MD_LOCK(&(_cpu)->queue->sleepQLock ); + +#define _PR_SLEEPQ_UNLOCK(_cpu) \ + _PR_MD_UNLOCK(&(_cpu)->queue->sleepQLock ); + +#define _PR_MISCQ_LOCK(_cpu) \ + _PR_MD_LOCK(&(_cpu)->queue->miscQLock ); + +#define _PR_MISCQ_UNLOCK(_cpu) \ + _PR_MD_UNLOCK(&(_cpu)->queue->miscQLock ); + +#define _PR_CPU_LIST_LOCK() _PR_MD_LOCK(&_pr_cpuLock) +#define _PR_CPU_LIST_UNLOCK() _PR_MD_UNLOCK(&_pr_cpuLock) + +#define QUEUE_RUN 0x1 +#define QUEUE_SLEEP 0x2 +#define QUEUE_JOIN 0x4 +#define QUEUE_SUSPEND 0x8 +#define QUEUE_LOCK 0x10 + +#define _PR_ADD_RUNQ(_thread, _cpu, _pri) \ + PR_BEGIN_MACRO \ + PR_APPEND_LINK(&(_thread)->links, &_PR_RUNQ(_cpu)[_pri]); \ + _PR_RUNQREADYMASK(_cpu) |= (1L << _pri); \ + PR_ASSERT((_thread)->queueCount == 0); \ + (_thread)->queueCount = QUEUE_RUN; \ + PR_END_MACRO + +#define _PR_DEL_RUNQ(_thread) \ + PR_BEGIN_MACRO \ + _PRCPU *_cpu = _thread->cpu; \ + PRInt32 _pri = _thread->priority; \ + PR_REMOVE_LINK(&(_thread)->links); \ + if (PR_CLIST_IS_EMPTY(&_PR_RUNQ(_cpu)[_pri])) \ + _PR_RUNQREADYMASK(_cpu) &= ~(1L << _pri); \ + PR_ASSERT((_thread)->queueCount == QUEUE_RUN);\ + (_thread)->queueCount = 0; \ + PR_END_MACRO + +#define _PR_ADD_SLEEPQ(_thread, _timeout) \ + PR_ASSERT((_thread)->queueCount == 0); \ + (_thread)->queueCount = QUEUE_SLEEP; \ + _PR_AddSleepQ(_thread, _timeout); + +#define _PR_DEL_SLEEPQ(_thread, _propogate) \ + PR_ASSERT((_thread)->queueCount == QUEUE_SLEEP);\ + (_thread)->queueCount = 0; \ + _PR_DelSleepQ(_thread, _propogate); + +#define _PR_ADD_JOINQ(_thread, _cpu) \ + PR_ASSERT((_thread)->queueCount == 0); \ + (_thread)->queueCount = QUEUE_JOIN; \ + PR_APPEND_LINK(&(_thread)->links, &_PR_WAITINGTOJOINQ(_cpu)); + +#define _PR_DEL_JOINQ(_thread) \ + PR_ASSERT((_thread)->queueCount == QUEUE_JOIN);\ + (_thread)->queueCount = 0; \ + PR_REMOVE_LINK(&(_thread)->links); + +#define _PR_ADD_SUSPENDQ(_thread, _cpu) \ + PR_ASSERT((_thread)->queueCount == 0); \ + (_thread)->queueCount = QUEUE_SUSPEND; \ + PR_APPEND_LINK(&(_thread)->links, &_PR_SUSPENDQ(_cpu)); + +#define _PR_DEL_SUSPENDQ(_thread) \ + PR_ASSERT((_thread)->queueCount == QUEUE_SUSPEND);\ + (_thread)->queueCount = 0; \ + PR_REMOVE_LINK(&(_thread)->links); + +#define _PR_THREAD_SWITCH_CPU(_thread, _newCPU) \ + (_thread)->cpu = (_newCPU); + +#define _PR_IS_NATIVE_THREAD(thread) (thread->flags & _PR_GLOBAL_SCOPE) +#define _PR_IS_NATIVE_THREAD_SUPPORTED() 1 + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +#endif /* _PR_GLOBAL_THREADS_ONLY */ + +#define _PR_SET_RESCHED_FLAG() _PR_MD_CURRENT_CPU()->u.missed[3] = 1 +#define _PR_CLEAR_RESCHED_FLAG() _PR_MD_CURRENT_CPU()->u.missed[3] = 0 + +extern _PRInterruptTable _pr_interruptTable[]; + +/* Bits for _pr_interruptState.u.missed[0,1] */ +#define _PR_MISSED_CLOCK 0x1 +#define _PR_MISSED_IO 0x2 +#define _PR_MISSED_CHILD 0x4 + +extern void _PR_IntsOn(_PRCPU *cpu); + +NSPR_API(void) _PR_WakeupCPU(void); +NSPR_API(void) _PR_PauseCPU(void); + +/************************************************************************/ + +#define _PR_LOCK_LOCK(_lock) \ + _PR_MD_LOCK(&(_lock)->ilock); +#define _PR_LOCK_UNLOCK(_lock) \ + _PR_MD_UNLOCK(&(_lock)->ilock); + +extern void _PR_UnblockLockWaiter(PRLock *lock); +extern PRStatus _PR_InitLock(PRLock *lock); +extern void _PR_FreeLock(PRLock *lock); + +#define _PR_LOCK_PTR(_qp) \ + ((PRLock*) ((char*) (_qp) - offsetof(PRLock,links))) + +/************************************************************************/ + +#define _PR_CVAR_LOCK(_cvar) \ + _PR_MD_LOCK(&(_cvar)->ilock); +#define _PR_CVAR_UNLOCK(_cvar) \ + _PR_MD_UNLOCK(&(_cvar)->ilock); + +extern PRStatus _PR_InitCondVar(PRCondVar *cvar, PRLock *lock); +extern void _PR_FreeCondVar(PRCondVar *cvar); +extern PRStatus _PR_WaitCondVar( + PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout); +extern void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me); +extern PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen); + +NSPR_API(void) _PR_Notify(PRMonitor *mon, PRBool all, PRBool sticky); + +/* PRThread.flags */ +#define _PR_SYSTEM 0x01 +#define _PR_INTERRUPT 0x02 +#define _PR_ATTACHED 0x04 /* created via PR_AttachThread */ +#define _PR_PRIMORDIAL 0x08 /* the thread that called PR_Init */ +#define _PR_ON_SLEEPQ 0x10 /* thread is on the sleepQ */ +#define _PR_ON_PAUSEQ 0x20 /* thread is on the pauseQ */ +#define _PR_SUSPENDING 0x40 /* thread wants to suspend */ +#define _PR_GLOBAL_SCOPE 0x80 /* thread is global scope */ +#define _PR_IDLE_THREAD 0x200 /* this is an idle thread */ +#define _PR_GCABLE_THREAD 0x400 /* this is a collectable thread */ +#define _PR_BOUND_THREAD 0x800 /* a bound thread */ +#define _PR_INTERRUPT_BLOCKED 0x1000 /* interrupts blocked */ + +/* PRThread.state */ +#define _PR_UNBORN 0 +#define _PR_RUNNABLE 1 +#define _PR_RUNNING 2 +#define _PR_LOCK_WAIT 3 +#define _PR_COND_WAIT 4 +#define _PR_JOIN_WAIT 5 +#define _PR_IO_WAIT 6 +#define _PR_SUSPENDED 7 +#define _PR_DEAD_STATE 8 /* for debugging */ + +/* PRThreadStack.flags */ +#define _PR_STACK_VM 0x1 /* using vm instead of malloc */ +#define _PR_STACK_MAPPED 0x2 /* vm is mapped */ +#define _PR_STACK_PRIMORDIAL 0x4 /* stack for primordial thread */ + +/* +** If the default stcksize from the client is zero, we need to pick a machine +** dependent value. This is only for standard user threads. For custom threads, +** 0 has a special meaning. +** Adjust stackSize. Round up to a page boundary. +*/ + +#ifndef _MD_MINIMUM_STACK_SIZE +#define _MD_MINIMUM_STACK_SIZE 0 +#endif + +#if (!defined(HAVE_CUSTOM_USER_THREADS)) +#define _PR_ADJUST_STACKSIZE(stackSize) \ + PR_BEGIN_MACRO \ + if (stackSize == 0) \ + stackSize = _MD_DEFAULT_STACK_SIZE; \ + if (stackSize < _MD_MINIMUM_STACK_SIZE) \ + stackSize = _MD_MINIMUM_STACK_SIZE; \ + stackSize = (stackSize + (1 << _pr_pageShift) - 1) >> _pr_pageShift; \ + stackSize <<= _pr_pageShift; \ + PR_END_MACRO +#else +#define _PR_ADJUST_STACKSIZE(stackSize) +#endif + +#define _PR_IS_GCABLE_THREAD(thr) ((thr)->flags & _PR_GCABLE_THREAD) + +#define _PR_PENDING_INTERRUPT(thr) \ + (!((thr)->flags & _PR_INTERRUPT_BLOCKED) && ((thr)->flags & _PR_INTERRUPT)) +#define _PR_THREAD_BLOCK_INTERRUPT(thr) \ + (thr->flags |= _PR_INTERRUPT_BLOCKED) +#define _PR_THREAD_UNBLOCK_INTERRUPT(thr) \ + (thr->flags &= ~_PR_INTERRUPT_BLOCKED) + +#define _PR_THREAD_PTR(_qp) \ + ((PRThread*) ((char*) (_qp) - offsetof(PRThread,links))) + +#define _PR_ACTIVE_THREAD_PTR(_qp) \ + ((PRThread*) ((char*) (_qp) - offsetof(PRThread,active))) + +#define _PR_THREAD_CONDQ_PTR(_qp) \ + ((PRThread*) ((char*) (_qp) - offsetof(PRThread,waitQLinks))) + +#define _PR_THREAD_MD_TO_PTR(_md) \ + ((PRThread*) ((char*) (_md) - offsetof(PRThread,md))) + +#define _PR_THREAD_STACK_TO_PTR(_stack) \ + ((PRThread*) (_stack->thr)) + +extern PRCList _pr_active_local_threadQ; +extern PRCList _pr_active_global_threadQ; +extern PRCList _pr_cpuQ; +extern _MDLock _pr_cpuLock; +extern PRInt32 _pr_md_idle_cpus; + +#define _PR_ACTIVE_LOCAL_THREADQ() _pr_active_local_threadQ +#define _PR_ACTIVE_GLOBAL_THREADQ() _pr_active_global_threadQ +#define _PR_CPUQ() _pr_cpuQ +#define _PR_RUNQ(_cpu) ((_cpu)->queue->runQ) +#define _PR_RUNQREADYMASK(_cpu) ((_cpu)->queue->runQReadyMask) +#define _PR_SLEEPQ(_cpu) ((_cpu)->queue->sleepQ) +#define _PR_SLEEPQMAX(_cpu) ((_cpu)->queue->sleepQmax) +#define _PR_PAUSEQ(_cpu) ((_cpu)->queue->pauseQ) +#define _PR_SUSPENDQ(_cpu) ((_cpu)->queue->suspendQ) +#define _PR_WAITINGTOJOINQ(_cpu) ((_cpu)->queue->waitingToJoinQ) + +extern PRUint32 _pr_recycleThreads; /* Flag for behavior on thread cleanup */ +extern PRLock *_pr_deadQLock; +extern PRUint32 _pr_numNativeDead; +extern PRUint32 _pr_numUserDead; +extern PRCList _pr_deadNativeQ; +extern PRCList _pr_deadUserQ; +#define _PR_DEADNATIVEQ _pr_deadNativeQ +#define _PR_DEADUSERQ _pr_deadUserQ +#define _PR_DEADQ_LOCK PR_Lock(_pr_deadQLock); +#define _PR_DEADQ_UNLOCK PR_Unlock(_pr_deadQLock); +#define _PR_INC_DEADNATIVE (_pr_numNativeDead++) +#define _PR_DEC_DEADNATIVE (_pr_numNativeDead--) +#define _PR_NUM_DEADNATIVE (_pr_numNativeDead) +#define _PR_INC_DEADUSER (_pr_numUserDead++) +#define _PR_DEC_DEADUSER (_pr_numUserDead--) +#define _PR_NUM_DEADUSER (_pr_numUserDead) + +extern PRUint32 _pr_utid; + +extern struct _PRCPU *_pr_primordialCPU; + +extern PRLock *_pr_activeLock; /* lock for userActive and systemActive */ +extern PRInt32 _pr_userActive; /* number of active user threads */ +extern PRInt32 _pr_systemActive; /* number of active system threads */ +extern PRInt32 _pr_primordialExitCount; /* number of user threads left + * before the primordial thread + * can exit. */ +extern PRCondVar *_pr_primordialExitCVar; /* the condition variable for + * notifying the primordial thread + * when all other user threads + * have terminated. */ + +extern PRUintn _pr_maxPTDs; + +extern PRLock *_pr_terminationCVLock; + +/************************************************************************* +* Internal routines either called by PR itself or from machine-dependent * +* code. * +*************************************************************************/ + +extern void _PR_ClockInterrupt(void); + +extern void _PR_Schedule(void); +extern void _PR_SetThreadPriority( + PRThread* thread, PRThreadPriority priority); + +/*********************************************************************** +** FUNCTION: _PR_NewSegment() +** DESCRIPTION: +** Allocate a memory segment. The "size" value is rounded up to the +** native system page size and a page aligned portion of memory is +** returned. This memory is not part of the malloc heap. If "vaddr" is +** not NULL then PR tries to allocate the segment at the desired virtual +** address. +** INPUTS: size: size of the desired memory segment +** vaddr: address at which the newly aquired segment is to be +** mapped into memory. +** OUTPUTS: a memory segment is allocated, a PRSegment is allocated +** RETURN: pointer to PRSegment +***********************************************************************/ +extern PRSegment* _PR_NewSegment(PRUint32 size, void *vaddr); + +/*********************************************************************** +** FUNCTION: _PR_DestroySegment() +** DESCRIPTION: +** The memory segment and the PRSegment are freed +** INPUTS: seg: pointer to PRSegment to be freed +** OUTPUTS: the the PRSegment and its associated memory segment are freed +** RETURN: void +***********************************************************************/ +extern void _PR_DestroySegment(PRSegment *seg); + +extern PRThreadStack * _PR_NewStack(PRUint32 stackSize); +extern void _PR_FreeStack(PRThreadStack *stack); +extern PRBool _PR_NotifyThread (PRThread *thread, PRThread *me); +extern void _PR_NotifyLockedThread (PRThread *thread); + +NSPR_API(void) _PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout); +NSPR_API(void) _PR_DelSleepQ(PRThread *thread, PRBool propogate_time); + +extern void _PR_AddThreadToRunQ(PRThread *me, PRThread *thread); + +NSPR_API(PRThread*) _PR_CreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, + PRUint32 flags); + +extern void _PR_NativeDestroyThread(PRThread *thread); +extern void _PR_UserDestroyThread(PRThread *thread); + +extern PRThread* _PRI_AttachThread( + PRThreadType type, PRThreadPriority priority, + PRThreadStack *stack, PRUint32 flags); + +extern void _PRI_DetachThread(void); + + +#define _PR_IO_PENDING(_thread) ((_thread)->io_pending) + +NSPR_API(void) _PR_MD_INIT_CPUS(); +#define _PR_MD_INIT_CPUS _MD_INIT_CPUS + +NSPR_API(void) _PR_MD_WAKEUP_CPUS(); +#define _PR_MD_WAKEUP_CPUS _MD_WAKEUP_CPUS + +/* Interrupts related */ + +NSPR_API(void) _PR_MD_START_INTERRUPTS(void); +#define _PR_MD_START_INTERRUPTS _MD_START_INTERRUPTS + +NSPR_API(void) _PR_MD_STOP_INTERRUPTS(void); +#define _PR_MD_STOP_INTERRUPTS _MD_STOP_INTERRUPTS + +NSPR_API(void) _PR_MD_ENABLE_CLOCK_INTERRUPTS(void); +#define _PR_MD_ENABLE_CLOCK_INTERRUPTS _MD_ENABLE_CLOCK_INTERRUPTS + +NSPR_API(void) _PR_MD_DISABLE_CLOCK_INTERRUPTS(void); +#define _PR_MD_DISABLE_CLOCK_INTERRUPTS _MD_DISABLE_CLOCK_INTERRUPTS + +NSPR_API(void) _PR_MD_BLOCK_CLOCK_INTERRUPTS(void); +#define _PR_MD_BLOCK_CLOCK_INTERRUPTS _MD_BLOCK_CLOCK_INTERRUPTS + +NSPR_API(void) _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(void); +#define _PR_MD_UNBLOCK_CLOCK_INTERRUPTS _MD_UNBLOCK_CLOCK_INTERRUPTS + +/* The _PR_MD_WAIT_LOCK and _PR_MD_WAKEUP_WAITER functions put to sleep and + * awaken a thread which is waiting on a lock or cvar. + */ +extern PRStatus _PR_MD_WAIT(PRThread *, PRIntervalTime timeout); +#define _PR_MD_WAIT _MD_WAIT + +extern PRStatus _PR_MD_WAKEUP_WAITER(PRThread *); +#define _PR_MD_WAKEUP_WAITER _MD_WAKEUP_WAITER + +#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */ +NSPR_API(void) _PR_MD_CLOCK_INTERRUPT(void); +#define _PR_MD_CLOCK_INTERRUPT _MD_CLOCK_INTERRUPT +#endif + +/* Stack debugging */ +NSPR_API(void) _PR_MD_INIT_STACK(PRThreadStack *ts, PRIntn redzone); +#define _PR_MD_INIT_STACK _MD_INIT_STACK + +NSPR_API(void) _PR_MD_CLEAR_STACK(PRThreadStack* ts); +#define _PR_MD_CLEAR_STACK _MD_CLEAR_STACK + +/* CPU related */ +NSPR_API(PRInt32) _PR_MD_GET_INTSOFF(void); +#define _PR_MD_GET_INTSOFF _MD_GET_INTSOFF + +NSPR_API(void) _PR_MD_SET_INTSOFF(PRInt32 _val); +#define _PR_MD_SET_INTSOFF _MD_SET_INTSOFF + +NSPR_API(_PRCPU*) _PR_MD_CURRENT_CPU(void); +#define _PR_MD_CURRENT_CPU _MD_CURRENT_CPU + +NSPR_API(void) _PR_MD_SET_CURRENT_CPU(_PRCPU *cpu); +#define _PR_MD_SET_CURRENT_CPU _MD_SET_CURRENT_CPU + +NSPR_API(void) _PR_MD_INIT_RUNNING_CPU(_PRCPU *cpu); +#define _PR_MD_INIT_RUNNING_CPU _MD_INIT_RUNNING_CPU + +/* + * Returns the number of threads awoken or 0 if a timeout occurred; + */ +extern PRInt32 _PR_MD_PAUSE_CPU(PRIntervalTime timeout); +#define _PR_MD_PAUSE_CPU _MD_PAUSE_CPU + +extern void _PR_MD_CLEANUP_BEFORE_EXIT(void); +#define _PR_MD_CLEANUP_BEFORE_EXIT _MD_CLEANUP_BEFORE_EXIT + +extern void _PR_MD_EXIT(PRIntn status); +#define _PR_MD_EXIT _MD_EXIT + +/* Locks related */ + +NSPR_API(void) _PR_MD_INIT_LOCKS(void); +#define _PR_MD_INIT_LOCKS _MD_INIT_LOCKS + +NSPR_API(PRStatus) _PR_MD_NEW_LOCK(_MDLock *md); +#define _PR_MD_NEW_LOCK _MD_NEW_LOCK + +NSPR_API(void) _PR_MD_FREE_LOCK(_MDLock *md); +#define _PR_MD_FREE_LOCK _MD_FREE_LOCK + +NSPR_API(void) _PR_MD_LOCK(_MDLock *md); +#define _PR_MD_LOCK _MD_LOCK + +/* Return 0 on success, a nonzero value on failure. */ +NSPR_API(PRIntn) _PR_MD_TEST_AND_LOCK(_MDLock *md); +#define _PR_MD_TEST_AND_LOCK _MD_TEST_AND_LOCK + +NSPR_API(void) _PR_MD_UNLOCK(_MDLock *md); +#define _PR_MD_UNLOCK _MD_UNLOCK + +NSPR_API(void) _PR_MD_IOQ_LOCK(void); +#define _PR_MD_IOQ_LOCK _MD_IOQ_LOCK + +NSPR_API(void) _PR_MD_IOQ_UNLOCK(void); +#define _PR_MD_IOQ_UNLOCK _MD_IOQ_UNLOCK + +#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */ +/* Semaphore related -- only for native threads */ +#ifdef HAVE_CVAR_BUILT_ON_SEM +NSPR_API(void) _PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value); +#define _PR_MD_NEW_SEM _MD_NEW_SEM + +NSPR_API(void) _PR_MD_DESTROY_SEM(_MDSemaphore *md); +#define _PR_MD_DESTROY_SEM _MD_DESTROY_SEM + +NSPR_API(PRStatus) _PR_MD_TIMED_WAIT_SEM( + _MDSemaphore *md, PRIntervalTime timeout); +#define _PR_MD_TIMED_WAIT_SEM _MD_TIMED_WAIT_SEM + +NSPR_API(PRStatus) _PR_MD_WAIT_SEM(_MDSemaphore *md); +#define _PR_MD_WAIT_SEM _MD_WAIT_SEM + +NSPR_API(void) _PR_MD_POST_SEM(_MDSemaphore *md); +#define _PR_MD_POST_SEM _MD_POST_SEM +#endif /* HAVE_CVAR_BUILT_ON_SEM */ + +#endif + +/* Condition Variables related -- only for native threads */ + +#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */ +NSPR_API(PRInt32) _PR_MD_NEW_CV(_MDCVar *md); +#define _PR_MD_NEW_CV _MD_NEW_CV + +NSPR_API(void) _PR_MD_FREE_CV(_MDCVar *md); +#define _PR_MD_FREE_CV _MD_FREE_CV + +NSPR_API(void) _PR_MD_WAIT_CV( + _MDCVar *mdCVar,_MDLock *mdLock,PRIntervalTime timeout); +#define _PR_MD_WAIT_CV _MD_WAIT_CV + +NSPR_API(void) _PR_MD_NOTIFY_CV(_MDCVar *md, _MDLock *lock); +#define _PR_MD_NOTIFY_CV _MD_NOTIFY_CV + +NSPR_API(void) _PR_MD_NOTIFYALL_CV(_MDCVar *md, _MDLock *lock); +#define _PR_MD_NOTIFYALL_CV _MD_NOTIFYALL_CV +#endif /* _PR_LOCAL_THREADS_ONLY */ + +/* Threads related */ +NSPR_API(PRThread*) _PR_MD_CURRENT_THREAD(void); +#define _PR_MD_CURRENT_THREAD _MD_CURRENT_THREAD + +NSPR_API(PRThread*) _PR_MD_GET_ATTACHED_THREAD(void); +#define _PR_MD_GET_ATTACHED_THREAD _MD_GET_ATTACHED_THREAD + +NSPR_API(PRThread*) _PR_MD_LAST_THREAD(void); +#define _PR_MD_LAST_THREAD _MD_LAST_THREAD + +NSPR_API(void) _PR_MD_SET_CURRENT_THREAD(PRThread *thread); +#define _PR_MD_SET_CURRENT_THREAD _MD_SET_CURRENT_THREAD + +NSPR_API(void) _PR_MD_SET_LAST_THREAD(PRThread *thread); +#define _PR_MD_SET_LAST_THREAD _MD_SET_LAST_THREAD + +extern PRStatus _PR_MD_INIT_THREAD(PRThread *thread); +#define _PR_MD_INIT_THREAD _MD_INIT_THREAD + +extern void _PR_MD_EXIT_THREAD(PRThread *thread); +#define _PR_MD_EXIT_THREAD _MD_EXIT_THREAD + +#ifndef _PR_LOCAL_THREADS_ONLY /* not if only local threads supported */ + +NSPR_API(PRStatus) _PR_MD_INIT_ATTACHED_THREAD(PRThread *thread); +#define _PR_MD_INIT_ATTACHED_THREAD _MD_INIT_ATTACHED_THREAD + +extern void _PR_MD_SUSPEND_THREAD(PRThread *thread); +#define _PR_MD_SUSPEND_THREAD _MD_SUSPEND_THREAD + +extern void _PR_MD_RESUME_THREAD(PRThread *thread); +#define _PR_MD_RESUME_THREAD _MD_RESUME_THREAD + +extern void _PR_MD_SUSPEND_CPU(_PRCPU *cpu); +#define _PR_MD_SUSPEND_CPU _MD_SUSPEND_CPU + +extern void _PR_MD_RESUME_CPU(_PRCPU *cpu); +#define _PR_MD_RESUME_CPU _MD_RESUME_CPU + +extern void _PR_MD_BEGIN_SUSPEND_ALL(void); +#define _PR_MD_BEGIN_SUSPEND_ALL _MD_BEGIN_SUSPEND_ALL + +extern void _PR_MD_END_SUSPEND_ALL(void); +#define _PR_MD_END_SUSPEND_ALL _MD_END_SUSPEND_ALL + +extern void _PR_MD_BEGIN_RESUME_ALL(void); +#define _PR_MD_BEGIN_RESUME_ALL _MD_BEGIN_RESUME_ALL + +extern void _PR_MD_END_RESUME_ALL(void); +#define _PR_MD_END_RESUME_ALL _MD_END_RESUME_ALL + +#endif /* !_PR_LOCAL_THREADS_ONLY */ + +extern void _PR_MD_CLEAN_THREAD(PRThread *thread); +#define _PR_MD_CLEAN_THREAD _MD_CLEAN_THREAD + +#ifdef HAVE_CUSTOM_USER_THREADS +extern void _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *); +#define _PR_MD_CREATE_PRIMORDIAL_USER_THREAD _MD_CREATE_PRIMORDIAL_USER_THREAD + +extern PRThread* _PR_MD_CREATE_USER_THREAD( + PRUint32 stacksize, + void (*start)(void *), + void *arg); +#define _PR_MD_CREATE_USER_THREAD _MD_CREATE_USER_THREAD +#endif + +extern PRStatus _PR_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); +#define _PR_MD_CREATE_THREAD _MD_CREATE_THREAD + +extern void _PR_MD_JOIN_THREAD(_MDThread *md); +#define _PR_MD_JOIN_THREAD _MD_JOIN_THREAD + +extern void _PR_MD_END_THREAD(void); +#define _PR_MD_END_THREAD _MD_END_THREAD + +extern void _PR_MD_YIELD(void); +#define _PR_MD_YIELD _MD_YIELD + +extern void _PR_MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri); +#define _PR_MD_SET_PRIORITY _MD_SET_PRIORITY + +extern void _PR_MD_SET_CURRENT_THREAD_NAME(const char *name); +#define _PR_MD_SET_CURRENT_THREAD_NAME _MD_SET_CURRENT_THREAD_NAME + +NSPR_API(void) _PR_MD_SUSPENDALL(void); +#define _PR_MD_SUSPENDALL _MD_SUSPENDALL + +NSPR_API(void) _PR_MD_RESUMEALL(void); +#define _PR_MD_RESUMEALL _MD_RESUMEALL + +extern void _PR_MD_INIT_CONTEXT( + PRThread *thread, char *top, void (*start) (void), PRBool *status); +#define _PR_MD_INIT_CONTEXT _MD_INIT_CONTEXT + +extern void _PR_MD_SWITCH_CONTEXT(PRThread *thread); +#define _PR_MD_SWITCH_CONTEXT _MD_SWITCH_CONTEXT + +extern void _PR_MD_RESTORE_CONTEXT(PRThread *thread); +#define _PR_MD_RESTORE_CONTEXT _MD_RESTORE_CONTEXT + +/* Segment related */ +extern void _PR_MD_INIT_SEGS(void); +#define _PR_MD_INIT_SEGS _MD_INIT_SEGS + +extern PRStatus _PR_MD_ALLOC_SEGMENT(PRSegment *seg, PRUint32 size, void *vaddr); +#define _PR_MD_ALLOC_SEGMENT _MD_ALLOC_SEGMENT + +extern void _PR_MD_FREE_SEGMENT(PRSegment *seg); +#define _PR_MD_FREE_SEGMENT _MD_FREE_SEGMENT + +/* Directory enumeration related */ +extern PRStatus _PR_MD_OPEN_DIR(_MDDir *md,const char *name); +#define _PR_MD_OPEN_DIR _MD_OPEN_DIR + +extern char * _PR_MD_READ_DIR(_MDDir *md, PRIntn flags); +#define _PR_MD_READ_DIR _MD_READ_DIR + +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 + +extern void _PR_MD_MAKE_NONBLOCK(PRFileDesc *fd); +#define _PR_MD_MAKE_NONBLOCK _MD_MAKE_NONBLOCK + +/* File I/O related */ +extern PROsfd _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode); +#define _PR_MD_OPEN _MD_OPEN + +extern PROsfd _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode); +#define _PR_MD_OPEN_FILE _MD_OPEN_FILE + +extern PRInt32 _PR_MD_CLOSE_FILE(PROsfd osfd); +#define _PR_MD_CLOSE_FILE _MD_CLOSE_FILE + +extern PRInt32 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 amount); +#define _PR_MD_READ _MD_READ + +extern PRInt32 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 amount); +#define _PR_MD_WRITE _MD_WRITE + +extern PRInt32 _PR_MD_WRITEV( + PRFileDesc *fd, const struct PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout); +#define _PR_MD_WRITEV _MD_WRITEV + +extern PRInt32 _PR_MD_FSYNC(PRFileDesc *fd); +#define _PR_MD_FSYNC _MD_FSYNC + +extern PRInt32 _PR_MD_DELETE(const char *name); +#define _PR_MD_DELETE _MD_DELETE + +extern PRInt32 _PR_MD_RENAME(const char *from, const char *to); +#define _PR_MD_RENAME _MD_RENAME + +extern PRInt32 _PR_MD_ACCESS(const char *name, PRAccessHow how); +#define _PR_MD_ACCESS _MD_ACCESS + +extern PRInt32 _PR_MD_STAT(const char *name, struct stat *buf); +#define _PR_MD_STAT _MD_STAT + +extern PRInt32 _PR_MD_MKDIR(const char *name, PRIntn mode); +#define _PR_MD_MKDIR _MD_MKDIR + +extern PRInt32 _PR_MD_MAKE_DIR(const char *name, PRIntn mode); +#define _PR_MD_MAKE_DIR _MD_MAKE_DIR + +extern PRInt32 _PR_MD_RMDIR(const char *name); +#define _PR_MD_RMDIR _MD_RMDIR + +#ifdef MOZ_UNICODE +/* UTF16 File I/O related */ +extern PRStatus _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *md, const PRUnichar *name); +#define _PR_MD_OPEN_DIR_UTF16 _MD_OPEN_DIR_UTF16 + +extern PROsfd _PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, PRIntn mode); +#define _PR_MD_OPEN_FILE_UTF16 _MD_OPEN_FILE_UTF16 + +extern PRUnichar * _PR_MD_READ_DIR_UTF16(_MDDirUTF16 *md, PRIntn flags); +#define _PR_MD_READ_DIR_UTF16 _MD_READ_DIR_UTF16 + +extern PRInt32 _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *md); +#define _PR_MD_CLOSE_DIR_UTF16 _MD_CLOSE_DIR_UTF16 + +extern PRInt32 _PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info); +#define _PR_MD_GETFILEINFO64_UTF16 _MD_GETFILEINFO64_UTF16 +#endif /* MOZ_UNICODE */ + +/* Socket I/O related */ +extern void _PR_MD_INIT_IO(void); +#define _PR_MD_INIT_IO _MD_INIT_IO + +extern PRInt32 _PR_MD_CLOSE_SOCKET(PROsfd osfd); +#define _PR_MD_CLOSE_SOCKET _MD_CLOSE_SOCKET + +extern PRInt32 _PR_MD_CONNECT( + PRFileDesc *fd, const PRNetAddr *addr, + PRUint32 addrlen, PRIntervalTime timeout); +#define _PR_MD_CONNECT _MD_CONNECT + +extern PROsfd _PR_MD_ACCEPT( + PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen, PRIntervalTime timeout); +#define _PR_MD_ACCEPT _MD_ACCEPT + +extern PRInt32 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen); +#define _PR_MD_BIND _MD_BIND + +extern PRInt32 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog); +#define _PR_MD_LISTEN _MD_LISTEN + +extern PRInt32 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how); +#define _PR_MD_SHUTDOWN _MD_SHUTDOWN + +extern PRInt32 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); +#define _PR_MD_RECV _MD_RECV + +extern PRInt32 _PR_MD_SEND( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout); +#define _PR_MD_SEND _MD_SEND + +extern PRInt32 _PR_MD_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock, + PRNetAddr **raddr, void *buf, PRInt32 amount, + PRIntervalTime timeout); +#define _PR_MD_ACCEPT_READ _MD_ACCEPT_READ + +#ifdef WIN32 +extern PROsfd _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen, PRIntervalTime timeout, + PRBool fast, + _PR_AcceptTimeoutCallback callback, + void *callbackArg); + +extern PRInt32 _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock, + PRNetAddr **raddr, void *buf, PRInt32 amount, + PRIntervalTime timeout, PRBool fast, + _PR_AcceptTimeoutCallback callback, + void *callbackArg); + +extern void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd s, PROsfd ls); +#define _PR_MD_UPDATE_ACCEPT_CONTEXT _MD_UPDATE_ACCEPT_CONTEXT +/* + * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME. + * We store the value in a PRTime variable for convenience. + * This constant is used by _PR_FileTimeToPRTime(). + * This is defined in ntmisc.c + */ +extern const PRTime _pr_filetime_offset; +#endif /* WIN32 */ + +extern PRInt32 _PR_MD_SENDFILE( + PRFileDesc *sock, PRSendFileData *sfd, + PRInt32 flags, PRIntervalTime timeout); +#define _PR_MD_SENDFILE _MD_SENDFILE + +extern PRStatus _PR_MD_GETSOCKNAME( + PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen); +#define _PR_MD_GETSOCKNAME _MD_GETSOCKNAME + +extern PRStatus _PR_MD_GETPEERNAME( + PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen); +#define _PR_MD_GETPEERNAME _MD_GETPEERNAME + +extern PRStatus _PR_MD_GETSOCKOPT( + PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen); +#define _PR_MD_GETSOCKOPT _MD_GETSOCKOPT + +extern PRStatus _PR_MD_SETSOCKOPT( + PRFileDesc *fd, PRInt32 level, PRInt32 optname, + const char* optval, PRInt32 optlen); +#define _PR_MD_SETSOCKOPT _MD_SETSOCKOPT + +extern PRStatus PR_CALLBACK _PR_SocketGetSocketOption( + PRFileDesc *fd, PRSocketOptionData *data); + +extern PRStatus PR_CALLBACK _PR_SocketSetSocketOption( + PRFileDesc *fd, const PRSocketOptionData *data); + +extern PRInt32 _PR_MD_RECVFROM( + PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout); +#define _PR_MD_RECVFROM _MD_RECVFROM + +extern PRInt32 _PR_MD_SENDTO( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout); +#define _PR_MD_SENDTO _MD_SENDTO + +#if defined(_WIN64) && defined(WIN95) +extern PRInt32 _PR_MD_TCPSENDTO( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout); +#define _PR_MD_TCPSENDTO _MD_TCPSENDTO +#endif + +extern PRInt32 _PR_MD_SOCKETPAIR(int af, int type, int flags, PROsfd *osfd); +#define _PR_MD_SOCKETPAIR _MD_SOCKETPAIR + +extern PROsfd _PR_MD_SOCKET(int af, int type, int flags); +#define _PR_MD_SOCKET _MD_SOCKET + +extern PRInt32 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd); +#define _PR_MD_SOCKETAVAILABLE _MD_SOCKETAVAILABLE + +extern PRInt32 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd); +#define _PR_MD_PIPEAVAILABLE _MD_PIPEAVAILABLE + +extern PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, + PRIntervalTime timeout); +#define _PR_MD_PR_POLL _MD_PR_POLL + +/* + * Initialize fd->secret->inheritable for a newly created fd. + * If 'imported' is false, the osfd (i.e., fd->secret->md.osfd) + * was created by NSPR and hence has the OS-dependent default + * inheritable attribute. If 'imported' is true, the osfd was + * not created by NSPR and hence a system call is required to + * query its inheritable attribute. Since we may never need to + * know the inheritable attribute of a fd, a platform may choose + * to initialize fd->secret->inheritable of an imported fd to + * _PR_TRI_UNKNOWN and only pay the cost of the system call + * (in _PR_MD_QUERY_FD_INHERITABLE) when necessary. + */ +extern void _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported); +#define _PR_MD_INIT_FD_INHERITABLE _MD_INIT_FD_INHERITABLE + +extern PRStatus _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable); +#define _PR_MD_SET_FD_INHERITABLE _MD_SET_FD_INHERITABLE + + +#define _PR_PROCESS_TIMEOUT_INTERRUPT_ERRORS(me) \ + if (_PR_PENDING_INTERRUPT(me)) { \ + me->flags &= ~_PR_INTERRUPT; \ + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); \ + } else { \ + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); \ + } + +extern void *_PR_MD_GET_SP(PRThread *thread); +#define _PR_MD_GET_SP _MD_GET_SP + +#endif /* defined(_PR_PTHREADS) */ + +/************************************************************************/ +/************************************************************************* +** The remainder of the definitions are shared by pthreads and the classic +** NSPR code. These too may be conditionalized. +*************************************************************************/ +/************************************************************************/ + +extern PROffset32 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence); +#define _PR_MD_LSEEK _MD_LSEEK + +extern PROffset64 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence); +#define _PR_MD_LSEEK64 _MD_LSEEK64 + +extern PRInt32 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info); +#define _PR_MD_GETFILEINFO _MD_GETFILEINFO + +extern PRInt32 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info); +#define _PR_MD_GETFILEINFO64 _MD_GETFILEINFO64 + +extern PRInt32 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info); +#define _PR_MD_GETOPENFILEINFO _MD_GETOPENFILEINFO + +extern PRInt32 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info); +#define _PR_MD_GETOPENFILEINFO64 _MD_GETOPENFILEINFO64 + + +/*****************************************************************************/ +/************************** File descriptor caching **************************/ +/*****************************************************************************/ +extern void _PR_InitFdCache(void); +extern void _PR_CleanupFdCache(void); +extern PRFileDesc *_PR_Getfd(void); +extern void _PR_Putfd(PRFileDesc *fd); + +/* + * These flags are used by NSPR temporarily in the poll + * descriptor's out_flags field to record the mapping of + * NSPR's poll flags to the system poll flags. + * + * If _PR_POLL_READ_SYS_WRITE bit is set, it means the + * PR_POLL_READ flag specified by the topmost layer is + * mapped to the WRITE flag at the system layer. Similarly + * for the other three _PR_POLL_XXX_SYS_YYY flags. It is + * assumed that the PR_POLL_EXCEPT flag doesn't get mapped + * to other flags. + */ +#define _PR_POLL_READ_SYS_READ 0x1 +#define _PR_POLL_READ_SYS_WRITE 0x2 +#define _PR_POLL_WRITE_SYS_READ 0x4 +#define _PR_POLL_WRITE_SYS_WRITE 0x8 + +/* +** These methods are coerced into file descriptor methods table +** when the intended service is inappropriate for the particular +** type of file descriptor. +*/ +extern PRIntn _PR_InvalidInt(void); +extern PRInt16 _PR_InvalidInt16(void); +extern PRInt64 _PR_InvalidInt64(void); +extern PRStatus _PR_InvalidStatus(void); +extern PRFileDesc *_PR_InvalidDesc(void); + +extern PRIOMethods _pr_faulty_methods; + +/* +** The PR_NETADDR_SIZE macro can only be called on a PRNetAddr union +** whose 'family' field is set. It returns the size of the union +** member corresponding to the specified address family. +*/ + +extern PRUintn _PR_NetAddrSize(const PRNetAddr* addr); + +#if defined(_PR_INET6) + +#define PR_NETADDR_SIZE(_addr) _PR_NetAddrSize(_addr) + +#elif defined(_PR_HAVE_MD_SOCKADDR_IN6) + +/* +** Under the following conditions: +** 1. _PR_INET6 is not defined; +** 2. _PR_INET6_PROBE is defined; +** 3. struct sockaddr_in6 has nonstandard fields at the end +** (e.g., on Solaris 8), +** (_addr)->ipv6 is smaller than struct sockaddr_in6, and +** hence we can't pass sizeof((_addr)->ipv6) to socket +** functions such as connect because they would fail with +** EINVAL. +** +** To pass the correct socket address length to socket +** functions, define the macro _PR_HAVE_MD_SOCKADDR_IN6 and +** define struct _md_sockaddr_in6 to be isomorphic to +** struct sockaddr_in6. +*/ + +#if defined(XP_UNIX) || defined(XP_OS2) +#define PR_NETADDR_SIZE(_addr) \ + ((_addr)->raw.family == PR_AF_INET \ + ? sizeof((_addr)->inet) \ + : ((_addr)->raw.family == PR_AF_INET6 \ + ? sizeof(struct _md_sockaddr_in6) \ + : sizeof((_addr)->local))) +#else +#define PR_NETADDR_SIZE(_addr) \ + ((_addr)->raw.family == PR_AF_INET \ + ? sizeof((_addr)->inet) \ + : sizeof(struct _md_sockaddr_in6)) +#endif /* defined(XP_UNIX) */ + +#else + +#if defined(XP_UNIX) || defined(XP_OS2) +#define PR_NETADDR_SIZE(_addr) \ + ((_addr)->raw.family == PR_AF_INET \ + ? sizeof((_addr)->inet) \ + : ((_addr)->raw.family == PR_AF_INET6 \ + ? sizeof((_addr)->ipv6) \ + : sizeof((_addr)->local))) +#else +#define PR_NETADDR_SIZE(_addr) \ + ((_addr)->raw.family == PR_AF_INET \ + ? sizeof((_addr)->inet) \ + : sizeof((_addr)->ipv6)) +#endif /* defined(XP_UNIX) */ + +#endif /* defined(_PR_INET6) */ + +extern PRStatus _PR_MapOptionName( + PRSockOption optname, PRInt32 *level, PRInt32 *name); +extern void _PR_InitThreads( + PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs); + +struct PRLock { +#if defined(_PR_PTHREADS) + pthread_mutex_t mutex; /* the underlying lock */ + _PT_Notified notified; /* array of conditions notified */ + PRBool locked; /* whether the mutex is locked */ + pthread_t owner; /* if locked, current lock owner */ +#elif defined(_PR_BTHREADS) + sem_id semaphoreID; /* the underlying lock */ + int32 benaphoreCount; /* number of people in lock */ + thread_id owner; /* current lock owner */ +#else /* not pthreads or Be threads */ + PRCList links; /* linkage for PRThread.lockList */ + struct PRThread *owner; /* current lock owner */ + PRCList waitQ; /* list of threads waiting for lock */ + PRThreadPriority priority; /* priority of lock */ + PRThreadPriority boostPriority; /* boosted priority of lock owner */ + _MDLock ilock; /* Internal Lock to protect user-level fields */ +#endif +}; + +struct PRCondVar { + PRLock *lock; /* associated lock that protects the condition */ +#if defined(_PR_PTHREADS) + pthread_cond_t cv; /* underlying pthreads condition */ + PRInt32 notify_pending; /* CV has destroy pending notification */ +#elif defined(_PR_BTHREADS) + sem_id sem; /* the underlying lock */ + sem_id handshakeSem; /* the lock for 'notify'-threads waiting for confirmation */ + sem_id signalSem; /* the lock for threads waiting for someone to notify */ + volatile int32 nw; /* the number waiting */ + volatile int32 ns; /* the number signalling */ + long signalBenCount; /* the number waiting on the underlying sem */ +#else /* not pthreads or Be threads */ + PRCList condQ; /* Condition variable wait Q */ + _MDLock ilock; /* Internal Lock to protect condQ */ + _MDCVar md; +#endif +}; + +/************************************************************************/ + +struct PRMonitor { + const char* name; /* monitor name for debugging */ +#if defined(_PR_PTHREADS) + pthread_mutex_t lock; /* lock is only held when accessing fields + * of the PRMonitor, instead of being held + * while the monitor is entered. The only + * exception is notifyTimes, which is + * protected by the monitor. */ + pthread_t owner; /* the owner of the monitor or invalid */ + pthread_cond_t entryCV; /* for threads waiting to enter the monitor */ + + pthread_cond_t waitCV; /* for threads waiting on the monitor */ + PRInt32 refCount; /* reference count, an atomic variable. + * PR_NewMonitor adds a reference to the + * newly created PRMonitor, and + * PR_DestroyMonitor releases that reference. + * PR_ExitMonitor adds a reference before + * unlocking the internal lock if it needs to + * signal entryCV, and releases the reference + * after signaling entryCV. */ +#else /* defined(_PR_PTHREADS) */ + PRLock lock; /* lock is only held when accessing fields + * of the PRMonitor, instead of being held + * while the monitor is entered. The only + * exception is notifyTimes, which is + * protected by the monitor. */ + PRThread *owner; /* the owner of the monitor or invalid */ + PRCondVar entryCV; /* for threads waiting to enter the monitor */ + + PRCondVar waitCV; /* for threads waiting on the monitor */ +#endif /* defined(_PR_PTHREADS) */ + PRUint32 entryCount; /* # of times re-entered */ + PRIntn notifyTimes; /* number of pending notifies for waitCV. + * The special value -1 means a broadcast + * (PR_NotifyAll). */ +}; + +/************************************************************************/ + +struct PRSemaphore { +#if defined(_PR_BTHREADS) + sem_id sem; + int32 benaphoreCount; +#else + PRCondVar *cvar; /* associated lock and condition variable queue */ + PRUintn count; /* the value of the counting semaphore */ + PRUint32 waiters; /* threads waiting on the semaphore */ +#if defined(_PR_PTHREADS) +#else /* defined(_PR_PTHREADS) */ + _MDSemaphore md; +#endif /* defined(_PR_PTHREADS) */ +#endif /* defined(_PR_BTHREADS) */ +}; + +/*************************************************************************/ + +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 +}; + +/*************************************************************************/ + +struct PRStackStr { + /* head MUST be at offset 0; assembly language code relies on this */ +#if defined(AIX) + volatile PRStackElem prstk_head; +#else + PRStackElem prstk_head; +#endif + + PRLock *prstk_lock; + char *prstk_name; +}; + +/************************************************************************/ + +/* XXX this needs to be exported (sigh) */ +struct PRThreadStack { + PRCList links; + PRUintn flags; + + char *allocBase; /* base of stack's allocated memory */ + PRUint32 allocSize; /* size of stack's allocated memory */ + char *stackBottom; /* bottom of stack from C's point of view */ + char *stackTop; /* top of stack from C's point of view */ + PRUint32 stackSize; /* size of usable portion of the stack */ + + PRSegment *seg; + PRThread* thr; /* back pointer to thread owning this stack */ + +#if defined(_PR_PTHREADS) +#else /* defined(_PR_PTHREADS) */ + _MDThreadStack md; +#endif /* defined(_PR_PTHREADS) */ +}; + +extern void _PR_DestroyThreadPrivate(PRThread*); + +typedef void (PR_CALLBACK *_PRStartFn)(void *); + +struct PRThread { + PRUint32 state; /* thread's creation state */ + PRThreadPriority priority; /* apparent priority, loosly defined */ + + void *arg; /* argument to the client's entry point */ + _PRStartFn startFunc; /* the root of the client's thread */ + + PRThreadStack *stack; /* info about thread's stack (for GC) */ + void *environment; /* pointer to execution environment */ + + PRThreadDumpProc dump; /* dump thread info out */ + void *dumpArg; /* argument for the dump function */ + + /* + ** Per thread private data + */ + PRUint32 tpdLength; /* thread's current vector length */ + void **privateData; /* private data vector or NULL */ + PRErrorCode errorCode; /* current NSPR error code | zero */ + PRInt32 osErrorCode; /* mapping of errorCode | zero */ + PRIntn errorStringLength; /* textLength from last call to PR_SetErrorText() */ + PRInt32 errorStringSize; /* malloc()'d size of buffer | zero */ + char *errorString; /* current error string | NULL */ + char *name; /* thread's name */ + +#if defined(_PR_PTHREADS) + pthread_t id; /* pthread identifier for the thread */ + PRBool idSet; /* whether 'id' has been set. Protected by + * pt_book.ml. */ +#ifdef _PR_NICE_PRIORITY_SCHEDULING + pid_t tid; /* Linux-specific kernel thread ID */ +#endif + PRBool okToDelete; /* ok to delete the PRThread struct? */ + PRCondVar *waiting; /* where the thread is waiting | NULL */ + 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 */ +#ifdef PT_NO_SIGTIMEDWAIT + pthread_mutex_t suspendResumeMutex; + pthread_cond_t suspendResumeCV; +#endif + PRUint32 interrupt_blocked; /* interrupt blocked */ + struct pollfd *syspoll_list; /* Unix polling list used by PR_Poll */ + PRUint32 syspoll_count; /* number of elements in syspoll_list */ +#if defined(_PR_POLL_WITH_SELECT) + int *selectfd_list; /* Unix fd's that PR_Poll selects on */ + PRUint32 selectfd_count; /* number of elements in selectfd_list */ +#endif +#elif defined(_PR_BTHREADS) + PRUint32 flags; + _MDThread md; + PRBool io_pending; + PRInt32 io_fd; + PRBool io_suspended; +#else /* not pthreads or Be threads */ + _MDLock threadLock; /* Lock to protect thread state variables. + * Protects the following fields: + * state + * priority + * links + * wait + * cpu + */ + PRUint32 queueCount; + PRUint32 waitCount; + + PRCList active; /* on list of all active threads */ + PRCList links; + PRCList waitQLinks; /* when thread is PR_Wait'ing */ + PRCList lockList; /* list of locks currently holding */ + PRIntervalTime sleep; /* sleep time when thread is sleeping */ + struct _wait { + struct PRLock *lock; + struct PRCondVar *cvar; + } wait; + + PRUint32 id; + PRUint32 flags; + PRUint32 no_sched; /* Don't schedule the thread to run. + * This flag has relevance only when + * multiple NSPR CPUs are created. + * When a thread is de-scheduled, there + * is a narrow window of time in which + * the thread is put on the run queue + * but the scheduler is actually using + * the stack of this thread. It is safe + * to run this thread on a different CPU + * only when its stack is not in use on + * any other CPU. The no_sched flag is + * set during this interval to prevent + * the thread from being scheduled on a + * different CPU. + */ + + /* thread termination condition variable for join */ + PRCondVar *term; + + _PRCPU *cpu; /* cpu to which this thread is bound */ + PRUint32 threadAllocatedOnStack;/* boolean */ + + /* When an async IO is in progress and a second async IO cannot be + * initiated, the io_pending flag is set to true. Some platforms will + * not use the io_pending flag. If the io_pending flag is true, then + * io_fd is the OS-file descriptor on which IO is pending. + */ + PRBool io_pending; + PRInt32 io_fd; + + /* If a timeout occurs or if an outstanding IO is interrupted and the + * OS doesn't support a real cancellation (NT or MAC), then the + * io_suspended flag will be set to true. The thread will be resumed + * but may run into trouble issuing additional IOs until the io_pending + * flag can be cleared + */ + PRBool io_suspended; + + _MDThread md; +#endif +}; + +struct PRProcessAttr { + PRFileDesc *stdinFd; + PRFileDesc *stdoutFd; + PRFileDesc *stderrFd; + char *currentDirectory; + char *fdInheritBuffer; + PRSize fdInheritBufferSize; + PRSize fdInheritBufferUsed; +}; + +struct PRProcess { + _MDProcess md; +}; + +struct PRFileMap { + PRFileDesc *fd; + PRFileMapProtect prot; + _MDFileMap md; +}; + +/************************************************************************/ + +/* +** File descriptors of the NSPR layer can be in one of the +** following states (stored in the 'state' field of struct +** PRFilePrivate): +** - _PR_FILEDESC_OPEN: The OS fd is open. +** - _PR_FILEDESC_CLOSED: The OS fd is closed. The PRFileDesc +** is still open but is unusable. The only operation allowed +** on the PRFileDesc is PR_Close(). +** - _PR_FILEDESC_FREED: The OS fd is closed and the PRFileDesc +** structure is freed. +*/ + +#define _PR_FILEDESC_OPEN 0xaaaaaaaa /* 1010101... */ +#define _PR_FILEDESC_CLOSED 0x55555555 /* 0101010... */ +#define _PR_FILEDESC_FREED 0x11111111 + +/* +** A boolean type with an additional "unknown" state +*/ + +typedef enum { + _PR_TRI_TRUE = 1, + _PR_TRI_FALSE = 0, + _PR_TRI_UNKNOWN = -1 +} _PRTriStateBool; + +struct PRFilePrivate { + PRInt32 state; + PRBool nonblocking; + _PRTriStateBool inheritable; + PRFileDesc *next; + PRIntn lockCount; /* 0: not locked + * -1: a native lockfile call is in progress + * > 0: # times the file is locked */ +#ifdef _PR_HAVE_PEEK_BUFFER + char *peekBuffer; + PRInt32 peekBufSize; + PRInt32 peekBytes; +#endif +#if !defined(_PR_HAVE_O_APPEND) + PRBool appendMode; /* Some platforms don't have O_APPEND or its + * equivalent, so they have to seek to end of + * file on write if the file was opened in + * append mode. See Bugzilla 4090, 276330. */ +#endif + _MDFileDesc md; +#ifdef _PR_NEED_SECRET_AF + PRUint16 af; /* If the platform's implementation of accept() + * requires knowing the address family of the + * socket, we save the address family here. */ +#endif + +#if defined(_WIN64) + /* This is necessary for TCP Fast Open. TCP Fast Open in windows must + * use ConnectEx function which uses OVERLAPPED. TCPSendTo will call + * ConnectEx to send fast open data. If ConnectEx returns + * ERROR_IO_PENDING we need to save OVERLAPPED structure and we will + * use it in ConnectContinue to get the final result of ConnectEx. + */ + PRBool alreadyConnected; + PRBool overlappedActive; + OVERLAPPED ol; +#endif +}; + +#ifdef _WIN64 +#define PR_PRIdOSFD "lld" /* for printing PROsfd */ +#define PR_PRIxOSFD "llx" +#define PR_SCNdOSFD "lld" /* for scanning PROsfd */ +#define PR_SCNxOSFD "llx" +#else +#define PR_PRIdOSFD "ld" /* for printing PROsfd */ +#define PR_PRIxOSFD "lx" +#define PR_SCNdOSFD "ld" /* for scanning PROsfd */ +#define PR_SCNxOSFD "lx" +#endif + +struct PRDir { + PRDirEntry d; + _MDDir md; +}; + +#ifdef MOZ_UNICODE +struct PRDirUTF16 { + PRDirEntry d; + _MDDirUTF16 md; +}; +#endif /* MOZ_UNICODE */ + +extern void _PR_InitLocks(void); +extern void _PR_InitSegs(void); +extern void _PR_InitStacks(void); +extern void _PR_InitTPD(void); +extern void _PR_InitMem(void); +extern void _PR_InitEnv(void); +extern void _PR_InitCMon(void); +extern void _PR_InitIO(void); +extern void _PR_InitLog(void); +extern void _PR_InitNet(void); +extern void _PR_InitClock(void); +extern void _PR_InitLinker(void); +extern void _PR_InitAtomic(void); +extern void _PR_InitCPUs(void); +extern void _PR_InitDtoa(void); +extern void _PR_InitTime(void); +extern void _PR_InitMW(void); +extern void _PR_InitRWLocks(void); +extern void _PR_CleanupThread(PRThread *thread); +extern void _PR_CleanupCallOnce(void); +extern void _PR_CleanupMW(void); +extern void _PR_CleanupTime(void); +extern void _PR_CleanupDtoa(void); +extern void _PR_ShutdownLinker(void); +extern void _PR_CleanupEnv(void); +extern void _PR_CleanupIO(void); +extern void _PR_CleanupCMon(void); +extern void _PR_CleanupNet(void); +extern void _PR_CleanupLayerCache(void); +extern void _PR_CleanupStacks(void); +#ifdef WINNT +extern void _PR_CleanupCPUs(void); +#endif +extern void _PR_CleanupThreads(void); +extern void _PR_CleanupTPD(void); +extern void _PR_Cleanup(void); +extern void _PR_LogCleanup(void); +extern void _PR_InitLayerCache(void); + +extern PRBool _pr_initialized; +extern void _PR_ImplicitInitialization(void); +extern PRBool _PR_Obsolete(const char *obsolete, const char *preferred); + +/************************************************************************/ + +struct PRSegment { + void *vaddr; + PRUint32 size; + PRUintn flags; +#if defined(_PR_PTHREADS) +#else /* defined(_PR_PTHREADS) */ + _MDSegment md; +#endif /* defined(_PR_PTHREADS) */ +}; + +/* PRSegment.flags */ +#define _PR_SEG_VM 0x1 + +/************************************************************************/ + +extern PRInt32 _pr_pageSize; +extern PRInt32 _pr_pageShift; + +extern PRLogModuleInfo *_pr_clock_lm; +extern PRLogModuleInfo *_pr_cmon_lm; +extern PRLogModuleInfo *_pr_io_lm; +extern PRLogModuleInfo *_pr_cvar_lm; +extern PRLogModuleInfo *_pr_mon_lm; +extern PRLogModuleInfo *_pr_linker_lm; +extern PRLogModuleInfo *_pr_sched_lm; +extern PRLogModuleInfo *_pr_thread_lm; +extern PRLogModuleInfo *_pr_gc_lm; + +extern PRFileDesc *_pr_stdin; +extern PRFileDesc *_pr_stdout; +extern PRFileDesc *_pr_stderr; + +/* Zone allocator */ +/* +** The zone allocator code has hardcoded pthread types and +** functions, so it can only be used in the pthreads version. +** This can be fixed by replacing the hardcoded pthread types +** and functions with macros that expand to the native thread +** types and functions on each platform. +*/ +#if defined(_PR_PTHREADS) +#define _PR_ZONE_ALLOCATOR +#endif + +#ifdef _PR_ZONE_ALLOCATOR +extern void _PR_InitZones(void); +extern void _PR_DestroyZones(void); +#endif + +/* Overriding malloc, free, etc. */ +#if !defined(_PR_NO_PREEMPT) && defined(XP_UNIX) \ + && !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY) \ + && !defined(PURIFY) \ + && !defined(DARWIN) \ + && !defined(QNX) \ + && !(defined (UNIXWARE) && defined (USE_SVR4_THREADS)) +#define _PR_OVERRIDE_MALLOC +#endif + +/************************************************************************* +* External machine-dependent code provided by each OS. * * +*************************************************************************/ + +/* Initialization related */ +extern void _PR_MD_EARLY_INIT(void); +#define _PR_MD_EARLY_INIT _MD_EARLY_INIT + +extern void _PR_MD_INTERVAL_INIT(void); +#define _PR_MD_INTERVAL_INIT _MD_INTERVAL_INIT + +NSPR_API(void) _PR_MD_FINAL_INIT(void); +#define _PR_MD_FINAL_INIT _MD_FINAL_INIT + +extern void _PR_MD_EARLY_CLEANUP(void); +#define _PR_MD_EARLY_CLEANUP _MD_EARLY_CLEANUP + +/* Process control */ + +extern PRProcess * _PR_MD_CREATE_PROCESS( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr); +#define _PR_MD_CREATE_PROCESS _MD_CREATE_PROCESS + +extern PRStatus _PR_MD_DETACH_PROCESS(PRProcess *process); +#define _PR_MD_DETACH_PROCESS _MD_DETACH_PROCESS + +extern PRStatus _PR_MD_WAIT_PROCESS(PRProcess *process, PRInt32 *exitCode); +#define _PR_MD_WAIT_PROCESS _MD_WAIT_PROCESS + +extern PRStatus _PR_MD_KILL_PROCESS(PRProcess *process); +#define _PR_MD_KILL_PROCESS _MD_KILL_PROCESS + +/* Current Time */ +NSPR_API(PRTime) _PR_MD_NOW(void); +#define _PR_MD_NOW _MD_NOW + +/* Environment related */ +extern char* _PR_MD_GET_ENV(const char *name); +#define _PR_MD_GET_ENV _MD_GET_ENV + +extern PRIntn _PR_MD_PUT_ENV(const char *name); +#define _PR_MD_PUT_ENV _MD_PUT_ENV + +/* Atomic operations */ + +extern void _PR_MD_INIT_ATOMIC(void); +#define _PR_MD_INIT_ATOMIC _MD_INIT_ATOMIC + +extern PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *); +#define _PR_MD_ATOMIC_INCREMENT _MD_ATOMIC_INCREMENT + +extern PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *, PRInt32); +#define _PR_MD_ATOMIC_ADD _MD_ATOMIC_ADD + +extern PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *); +#define _PR_MD_ATOMIC_DECREMENT _MD_ATOMIC_DECREMENT + +extern PRInt32 _PR_MD_ATOMIC_SET(PRInt32 *, PRInt32); +#define _PR_MD_ATOMIC_SET _MD_ATOMIC_SET + +/* Garbage collection */ + +/* +** Save the registers that the GC would find interesting into the thread +** "t". isCurrent will be non-zero if the thread state that is being +** saved is the currently executing thread. Return the address of the +** first register to be scanned as well as the number of registers to +** scan in "np". +** +** If "isCurrent" is non-zero then it is allowed for the thread context +** area to be used as scratch storage to hold just the registers +** necessary for scanning. +*/ +extern PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np); + +/* Time intervals */ + +extern PRIntervalTime _PR_MD_GET_INTERVAL(void); +#define _PR_MD_GET_INTERVAL _MD_GET_INTERVAL + +extern PRIntervalTime _PR_MD_INTERVAL_PER_SEC(void); +#define _PR_MD_INTERVAL_PER_SEC _MD_INTERVAL_PER_SEC + +/* Affinity masks */ + +extern PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ); +#define _PR_MD_SETTHREADAFFINITYMASK _MD_SETTHREADAFFINITYMASK + +extern PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask); +#define _PR_MD_GETTHREADAFFINITYMASK _MD_GETTHREADAFFINITYMASK + +/* File locking */ + +extern PRStatus _PR_MD_LOCKFILE(PROsfd osfd); +#define _PR_MD_LOCKFILE _MD_LOCKFILE + +extern PRStatus _PR_MD_TLOCKFILE(PROsfd osfd); +#define _PR_MD_TLOCKFILE _MD_TLOCKFILE + +extern PRStatus _PR_MD_UNLOCKFILE(PROsfd osfd); +#define _PR_MD_UNLOCKFILE _MD_UNLOCKFILE + +/* Memory-mapped files */ + +extern PRStatus _PR_MD_CREATE_FILE_MAP(PRFileMap *fmap, PRInt64 size); +#define _PR_MD_CREATE_FILE_MAP _MD_CREATE_FILE_MAP + +extern PRInt32 _PR_MD_GET_MEM_MAP_ALIGNMENT(void); +#define _PR_MD_GET_MEM_MAP_ALIGNMENT _MD_GET_MEM_MAP_ALIGNMENT + +extern void * _PR_MD_MEM_MAP( + PRFileMap *fmap, + PROffset64 offset, + PRUint32 len); +#define _PR_MD_MEM_MAP _MD_MEM_MAP + +extern PRStatus _PR_MD_MEM_UNMAP(void *addr, PRUint32 size); +#define _PR_MD_MEM_UNMAP _MD_MEM_UNMAP + +extern PRStatus _PR_MD_CLOSE_FILE_MAP(PRFileMap *fmap); +#define _PR_MD_CLOSE_FILE_MAP _MD_CLOSE_FILE_MAP + +extern PRStatus _PR_MD_SYNC_MEM_MAP( + PRFileDesc *fd, + void *addr, + PRUint32 len); +#define _PR_MD_SYNC_MEM_MAP _MD_SYNC_MEM_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 */ + +NSPR_API(PRInt32) _PR_MD_GET_SOCKET_ERROR(void); +#define _PR_MD_GET_SOCKET_ERROR _MD_GET_SOCKET_ERROR + +/* Get name of current host */ +extern PRStatus _PR_MD_GETHOSTNAME(char *name, PRUint32 namelen); +#define _PR_MD_GETHOSTNAME _MD_GETHOSTNAME + +extern PRStatus _PR_MD_GETSYSINFO(PRSysInfo cmd, char *name, PRUint32 namelen); +#define _PR_MD_GETSYSINFO _MD_GETSYSINFO + +/* File descriptor inheritance */ + +/* + * If fd->secret->inheritable is _PR_TRI_UNKNOWN and we need to + * know the inheritable attribute of the fd, call this function + * to find that out. This typically requires a system call. + */ +extern void _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd); +#define _PR_MD_QUERY_FD_INHERITABLE _MD_QUERY_FD_INHERITABLE + +/* --- PR_GetRandomNoise() related things --- */ + +extern PRSize _PR_MD_GetRandomNoise( void *buf, PRSize size ); +#define _PR_MD_GET_RANDOM_NOISE(buf,size) _PR_MD_GetRandomNoise((buf),(size)) +extern PRSize _pr_CopyLowBits( void *dest, PRSize dstlen, void *src, PRSize srclen ); + +/* end PR_GetRandomNoise() related */ + +#if defined(_WIN64) && defined(WIN95) +typedef struct _PRFileDescList { + PRFileDesc *fd; + struct _PRFileDescList *next; +} PRFileDescList; + +extern PRLock *_fd_waiting_for_overlapped_done_lock; +extern PRFileDescList *_fd_waiting_for_overlapped_done; +extern void CheckOverlappedPendingSocketsAreDone(); +#endif + + +PR_END_EXTERN_C + +#endif /* primpl_h___ */ diff --git a/nsprpub/pr/include/private/prpriv.h b/nsprpub/pr/include/private/prpriv.h new file mode 100644 index 0000000000..f45eee4752 --- /dev/null +++ b/nsprpub/pr/include/private/prpriv.h @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prpriv_h___ +#define prpriv_h___ + +/* + * NSPR 2.0 Private API + */ + +#include "private/pprio.h" +#include "private/pprthred.h" + +#endif /* prpriv_h___ */ diff --git a/nsprpub/pr/include/prlink.h b/nsprpub/pr/include/prlink.h new file mode 100644 index 0000000000..0e38effaf7 --- /dev/null +++ b/nsprpub/pr/include/prlink.h @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prlink_h___ +#define prlink_h___ + +/* +** API to static and dynamic linking. +*/ +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +typedef struct PRLibrary PRLibrary; + +typedef struct PRStaticLinkTable { + const char *name; + void (*fp)(void); +} PRStaticLinkTable; + +/* +** Change the default library path to the given string. The string is +** copied. This call will fail if it runs out of memory. +** +** The string provided as 'path' is copied. The caller can do whatever is +** convenient with the argument when the function is complete. +*/ +NSPR_API(PRStatus) PR_SetLibraryPath(const char *path); + +/* +** Return a character string which contains the path used to search for +** dynamically loadable libraries. +** +** The returned value is basically a copy of a PR_SetLibraryPath(). +** The storage is allocated by the runtime and becomes the responsibilty +** of the caller. +*/ +NSPR_API(char*) PR_GetLibraryPath(void); + +/* +** Given a directory name "dir" and a library name "lib" construct a full +** path name that will refer to the actual dynamically loaded +** library. This does not test for existance of said file, it just +** constructs the full filename. The name constructed is system dependent +** and prepared for PR_LoadLibrary. The result must be free'd when the +** caller is done with it. +** +** The storage for the result is allocated by the runtime and becomes the +** responsibility of the caller. +*/ +NSPR_API(char*) PR_GetLibraryName(const char *dir, const char *lib); + +/* +** +** Free the memory allocated, for the caller, by PR_GetLibraryName +*/ +NSPR_API(void) PR_FreeLibraryName(char *mem); + +/* +** Given a library "name" try to load the library. The argument "name" +** is a machine-dependent name for the library, such as the full pathname +** returned by PR_GetLibraryName. If the library is already loaded, +** this function will avoid loading the library twice. +** +** If the library is loaded successfully, then a pointer to the PRLibrary +** structure representing the library is returned. Otherwise, NULL is +** returned. +** +** This increments the reference count of the library. +*/ +NSPR_API(PRLibrary*) PR_LoadLibrary(const char *name); + +/* +** Each operating system has its preferred way of specifying +** a file in the file system. Most operating systems use +** a pathname. Mac OS Classic, on the other hand, uses the FSSpec +** structure to specify a file. PRLibSpec allows NSPR clients +** to use the type of file specification that is most efficient +** for a particular platform. +** +** On some operating systems such as Mac OS Classic, a shared library +** may contain code fragments that can be individually loaded. +** PRLibSpec also allows NSPR clients to identify a code fragment +** in a library, if code fragments are supported by the OS. +** A code fragment can be specified by name or by an integer index. +** +** Right now PRLibSpec supports four types of library specification: +** a pathname in the native character encoding, a Mac code fragment +** by name, a Mac code fragment by index, and a UTF-16 pathname. +*/ + +typedef enum PRLibSpecType { + PR_LibSpec_Pathname, + PR_LibSpec_MacNamedFragment, /* obsolete (for Mac OS Classic) */ + PR_LibSpec_MacIndexedFragment, /* obsolete (for Mac OS Classic) */ + PR_LibSpec_PathnameU /* supported only on Win32 */ +} PRLibSpecType; + +struct FSSpec; /* Mac OS Classic FSSpec */ + +typedef struct PRLibSpec { + PRLibSpecType type; + union { + /* if type is PR_LibSpec_Pathname */ + const char *pathname; + + /* if type is PR_LibSpec_MacNamedFragment */ + struct { + const struct FSSpec *fsspec; + const char *name; + } mac_named_fragment; /* obsolete (for Mac OS Classic) */ + + /* if type is PR_LibSpec_MacIndexedFragment */ + struct { + const struct FSSpec *fsspec; + PRUint32 index; + } mac_indexed_fragment; /* obsolete (for Mac OS Classic) */ + + /* if type is PR_LibSpec_PathnameU */ + const PRUnichar *pathname_u; /* supported only on Win32 */ + } value; +} PRLibSpec; + +/* +** The following bit flags may be or'd together and passed +** as the 'flags' argument to PR_LoadLibraryWithFlags. +** Flags not supported by the underlying OS are ignored. +*/ + +#define PR_LD_LAZY 0x1 /* equivalent to RTLD_LAZY on Unix */ +#define PR_LD_NOW 0x2 /* equivalent to RTLD_NOW on Unix */ +#define PR_LD_GLOBAL 0x4 /* equivalent to RTLD_GLOBAL on Unix */ +#define PR_LD_LOCAL 0x8 /* equivalent to RTLD_LOCAL on Unix */ +/* The following is equivalent to LOAD_WITH_ALTERED_SEARCH_PATH on Windows */ +#define PR_LD_ALT_SEARCH_PATH 0x10 +/* 0x8000 reserved for NSPR internal use */ + +/* +** Load the specified library, in the manner specified by 'flags'. +*/ + +NSPR_API(PRLibrary *) +PR_LoadLibraryWithFlags( + PRLibSpec libSpec, /* the shared library */ + PRIntn flags /* flags that affect the loading */ +); + +/* +** Unload a previously loaded library. If the library was a static +** library then the static link table will no longer be referenced. The +** associated PRLibrary object is freed. +** +** PR_FAILURE is returned if the library cannot be unloaded. +** +** This function decrements the reference count of the library. +*/ +NSPR_API(PRStatus) PR_UnloadLibrary(PRLibrary *lib); + +/* +** Given the name of a procedure, return the address of the function that +** implements the procedure, or NULL if no such function can be +** found. This does not find symbols in the main program (the ".exe"); +** use PR_LoadStaticLibrary to register symbols in the main program. +** +** This function does not modify the reference count of the library. +*/ +NSPR_API(void*) PR_FindSymbol(PRLibrary *lib, const char *name); + +/* +** Similar to PR_FindSymbol, except that the return value is a pointer to +** a function, and not a pointer to void. Casting between a data pointer +** and a function pointer is not portable according to the C standard. +** Any function pointer can be cast to any other function pointer. +** +** This function does not modify the reference count of the library. +*/ +typedef void (*PRFuncPtr)(void); +NSPR_API(PRFuncPtr) PR_FindFunctionSymbol(PRLibrary *lib, const char *name); + +/* +** Finds a symbol in one of the currently loaded libraries. Given the +** name of a procedure, return the address of the function that +** implements the procedure, and return the library that contains that +** symbol, or NULL if no such function can be found. This does not find +** symbols in the main program (the ".exe"); use PR_AddStaticLibrary to +** register symbols in the main program. +** +** This increments the reference count of the library. +*/ +NSPR_API(void*) PR_FindSymbolAndLibrary(const char *name, + PRLibrary* *lib); + +/* +** Similar to PR_FindSymbolAndLibrary, except that the return value is +** a pointer to a function, and not a pointer to void. Casting between a +** data pointer and a function pointer is not portable according to the C +** standard. Any function pointer can be cast to any other function pointer. +** +** This increments the reference count of the library. +*/ +NSPR_API(PRFuncPtr) PR_FindFunctionSymbolAndLibrary(const char *name, + PRLibrary* *lib); + +/* +** Register a static link table with the runtime under the name +** "name". The symbols present in the static link table will be made +** available to PR_FindSymbol. If "name" is null then the symbols will be +** made available to the library which represents the executable. The +** tables are not copied. +** +** Returns the library object if successful, null otherwise. +** +** This increments the reference count of the library. +*/ +NSPR_API(PRLibrary*) PR_LoadStaticLibrary( + const char *name, const PRStaticLinkTable *table); + +/* +** Return the pathname of the file that the library "name" was loaded +** from. "addr" is the address of a function defined in the library. +** +** The caller is responsible for freeing the result with PR_Free. +*/ +NSPR_API(char *) PR_GetLibraryFilePathname(const char *name, PRFuncPtr addr); + +PR_END_EXTERN_C + +#endif /* prlink_h___ */ diff --git a/nsprpub/pr/include/prlock.h b/nsprpub/pr/include/prlock.h new file mode 100644 index 0000000000..9b7fdaa184 --- /dev/null +++ b/nsprpub/pr/include/prlock.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prlock.h +** Description: API to basic locking functions of NSPR. +** +** +** NSPR provides basic locking mechanisms for thread synchronization. Locks +** are lightweight resource contention controls that prevent multiple threads +** from accessing something (code/data) simultaneously. +**/ + +#ifndef prlock_h___ +#define prlock_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/**********************************************************************/ +/************************* TYPES AND CONSTANTS ************************/ +/**********************************************************************/ + +/* + * PRLock -- + * + * NSPR represents the lock as an opaque entity to the client of the + * API. All routines operate on a pointer to this opaque entity. + */ + +typedef struct PRLock PRLock; + +/**********************************************************************/ +/****************************** FUNCTIONS *****************************/ +/**********************************************************************/ + +/*********************************************************************** +** FUNCTION: PR_NewLock +** DESCRIPTION: +** Returns a pointer to a newly created opaque lock object. +** INPUTS: void +** OUTPUTS: void +** RETURN: PRLock* +** If the lock can not be created because of resource constraints, NULL +** is returned. +** +***********************************************************************/ +NSPR_API(PRLock*) PR_NewLock(void); + +/*********************************************************************** +** FUNCTION: PR_DestroyLock +** DESCRIPTION: +** Destroys a given opaque lock object. +** INPUTS: PRLock *lock +** Lock to be freed. +** OUTPUTS: void +** RETURN: None +***********************************************************************/ +NSPR_API(void) PR_DestroyLock(PRLock *lock); + +/*********************************************************************** +** FUNCTION: PR_Lock +** DESCRIPTION: +** Lock a lock. +** INPUTS: PRLock *lock +** Lock to locked. +** OUTPUTS: void +** RETURN: None +***********************************************************************/ +NSPR_API(void) PR_Lock(PRLock *lock); + +/*********************************************************************** +** FUNCTION: PR_Unlock +** DESCRIPTION: +** Unlock a lock. Unlocking an unlocked lock has undefined results. +** INPUTS: PRLock *lock +** Lock to unlocked. +** OUTPUTS: void +** RETURN: PR_STATUS +** Returns PR_FAILURE if the caller does not own the lock. +***********************************************************************/ +NSPR_API(PRStatus) PR_Unlock(PRLock *lock); + +/*********************************************************************** +** MACRO: PR_ASSERT_CURRENT_THREAD_OWNS_LOCK +** DESCRIPTION: +** If the current thread owns |lock|, this assertion is guaranteed to +** succeed. Otherwise, the behavior of this function is undefined. +** INPUTS: PRLock *lock +** Lock to assert ownership of. +** OUTPUTS: void +** RETURN: None +***********************************************************************/ +#if defined(DEBUG) || defined(FORCE_PR_ASSERT) +#define PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(/* PrLock* */ lock) \ + PR_AssertCurrentThreadOwnsLock(lock) +#else +#define PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(/* PrLock* */ lock) +#endif + +/* Don't call this function directly. */ +NSPR_API(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock); + +PR_END_EXTERN_C + +#endif /* prlock_h___ */ diff --git a/nsprpub/pr/include/prlog.h b/nsprpub/pr/include/prlog.h new file mode 100644 index 0000000000..1922c255d8 --- /dev/null +++ b/nsprpub/pr/include/prlog.h @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prlog_h___ +#define prlog_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** prlog.h -- Declare interfaces to NSPR's Logging service +** +** NSPR provides a logging service that is used by NSPR itself and is +** available to client programs. +** +** To use the service from a client program, you should create a +** PRLogModuleInfo structure by calling PR_NewLogModule(). After +** creating the LogModule, you can write to the log using the PR_LOG() +** macro. +** +** Initialization of the log service is handled by NSPR initialization. +** +** At execution time, you must enable the log service. To enable the +** log service, set the environment variable: NSPR_LOG_MODULES +** variable. +** +** NSPR_LOG_MODULES variable has the form: +** +** <moduleName>:<value>[, <moduleName>:<value>]* +** +** Where: +** <moduleName> is the name passed to PR_NewLogModule(). +** <value> is a numeric constant, e.g. 5. This value is the maximum +** value of a log event, enumerated by PRLogModuleLevel, that you want +** written to the log. +** +** For example: to record all events of greater value than or equal to +** PR_LOG_ERROR for a LogModule names "gizmo", say: +** +** set NSPR_LOG_MODULES=gizmo:2 +** +** Note that you must specify the numeric value of PR_LOG_ERROR. +** +** Special LogModule names are provided for controlling NSPR's log +** service at execution time. These controls should be set in the +** NSPR_LOG_MODULES environment variable at execution time to affect +** NSPR's log service for your application. +** +** The special LogModule "all" enables all LogModules. To enable all +** LogModule calls to PR_LOG(), say: +** +** set NSPR_LOG_MODULES=all:5 +** +** The special LogModule name "sync" tells the NSPR log service to do +** unbuffered logging. +** +** The special LogModule name "bufsize:<size>" tells NSPR to set the +** log buffer to <size>. +** +** The environment variable NSPR_LOG_FILE specifies the log file to use +** unless the default of "stderr" is acceptable. For MS Windows +** systems, NSPR_LOG_FILE can be set to a special value: "WinDebug" +** (case sensitive). This value causes PR_LOG() output to be written +** using the Windows API OutputDebugString(). OutputDebugString() +** writes to the debugger window; some people find this helpful. +** +** +** To put log messages in your programs, use the PR_LOG macro: +** +** PR_LOG(<module>, <level>, (<printfString>, <args>*)); +** +** Where <module> is the address of a PRLogModuleInfo structure, and +** <level> is one of the levels defined by the enumeration: +** PRLogModuleLevel. <args> is a printf() style of argument list. That +** is: (fmtstring, ...). +** +** Example: +** +** main() { +** PRIntn one = 1; +** PRLogModuleInfo * myLm = PR_NewLogModule("gizmo"); +** PR_LOG( myLm, PR_LOG_ALWAYS, ("Log this! %d\n", one)); +** return; +** } +** +** Note the use of printf() style arguments as the third agrument(s) to +** PR_LOG(). +** +** After compiling and linking you application, set the environment: +** +** set NSPR_LOG_MODULES=gizmo:5 +** set NSPR_LOG_FILE=logfile.txt +** +** When you execute your application, the string "Log this! 1" will be +** written to the file "logfile.txt". +** +** Note to NSPR engineers: a number of PRLogModuleInfo structures are +** defined and initialized in prinit.c. See this module for ideas on +** what to log where. +** +*/ + +typedef enum PRLogModuleLevel { + PR_LOG_NONE = 0, /* nothing */ + PR_LOG_ALWAYS = 1, /* always printed */ + PR_LOG_ERROR = 2, /* error messages */ + PR_LOG_WARNING = 3, /* warning messages */ + PR_LOG_DEBUG = 4, /* debug messages */ + + PR_LOG_NOTICE = PR_LOG_DEBUG, /* notice messages */ + PR_LOG_WARN = PR_LOG_WARNING, /* warning messages */ + PR_LOG_MIN = PR_LOG_DEBUG, /* minimal debugging messages */ + PR_LOG_MAX = PR_LOG_DEBUG /* maximal debugging messages */ +} PRLogModuleLevel; + +/* +** One of these structures is created for each module that uses logging. +** "name" is the name of the module +** "level" is the debugging level selected for that module +*/ +typedef struct PRLogModuleInfo { + const char *name; + PRLogModuleLevel level; + struct PRLogModuleInfo *next; +} PRLogModuleInfo; + +/* +** Create a new log module. +*/ +NSPR_API(PRLogModuleInfo*) PR_NewLogModule(const char *name); + +/* +** Set the file to use for logging. Returns PR_FALSE if the file cannot +** be created +*/ +NSPR_API(PRBool) PR_SetLogFile(const char *name); + +/* +** Set the size of the logging buffer. If "buffer_size" is zero then the +** logging becomes "synchronous" (or unbuffered). +*/ +NSPR_API(void) PR_SetLogBuffering(PRIntn buffer_size); + +/* +** Print a string to the log. "fmt" is a PR_snprintf format type. All +** messages printed to the log are preceeded by the name of the thread +** and a time stamp. Also, the routine provides a missing newline if one +** is not provided. +*/ +NSPR_API(void) PR_LogPrint(const char *fmt, ...); + +/* +** Flush the log to its file. +*/ +NSPR_API(void) PR_LogFlush(void); + +NSPR_API(void) PR_Assert(const char *s, const char *file, PRIntn ln) +PR_PRETEND_NORETURN; + +#if defined(DEBUG) || defined(FORCE_PR_LOG) +#define PR_LOGGING 1 + +#define PR_LOG_TEST(_module,_level) \ + ((_module)->level >= (_level)) + +/* +** Log something. +** "module" is the address of a PRLogModuleInfo structure +** "level" is the desired logging level +** "args" is a variable length list of arguments to print, in the following +** format: ("printf style format string", ...) +*/ +#define PR_LOG(_module,_level,_args) \ + PR_BEGIN_MACRO \ + if (PR_LOG_TEST(_module,_level)) { \ + PR_LogPrint _args; \ + } \ + PR_END_MACRO + +#else /* defined(DEBUG) || defined(FORCE_PR_LOG) */ + +#undef PR_LOGGING +#define PR_LOG_TEST(module,level) 0 +#define PR_LOG(module,level,args) + +#endif /* defined(DEBUG) || defined(FORCE_PR_LOG) */ + +#ifndef NO_NSPR_10_SUPPORT + +#ifdef PR_LOGGING +#define PR_LOG_BEGIN PR_LOG +#define PR_LOG_END PR_LOG +#define PR_LOG_DEFINE PR_NewLogModule +#else +#define PR_LOG_BEGIN(module,level,args) +#define PR_LOG_END(module,level,args) +#define PR_LOG_DEFINE(_name) NULL +#endif /* PR_LOGGING */ + +#endif /* NO_NSPR_10_SUPPORT */ + +#if defined(DEBUG) || defined(FORCE_PR_ASSERT) + +#define PR_ASSERT(_expr) \ + ((_expr)?((void)0):PR_Assert(# _expr,__FILE__,__LINE__)) + +#define PR_ASSERT_ARG(_expr) PR_ASSERT(_expr) + +#define PR_NOT_REACHED(_reasonStr) \ + PR_Assert(_reasonStr,__FILE__,__LINE__) + +#else + +#define PR_ASSERT(expr) ((void) 0) +/* PR_ASSERT_ARG avoids compiler warning: unused variable */ +#define PR_ASSERT_ARG(expr) ((void)(0 && (expr))) +#define PR_NOT_REACHED(reasonStr) + +#endif /* defined(DEBUG) || defined(FORCE_PR_ASSERT) */ + +PR_END_EXTERN_C + +#endif /* prlog_h___ */ diff --git a/nsprpub/pr/include/prlong.h b/nsprpub/pr/include/prlong.h new file mode 100644 index 0000000000..7cc1d567d1 --- /dev/null +++ b/nsprpub/pr/include/prlong.h @@ -0,0 +1,403 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prlong.h +** Description: Portable access to 64 bit numerics +** +** Long-long (64-bit signed integer type) support. Some C compilers +** don't support 64 bit integers yet, so we use these macros to +** support both machines that do and don't. +**/ +#ifndef prlong_h___ +#define prlong_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/*********************************************************************** +** DEFINES: LL_MaxInt +** LL_MinInt +** LL_Zero +** LL_MaxUint +** DESCRIPTION: +** Various interesting constants and static variable +** initializer +***********************************************************************/ +NSPR_API(PRInt64) LL_MaxInt(void); +NSPR_API(PRInt64) LL_MinInt(void); +NSPR_API(PRInt64) LL_Zero(void); +NSPR_API(PRUint64) LL_MaxUint(void); + +#if defined(HAVE_LONG_LONG) + +/* Keep this in sync with prtypes.h. */ +#if PR_BYTES_PER_LONG == 8 && !defined(PR_ALTERNATE_INT64_TYPEDEF) +#define LL_MAXINT 9223372036854775807L +#define LL_MININT (-LL_MAXINT - 1L) +#define LL_ZERO 0L +#define LL_MAXUINT 18446744073709551615UL +#define LL_INIT(hi, lo) ((hi ## L << 32) + lo ## L) +#elif defined(WIN32) && !defined(__GNUC__) +#define LL_MAXINT 9223372036854775807i64 +#define LL_MININT (-LL_MAXINT - 1i64) +#define LL_ZERO 0i64 +#define LL_MAXUINT 18446744073709551615ui64 +#define LL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64) +#else +#define LL_MAXINT 9223372036854775807LL +#define LL_MININT (-LL_MAXINT - 1LL) +#define LL_ZERO 0LL +#define LL_MAXUINT 18446744073709551615ULL +#define LL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL) +#endif + +/*********************************************************************** +** MACROS: LL_* +** DESCRIPTION: +** The following macros define portable access to the 64 bit +** math facilities. +** +***********************************************************************/ + +/*********************************************************************** +** MACROS: LL_<relational operators> +** +** LL_IS_ZERO Test for zero +** LL_EQ Test for equality +** LL_NE Test for inequality +** LL_GE_ZERO Test for zero or positive +** LL_CMP Compare two values +***********************************************************************/ +#define LL_IS_ZERO(a) ((a) == 0) +#define LL_EQ(a, b) ((a) == (b)) +#define LL_NE(a, b) ((a) != (b)) +#define LL_GE_ZERO(a) ((a) >= 0) +#define LL_CMP(a, op, b) ((PRInt64)(a) op (PRInt64)(b)) +#define LL_UCMP(a, op, b) ((PRUint64)(a) op (PRUint64)(b)) + +/*********************************************************************** +** MACROS: LL_<logical operators> +** +** LL_AND Logical and +** LL_OR Logical or +** LL_XOR Logical exclusion +** LL_OR2 A disgusting deviation +** LL_NOT Negation (one's complement) +***********************************************************************/ +#define LL_AND(r, a, b) ((r) = (a) & (b)) +#define LL_OR(r, a, b) ((r) = (a) | (b)) +#define LL_XOR(r, a, b) ((r) = (a) ^ (b)) +#define LL_OR2(r, a) ((r) = (r) | (a)) +#define LL_NOT(r, a) ((r) = ~(a)) + +/*********************************************************************** +** MACROS: LL_<mathematical operators> +** +** LL_NEG Negation (two's complement) +** LL_ADD Summation (two's complement) +** LL_SUB Difference (two's complement) +***********************************************************************/ +#define LL_NEG(r, a) ((r) = -(a)) +#define LL_ADD(r, a, b) ((r) = (a) + (b)) +#define LL_SUB(r, a, b) ((r) = (a) - (b)) + +/*********************************************************************** +** MACROS: LL_<mathematical operators> +** +** LL_MUL Product (two's complement) +** LL_DIV Quotient (two's complement) +** LL_MOD Modulus (two's complement) +***********************************************************************/ +#define LL_MUL(r, a, b) ((r) = (a) * (b)) +#define LL_DIV(r, a, b) ((r) = (a) / (b)) +#define LL_MOD(r, a, b) ((r) = (a) % (b)) + +/*********************************************************************** +** MACROS: LL_<shifting operators> +** +** LL_SHL Shift left [0..64] bits +** LL_SHR Shift right [0..64] bits with sign extension +** LL_USHR Unsigned shift right [0..64] bits +** LL_ISHL Signed shift left [0..64] bits +***********************************************************************/ +#define LL_SHL(r, a, b) ((r) = (PRInt64)(a) << (b)) +#define LL_SHR(r, a, b) ((r) = (PRInt64)(a) >> (b)) +#define LL_USHR(r, a, b) ((r) = (PRUint64)(a) >> (b)) +#define LL_ISHL(r, a, b) ((r) = (PRInt64)(a) << (b)) + +/*********************************************************************** +** MACROS: LL_<conversion operators> +** +** LL_L2I Convert to signed 32 bit +** LL_L2UI Convert to unsigned 32 bit +** LL_L2F Convert to floating point +** LL_L2D Convert to floating point +** LL_I2L Convert signed to 64 bit +** LL_UI2L Convert unsigned to 64 bit +** LL_F2L Convert float to 64 bit +** LL_D2L Convert float to 64 bit +***********************************************************************/ +#define LL_L2I(i, l) ((i) = (PRInt32)(l)) +#define LL_L2UI(ui, l) ((ui) = (PRUint32)(l)) +#define LL_L2F(f, l) ((f) = (PRFloat64)(l)) +#define LL_L2D(d, l) ((d) = (PRFloat64)(l)) + +#define LL_I2L(l, i) ((l) = (PRInt64)(i)) +#define LL_UI2L(l, ui) ((l) = (PRInt64)(ui)) +#define LL_F2L(l, f) ((l) = (PRInt64)(f)) +#define LL_D2L(l, d) ((l) = (PRInt64)(d)) + +/*********************************************************************** +** MACROS: LL_UDIVMOD +** DESCRIPTION: +** Produce both a quotient and a remainder given an unsigned +** INPUTS: PRUint64 a: The dividend of the operation +** PRUint64 b: The quotient of the operation +** OUTPUTS: PRUint64 *qp: pointer to quotient +** PRUint64 *rp: pointer to remainder +***********************************************************************/ +#define LL_UDIVMOD(qp, rp, a, b) \ + (*(qp) = ((PRUint64)(a) / (b)), \ + *(rp) = ((PRUint64)(a) % (b))) + +#else /* !HAVE_LONG_LONG */ + +#define LL_MAXINT LL_MaxInt() +#define LL_MININT LL_MinInt() +#define LL_ZERO LL_Zero() +#define LL_MAXUINT LL_MaxUint() + +#ifdef IS_LITTLE_ENDIAN +#define LL_INIT(hi, lo) {PR_UINT32(lo), PR_UINT32(hi)} +#else +#define LL_INIT(hi, lo) {PR_UINT32(hi), PR_UINT32(lo)} +#endif + +#define LL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0)) +#define LL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo)) +#define LL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo)) +#define LL_GE_ZERO(a) (((a).hi >> 31) == 0) + +#define LL_CMP(a, op, b) (((a).hi == (b).hi) ? ((a).lo op (b).lo) : \ + ((PRInt32)(a).hi op (PRInt32)(b).hi)) +#define LL_UCMP(a, op, b) (((a).hi == (b).hi) ? ((a).lo op (b).lo) : \ + ((a).hi op (b).hi)) + +#define LL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \ + (r).hi = (a).hi & (b).hi) +#define LL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \ + (r).hi = (a).hi | (b).hi) +#define LL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \ + (r).hi = (a).hi ^ (b).hi) +#define LL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \ + (r).hi = (r).hi | (a).hi) +#define LL_NOT(r, a) ((r).lo = ~(a).lo, \ + (r).hi = ~(a).hi) + +#define LL_NEG(r, a) ((r).lo = -(PRInt32)(a).lo, \ + (r).hi = -(PRInt32)(a).hi - ((r).lo != 0)) +#define LL_ADD(r, a, b) { \ + PRInt64 _a, _b; \ + _a = a; _b = b; \ + (r).lo = _a.lo + _b.lo; \ + (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \ +} + +#define LL_SUB(r, a, b) { \ + PRInt64 _a, _b; \ + _a = a; _b = b; \ + (r).lo = _a.lo - _b.lo; \ + (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \ +} + +#define LL_MUL(r, a, b) { \ + PRInt64 _a, _b; \ + _a = a; _b = b; \ + LL_MUL32(r, _a.lo, _b.lo); \ + (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \ +} + +#define _lo16(a) ((a) & PR_BITMASK(16)) +#define _hi16(a) ((a) >> 16) + +#define LL_MUL32(r, a, b) { \ + PRUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \ + _a1 = _hi16(a), _a0 = _lo16(a); \ + _b1 = _hi16(b), _b0 = _lo16(b); \ + _y0 = _a0 * _b0; \ + _y1 = _a0 * _b1; \ + _y2 = _a1 * _b0; \ + _y3 = _a1 * _b1; \ + _y1 += _hi16(_y0); /* can't carry */ \ + _y1 += _y2; /* might carry */ \ + if (_y1 < _y2) \ + _y3 += (PRUint32)(PR_BIT(16)); /* propagate */ \ + (r).lo = (_lo16(_y1) << 16) + _lo16(_y0); \ + (r).hi = _y3 + _hi16(_y1); \ +} + +#define LL_UDIVMOD(qp, rp, a, b) ll_udivmod(qp, rp, a, b) + +NSPR_API(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b); + +#define LL_DIV(r, a, b) { \ + PRInt64 _a, _b; \ + PRUint32 _negative = (PRInt32)(a).hi < 0; \ + if (_negative) { \ + LL_NEG(_a, a); \ + } else { \ + _a = a; \ + } \ + if ((PRInt32)(b).hi < 0) { \ + _negative ^= 1; \ + LL_NEG(_b, b); \ + } else { \ + _b = b; \ + } \ + LL_UDIVMOD(&(r), 0, _a, _b); \ + if (_negative) \ + LL_NEG(r, r); \ +} + +#define LL_MOD(r, a, b) { \ + PRInt64 _a, _b; \ + PRUint32 _negative = (PRInt32)(a).hi < 0; \ + if (_negative) { \ + LL_NEG(_a, a); \ + } else { \ + _a = a; \ + } \ + if ((PRInt32)(b).hi < 0) { \ + LL_NEG(_b, b); \ + } else { \ + _b = b; \ + } \ + LL_UDIVMOD(0, &(r), _a, _b); \ + if (_negative) \ + LL_NEG(r, r); \ +} + +#define LL_SHL(r, a, b) { \ + if (b) { \ + PRInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = _a.lo << ((b) & 31); \ + (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \ + } else { \ + (r).lo = 0; \ + (r).hi = _a.lo << ((b) & 31); \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +/* a is an PRInt32, b is PRInt32, r is PRInt64 */ +#define LL_ISHL(r, a, b) { \ + if (b) { \ + PRInt64 _a; \ + _a.lo = (a); \ + _a.hi = 0; \ + if ((b) < 32) { \ + (r).lo = (a) << ((b) & 31); \ + (r).hi = ((a) >> (32 - (b))); \ + } else { \ + (r).lo = 0; \ + (r).hi = (a) << ((b) & 31); \ + } \ + } else { \ + (r).lo = (a); \ + (r).hi = 0; \ + } \ +} + +#define LL_SHR(r, a, b) { \ + if (b) { \ + PRInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ + (r).hi = (PRInt32)_a.hi >> ((b) & 31); \ + } else { \ + (r).lo = (PRInt32)_a.hi >> ((b) & 31); \ + (r).hi = (PRInt32)_a.hi >> 31; \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +#define LL_USHR(r, a, b) { \ + if (b) { \ + PRInt64 _a; \ + _a = a; \ + if ((b) < 32) { \ + (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ + (r).hi = _a.hi >> ((b) & 31); \ + } else { \ + (r).lo = _a.hi >> ((b) & 31); \ + (r).hi = 0; \ + } \ + } else { \ + (r) = (a); \ + } \ +} + +#define LL_L2I(i, l) ((i) = (l).lo) +#define LL_L2UI(ui, l) ((ui) = (l).lo) +#define LL_L2F(f, l) { double _d; LL_L2D(_d, l); (f) = (PRFloat64)_d; } + +#define LL_L2D(d, l) { \ + int _negative; \ + PRInt64 _absval; \ + \ + _negative = (l).hi >> 31; \ + if (_negative) { \ + LL_NEG(_absval, l); \ + } else { \ + _absval = l; \ + } \ + (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \ + if (_negative) \ + (d) = -(d); \ +} + +#define LL_I2L(l, i) { PRInt32 _i = ((PRInt32)(i)) >> 31; (l).lo = (i); (l).hi = _i; } +#define LL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0) +#define LL_F2L(l, f) { double _d = (double)f; LL_D2L(l, _d); } + +#define LL_D2L(l, d) { \ + int _negative; \ + double _absval, _d_hi; \ + PRInt64 _lo_d; \ + \ + _negative = ((d) < 0); \ + _absval = _negative ? -(d) : (d); \ + \ + (l).hi = _absval / 4.294967296e9; \ + (l).lo = 0; \ + LL_L2D(_d_hi, l); \ + _absval -= _d_hi; \ + _lo_d.hi = 0; \ + if (_absval < 0) { \ + _lo_d.lo = -_absval; \ + LL_SUB(l, l, _lo_d); \ + } else { \ + _lo_d.lo = _absval; \ + LL_ADD(l, l, _lo_d); \ + } \ + \ + if (_negative) \ + LL_NEG(l, l); \ +} + +#endif /* !HAVE_LONG_LONG */ + +PR_END_EXTERN_C + +#endif /* prlong_h___ */ diff --git a/nsprpub/pr/include/prmem.h b/nsprpub/pr/include/prmem.h new file mode 100644 index 0000000000..ed9a428933 --- /dev/null +++ b/nsprpub/pr/include/prmem.h @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prmem.h +** Description: API to NSPR memory management functions +** +*/ +#ifndef prmem_h___ +#define prmem_h___ + +#include "prtypes.h" +#include <stdlib.h> + +PR_BEGIN_EXTERN_C + +/* +** Thread safe memory allocation. +** +** NOTE: pr wraps up malloc, free, calloc, realloc so they are already +** thread safe (and are not declared here - look in stdlib.h). +*/ + +/* +** PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free have the same signatures +** as their libc equivalent malloc, calloc, realloc, and free, and have +** the same semantics. (Note that the argument type size_t is replaced +** by PRUint32.) Memory allocated by PR_Malloc, PR_Calloc, or PR_Realloc +** must be freed by PR_Free. +*/ + +NSPR_API(void *) PR_Malloc(PRUint32 size); + +NSPR_API(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize); + +NSPR_API(void *) PR_Realloc(void *ptr, PRUint32 size); + +NSPR_API(void) PR_Free(void *ptr); + +/* +** The following are some convenience macros defined in terms of +** PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free. +*/ + +/*********************************************************************** +** FUNCTION: PR_MALLOC() +** DESCRIPTION: +** PR_NEW() allocates an untyped item of size _size from the heap. +** INPUTS: _size: size in bytes of item to be allocated +** OUTPUTS: untyped pointer to the node allocated +** RETURN: pointer to node or error returned from malloc(). +***********************************************************************/ +#define PR_MALLOC(_bytes) (PR_Malloc((_bytes))) + +/*********************************************************************** +** FUNCTION: PR_NEW() +** DESCRIPTION: +** PR_NEW() allocates an item of type _struct from the heap. +** INPUTS: _struct: a data type +** OUTPUTS: pointer to _struct +** RETURN: pointer to _struct or error returns from malloc(). +***********************************************************************/ +#define PR_NEW(_struct) ((_struct *) PR_MALLOC(sizeof(_struct))) + +/*********************************************************************** +** FUNCTION: PR_REALLOC() +** DESCRIPTION: +** PR_REALLOC() re-allocates _ptr bytes from the heap as a _size +** untyped item. +** INPUTS: _ptr: pointer to node to reallocate +** _size: size of node to allocate +** OUTPUTS: pointer to node allocated +** RETURN: pointer to node allocated +***********************************************************************/ +#define PR_REALLOC(_ptr, _size) (PR_Realloc((_ptr), (_size))) + +/*********************************************************************** +** FUNCTION: PR_CALLOC() +** DESCRIPTION: +** PR_CALLOC() allocates a _size bytes untyped item from the heap +** and sets the allocated memory to all 0x00. +** INPUTS: _size: size of node to allocate +** OUTPUTS: pointer to node allocated +** RETURN: pointer to node allocated +***********************************************************************/ +#define PR_CALLOC(_size) (PR_Calloc(1, (_size))) + +/*********************************************************************** +** FUNCTION: PR_NEWZAP() +** DESCRIPTION: +** PR_NEWZAP() allocates an item of type _struct from the heap +** and sets the allocated memory to all 0x00. +** INPUTS: _struct: a data type +** OUTPUTS: pointer to _struct +** RETURN: pointer to _struct +***********************************************************************/ +#define PR_NEWZAP(_struct) ((_struct*)PR_Calloc(1, sizeof(_struct))) + +/*********************************************************************** +** FUNCTION: PR_DELETE() +** DESCRIPTION: +** PR_DELETE() unallocates an object previosly allocated via PR_NEW() +** or PR_NEWZAP() to the heap. +** INPUTS: pointer to previously allocated object +** OUTPUTS: the referenced object is returned to the heap +** RETURN: void +***********************************************************************/ +#define PR_DELETE(_ptr) { PR_Free(_ptr); (_ptr) = NULL; } + +/*********************************************************************** +** FUNCTION: PR_FREEIF() +** DESCRIPTION: +** PR_FREEIF() conditionally unallocates an object previously allocated +** vial PR_NEW() or PR_NEWZAP(). If the pointer to the object is +** equal to zero (0), the object is not released. +** INPUTS: pointer to previously allocated object +** OUTPUTS: the referenced object is conditionally returned to the heap +** RETURN: void +***********************************************************************/ +#define PR_FREEIF(_ptr) if (_ptr) PR_DELETE(_ptr) + +PR_END_EXTERN_C + +#endif /* prmem_h___ */ diff --git a/nsprpub/pr/include/prmon.h b/nsprpub/pr/include/prmon.h new file mode 100644 index 0000000000..374e298265 --- /dev/null +++ b/nsprpub/pr/include/prmon.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prmon_h___ +#define prmon_h___ + +#include "prtypes.h" +#include "prinrval.h" + +PR_BEGIN_EXTERN_C + +typedef struct PRMonitor PRMonitor; + +/* +** Create a new monitor. Monitors are re-entrant locks with a single built-in +** condition variable. +** +** This may fail if memory is tight or if some operating system resource +** is low. +*/ +NSPR_API(PRMonitor*) PR_NewMonitor(void); + +/* +** Destroy a monitor. The caller is responsible for guaranteeing that the +** monitor is no longer in use. There must be no thread waiting on the monitor's +** condition variable and that the lock is not held. +** +*/ +NSPR_API(void) PR_DestroyMonitor(PRMonitor *mon); + +/* +** Enter the lock associated with the monitor. If the calling thread currently +** is in the monitor, the call to enter will silently succeed. In either case, +** it will increment the entry count by one. +*/ +NSPR_API(void) PR_EnterMonitor(PRMonitor *mon); + +/* +** Decrement the entry count associated with the monitor. If the decremented +** entry count is zero, the monitor is exited. Returns PR_FAILURE if the +** calling thread has not entered the monitor. +*/ +NSPR_API(PRStatus) PR_ExitMonitor(PRMonitor *mon); + +/* +** Wait for a notify on the monitor's condition variable. Sleep for "ticks" +** amount of time (if "ticks" is PR_INTERVAL_NO_TIMEOUT then the sleep is +** indefinite). +** +** While the thread is waiting it exits the monitor (as if it called +** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When +** the wait has finished the thread regains control of the monitors lock +** with the same entry count as before the wait began. +** +** The thread waiting on the monitor will be resumed when the monitor is +** notified (assuming the thread is the next in line to receive the +** notify) or when the "ticks" timeout elapses. +** +** Returns PR_FAILURE if the caller has not entered the monitor. +*/ +NSPR_API(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks); + +/* +** Notify a thread waiting on the monitor's condition variable. If a thread +** is waiting on the condition variable (using PR_Wait) then it is awakened +** and attempts to reenter the monitor. +*/ +NSPR_API(PRStatus) PR_Notify(PRMonitor *mon); + +/* +** Notify all of the threads waiting on the monitor's condition variable. +** All of threads waiting on the condition are scheduled to reenter the +** monitor. +*/ +NSPR_API(PRStatus) PR_NotifyAll(PRMonitor *mon); + +/* +** PR_ASSERT_CURRENT_THREAD_IN_MONITOR +** If the current thread is in |mon|, this assertion is guaranteed to +** succeed. Otherwise, the behavior of this function is undefined. +*/ +#if defined(DEBUG) || defined(FORCE_PR_ASSERT) +#define PR_ASSERT_CURRENT_THREAD_IN_MONITOR(/* PRMonitor* */ mon) \ + PR_AssertCurrentThreadInMonitor(mon) +#else +#define PR_ASSERT_CURRENT_THREAD_IN_MONITOR(/* PRMonitor* */ mon) +#endif + +/* Don't call this function directly. */ +NSPR_API(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon); + +PR_END_EXTERN_C + +#endif /* prmon_h___ */ diff --git a/nsprpub/pr/include/prmwait.h b/nsprpub/pr/include/prmwait.h new file mode 100644 index 0000000000..16c0762199 --- /dev/null +++ b/nsprpub/pr/include/prmwait.h @@ -0,0 +1,380 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(_PRMWAIT_H) +#else +#define _PRMWAIT_H + +#include "prio.h" +#include "prtypes.h" +#include "prclist.h" + +PR_BEGIN_EXTERN_C + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/****************************** WARNING ****************************/ +/********************************************************************************/ +/**************************** This is work in progress. *************************/ +/************************** Do not make any assumptions *************************/ +/************************** about the stability of this *************************/ +/************************** API or the underlying imple- ************************/ +/************************** mentation. ************************/ +/********************************************************************************/ +/********************************************************************************/ + +/* +** STRUCTURE: PRWaitGroup +** DESCRIPTION: +** The client may define several wait groups in order to semantically +** tie a collection of file descriptors for a single purpose. This allows +** easier dispatching of threads that returned with active file descriptors +** from the wait function. +*/ +typedef struct PRWaitGroup PRWaitGroup; + +/* +** ENUMERATION: PRMWStatus +** DESCRIPTION: +** This enumeration is used to indicate the completion status of +** a receive wait object. Generally stated, a positive value indicates +** that the operation is not yet complete. A zero value indicates +** success (similar to PR_SUCCESS) and any negative value is an +** indication of failure. The reason for the failure can be retrieved +** by calling PR_GetError(). +** +** PR_MW_PENDING The operation is still pending. None of the other +** fields of the object are currently valid. +** PR_MW_SUCCESS The operation is complete and it was successful. +** PR_MW_FAILURE The operation failed. The reason for the failure +** can be retrieved by calling PR_GetError(). +** PR_MW_TIMEOUT The amount of time allowed for by the object's +** 'timeout' field has expired w/o the operation +** otherwise coming to closure. +** PR_MW_INTERRUPT The operation was cancelled, either by the client +** calling PR_CancelWaitFileDesc() or destroying the +** entire wait group (PR_DestroyWaitGroup()). +*/ +typedef enum PRMWStatus +{ + PR_MW_PENDING = 1, + PR_MW_SUCCESS = 0, + PR_MW_FAILURE = -1, + PR_MW_TIMEOUT = -2, + PR_MW_INTERRUPT = -3 +} PRMWStatus; + +/* +** STRUCTURE: PRMemoryDescriptor +** DESCRIPTION: +** THis is a descriptor for an interval of memory. It contains a +** pointer to the first byte of that memory and the length (in +** bytes) of the interval. +*/ +typedef struct PRMemoryDescriptor +{ + void *start; /* pointer to first byte of memory */ + PRSize length; /* length (in bytes) of memory interval */ +} PRMemoryDescriptor; + +/* +** STRUCTURE: PRMWaitClientData +** DESCRIPTION: +** An opague stucture for which a client MAY give provide a concrete +** definition and associate with a receive descriptor. The NSPR runtime +** does not manage this field. It is completely up to the client. +*/ +typedef struct PRMWaitClientData PRMWaitClientData; + +/* +** STRUCTURE: PRRecvWait +** DESCRIPTION: +** A receive wait object contains the file descriptor that is subject +** to the wait and the amount of time (beginning epoch established +** when the object is presented to the runtime) the the channel should +** block before abandoning the process. +** +** The success of the wait operation will be noted in the object's +** 'outcome' field. The fields are not valid when the NSPR runtime +** is in possession of the object. +** +** The memory descriptor describes an interval of writable memory +** in the caller's address space where data from an initial read +** can be placed. The description may indicate a null interval. +*/ +typedef struct PRRecvWait +{ + PRCList internal; /* internal runtime linkages */ + + PRFileDesc *fd; /* file descriptor associated w/ object */ + PRMWStatus outcome; /* outcome of the current/last operation */ + PRIntervalTime timeout; /* time allowed for entire operation */ + + PRInt32 bytesRecv; /* number of bytes transferred into buffer */ + PRMemoryDescriptor buffer; /* where to store first segment of input data */ + PRMWaitClientData *client; /* pointer to arbitrary client defined data */ +} PRRecvWait; + +/* +** STRUCTURE: PRMWaitEnumerator +** DESCRIPTION: +** An enumeration object is used to store the state of an existing +** enumeration over a wait group. The opaque object must be allocated +** by the client and the reference presented on each call to the +** pseudo-stateless enumerator. The enumeration objects are sharable +** only in serial fashion. +*/ +typedef struct PRMWaitEnumerator PRMWaitEnumerator; + + +/* +** FUNCTION: PR_AddWaitFileDesc +** DESCRIPTION: +** This function will effectively add a file descriptor to the +** list of those waiting for network receive. The new descriptor +** will be semantically tied to the wait group specified. +** +** The ownership for the storage pointed to by 'desc' is temporarily +** passed over the the NSPR runtime. It will be handed back by the +** function PR_WaitRecvReady(). +** +** INPUTS +** group A reference to a PRWaitGroup or NULL. Wait groups are +** created by calling PR_CreateWaitGroup() and are used +** to semantically group various file descriptors by the +** client's application. +** desc A reference to a valid PRRecvWait. The object of the +** reference must be preserved and not be modified +** until its ownership is returned to the client. +** RETURN +** PRStatus An indication of success. If equal to PR_FAILUE details +** of the failure are avaiable via PR_GetError(). +** +** ERRORS +** PR_INVALID_ARGUMENT_ERROR +** Invalid 'group' identifier or duplicate 'desc' object. +** PR_OUT_OF_MEMORY_ERROR +** Insuffient memory for internal data structures. +** PR_INVALID_STATE_ERROR +** The group is being destroyed. +*/ +NSPR_API(PRStatus) PR_AddWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc); + +/* +** FUNCTION: PR_WaitRecvReady +** DESCRIPTION: +** PR_WaitRecvReady will block the calling thread until one of the +** file descriptors that have been added via PR_AddWaitFileDesc is +** available for input I/O. +** INPUT +** group A pointer to a valid PRWaitGroup or NULL (the null +** group. The function will block the caller until a +** channel from the wait group becomes ready for receive +** or there is some sort of error. +** RETURN +** PRReciveWait +** When the caller is resumed it is either returned a +** valid pointer to a previously added receive wait or +** a NULL. If the latter, the function has terminated +** for a reason that can be determined by calling +** PR_GetError(). +** If a valid pointer is returned, the reference is to the +** file descriptor contained in the receive wait object. +** The outcome of the wait operation may still fail, and +** if it has, that fact will be noted in the object's +** outcome field. Details can be retrieved from PR_GetError(). +** +** ERRORS +** PR_INVALID_ARGUMENT_ERROR +** The 'group' is not known by the runtime. +** PR_PENDING_INTERRUPT_ERROR + The thread was interrupted. +** PR_INVALID_STATE_ERROR +** The group is being destroyed. +*/ +NSPR_API(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group); + +/* +** FUNCTION: PR_CancelWaitFileDesc +** DESCRIPTION: +** PR_CancelWaitFileDesc is provided as a means for cancelling operations +** on objects previously submitted by use of PR_AddWaitFileDesc(). If +** the runtime knows of the object, it will be marked as having failed +** because it was interrupted (similar to PR_Interrupt()). The first +** available thread waiting on the group will be made to return the +** PRRecvWait object with the outcome noted. +** +** INPUTS +** group The wait group under which the wait receive object was +** added. +** desc A pointer to the wait receive object that is to be +** cancelled. +** RETURN +** PRStatus If the wait receive object was located and associated +** with the specified wait group, the status returned will +** be PR_SUCCESS. There is still a race condition that would +** permit the offected object to complete normally, but it +** is assured that it will complete in the near future. +** If the receive object or wait group are invalid, the +** function will return with a status of PR_FAILURE. +** +** ERRORS +** PR_INVALID_ARGUMENT_ERROR +** The 'group' argument is not recognized as a valid group. +** PR_COLLECTION_EMPTY_ERROR +** There are no more receive wait objects in the group's +** collection. +** PR_INVALID_STATE_ERROR +** The group is being destroyed. +*/ +NSPR_API(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc); + +/* +** FUNCTION: PR_CancelWaitGroup +** DESCRIPTION: +** PR_CancelWaitGroup is provided as a means for cancelling operations +** on objects previously submitted by use of PR_AddWaitFileDesc(). Each +** successive call will return a pointer to a PRRecvWait object that +** was previously registered via PR_AddWaitFileDesc(). If no wait +** objects are associated with the wait group, a NULL will be returned. +** This function should be called in a loop until a NULL is returned +** to reclaim all the wait objects prior to calling PR_DestroyWaitGroup(). +** +** INPUTS +** group The wait group under which the wait receive object was +** added. +** RETURN +** PRRecvWait* If the wait group is valid and at least one receive wait +** object is present in the group, that object will be +** marked as PR_MW_INTERRUPT'd and removed from the group's +** queues. Otherwise a NULL will be returned and the reason +** for the NULL may be retrieved by calling PR_GetError(). +** +** ERRORS +** PR_INVALID_ARGUMENT_ERROR +** PR_GROUP_EMPTY_ERROR +*/ +NSPR_API(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group); + +/* +** FUNCTION: PR_CreateWaitGroup +** DESCRIPTION: +** A wait group is an opaque object that a client may create in order +** to semantically group various wait requests. Each wait group is +** unique, including the default wait group (NULL). A wait request +** that was added under a wait group will only be serviced by a caller +** that specified the same wait group. +** +** INPUT +** size The size of the hash table to be used to contain the +** receive wait objects. This is just the initial size. +** It will grow as it needs to, but to avoid that hassle +** one can suggest a suitable size initially. It should +** be ~30% larger than the maximum number of receive wait +** objects expected. +** RETURN +** PRWaitGroup If successful, the function will return a pointer to an +** object that was allocated by and owned by the runtime. +** The reference remains valid until it is explicitly destroyed +** by calling PR_DestroyWaitGroup(). +** +** ERRORS +** PR_OUT_OF_MEMORY_ERROR +*/ +NSPR_API(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size); + +/* +** FUNCTION: PR_DestroyWaitGroup +** DESCRIPTION: +** Undo the effects of PR_CreateWaitGroup(). Any receive wait operations +** on the group will be treated as if the each had been the target of a +** PR_CancelWaitFileDesc(). +** +** INPUT +** group Reference to a wait group previously allocated using +** PR_CreateWaitGroup(). +** RETURN +** PRStatus Will be PR_SUCCESS if the wait group was valid and there +** are no receive wait objects in that group. Otherwise +** will indicate PR_FAILURE. +** +** ERRORS +** PR_INVALID_ARGUMENT_ERROR +** The 'group' argument does not reference a known object. +** PR_INVALID_STATE_ERROR +** The group still contains receive wait objects. +*/ +NSPR_API(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group); + +/* +** FUNCTION: PR_CreateMWaitEnumerator +** DESCRIPTION: +** The PR_CreateMWaitEnumerator() function returns a reference to an +** opaque PRMWaitEnumerator object. The enumerator object is required +** as an argument for each successive call in the stateless enumeration +** of the indicated wait group. +** +** group The wait group that the enumeration is intended to +** process. It may be be the default wait group (NULL). +** RETURN +** PRMWaitEnumerator* group +** A reference to an object that will be used to store +** intermediate state of enumerations. +** ERRORS +** Errors are indicated by the function returning a NULL. +** PR_INVALID_ARGUMENT_ERROR +** The 'group' argument does not reference a known object. +** PR_OUT_OF_MEMORY_ERROR +*/ +NSPR_API(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup *group); + +/* +** FUNCTION: PR_DestroyMWaitEnumerator +** DESCRIPTION: +** Destroys the object created by PR_CreateMWaitEnumerator(). The reference +** used as an argument becomes invalid. +** +** INPUT +** PRMWaitEnumerator* enumerator +** The PRMWaitEnumerator object to destroy. +** RETURN +** PRStatus +** PR_SUCCESS if successful, PR_FAILURE otherwise. +** ERRORS +** PR_INVALID_ARGUMENT_ERROR +** The enumerator is invalid. +*/ +NSPR_API(PRStatus) PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator); + +/* +** FUNCTION: PR_EnumerateWaitGroup +** DESCRIPTION: +** PR_EnumerateWaitGroup is a thread safe enumerator over a wait group. +** Each call to the enumerator must present a valid PRMWaitEnumerator +** rererence and a pointer to the "previous" element returned from the +** enumeration process or a NULL. +** +** An enumeration is started by passing a NULL as the "previous" value. +** Subsequent calls to the enumerator must pass in the result of the +** previous call. The enumeration end is signaled by the runtime returning +** a NULL as the result. +** +** Modifications to the content of the wait group are allowed during +** an enumeration. The effect is that the enumeration may have to be +** "reset" and that may result in duplicates being returned from the +** enumeration. +** +** An enumeration may be abandoned at any time. The runtime is not +** keeping any state, so there are no issues in that regard. +*/ +NSPR_API(PRRecvWait*) PR_EnumerateWaitGroup( + PRMWaitEnumerator *enumerator, const PRRecvWait *previous); + +PR_END_EXTERN_C + +#endif /* defined(_PRMWAIT_H) */ + +/* prmwait.h */ diff --git a/nsprpub/pr/include/prnetdb.h b/nsprpub/pr/include/prnetdb.h new file mode 100644 index 0000000000..023a46e5df --- /dev/null +++ b/nsprpub/pr/include/prnetdb.h @@ -0,0 +1,475 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prnetdb_h___ +#define prnetdb_h___ + +#include "prtypes.h" +#include "prio.h" + +PR_BEGIN_EXTERN_C + + +/* + ********************************************************************* + * Translate an Internet address to/from a character string + ********************************************************************* + */ +NSPR_API(PRStatus) PR_StringToNetAddr( + const char *string, PRNetAddr *addr); + +NSPR_API(PRStatus) PR_NetAddrToString( + const PRNetAddr *addr, char *string, PRUint32 size); + +/* +** Structures returned by network data base library. All addresses are +** supplied in host order, and returned in network order (suitable for +** use in system calls). +*/ +/* +** Beware that WINSOCK.H defines h_addrtype and h_length as short. +** Client code does direct struct copies of hostent to PRHostEnt and +** hence the ifdef. +*/ +typedef struct PRHostEnt { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ +#ifdef WIN32 + PRInt16 h_addrtype; /* host address type */ + PRInt16 h_length; /* length of address */ +#else + PRInt32 h_addrtype; /* host address type */ + PRInt32 h_length; /* length of address */ +#endif + char **h_addr_list; /* list of addresses from name server */ +} PRHostEnt; + +/* A safe size to use that will mostly work... */ +#if (defined(AIX) && defined(_THREAD_SAFE)) +#define PR_NETDB_BUF_SIZE sizeof(struct protoent_data) +#define PR_MIN_NETDB_BUF_SIZE PR_NETDB_BUF_SIZE +#else +/* PR_NETDB_BUF_SIZE is the recommended buffer size */ +#define PR_NETDB_BUF_SIZE 2048 +/* PR_MIN_NETDB_BUF_SIZE is the smallest buffer size that the API + * accepts (for backward compatibility). */ +#define PR_MIN_NETDB_BUF_SIZE 1024 +#endif + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_GetHostByName() +** Lookup a host by name. +** +** INPUTS: +** char *hostname Character string defining the host name of interest +** 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(). +***********************************************************************/ +NSPR_API(PRStatus) PR_GetHostByName( + const char *hostname, char *buf, PRIntn bufsize, PRHostEnt *hostentry); + +/*********************************************************************** +** 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_NOCANONNAME 0x8000 +#define PR_AI_DEFAULT (PR_AI_V4MAPPED | PR_AI_ADDRCONFIG) + +NSPR_API(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. +** +** INPUTS: +** char *hostaddr IP address of host in question +** 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(). +***********************************************************************/ +NSPR_API(PRStatus) PR_GetHostByAddr( + const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry); + +/*********************************************************************** +** FUNCTION: PR_EnumerateHostEnt() +** DESCRIPTION: +** A stateless enumerator over a PRHostEnt structure acquired from +** PR_GetHostByName() PR_GetHostByAddr() to evaluate the possible +** network addresses. +** +** INPUTS: +** PRIntn enumIndex Index of the enumeration. The enumeration starts +** and ends with a value of zero. +** +** PRHostEnt *hostEnt A pointer to a host entry struct that was +** previously returned by PR_GetHostByName() or +** PR_GetHostByAddr(). +** +** PRUint16 port The port number to be assigned as part of the +** PRNetAddr. +** +** OUTPUTS: +** PRNetAddr *address A pointer to an address structure that will be +** filled in by the call to the enumeration if the +** result of the call is greater than zero. +** +** RETURN: +** PRIntn The value that should be used for the next call +** of the enumerator ('enumIndex'). The enumeration +** is ended if this value is returned zero. +** If a value of -1 is returned, the enumeration +** has failed. The reason for the failure can be +** retrieved by calling PR_GetError(). +***********************************************************************/ +NSPR_API(PRIntn) PR_EnumerateHostEnt( + PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address); + +/*********************************************************************** +** FUNCTION: PR_InitializeNetAddr(), +** DESCRIPTION: +** Initialize the fields of a PRNetAddr, assigning well known values as +** appropriate. +** +** 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 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(); +***********************************************************************/ +typedef enum PRNetAddrValue +{ + PR_IpAddrNull, /* do NOT overwrite the IP address */ + PR_IpAddrAny, /* assign logical INADDR_ANY to IP address */ + PR_IpAddrLoopback, /* assign logical INADDR_LOOPBACK */ + PR_IpAddrV4Mapped /* IPv4 mapped address */ +} PRNetAddrValue; + +NSPR_API(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(); +***********************************************************************/ +NSPR_API(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. +***********************************************************************/ +NSPR_API(PRBool) PR_IsNetAddrType(const PRNetAddr *addr, PRNetAddrValue val); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_ConvertIPv4AddrToIPv6() +** Convert an IPv4 addr to an (IPv4-mapped) IPv6 addr +** +** INPUTS: +** PRUint32 v4addr IPv4 address +** +** OUTPUTS: +** PRIPv6Addr *v6addr The converted IPv6 address +** +** RETURN: +** void +** +***********************************************************************/ +NSPR_API(void) PR_ConvertIPv4AddrToIPv6(PRUint32 v4addr, PRIPv6Addr *v6addr); + +/*********************************************************************** +** 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'. +***********************************************************************/ +#define PR_NetAddrInetPort(addr) \ + ((addr)->raw.family == PR_AF_INET6 ? (addr)->ipv6.port : (addr)->inet.port) + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_GetProtoByName() +** Lookup a protocol entry based on protocol's name +** +** INPUTS: +** char *protocolname Character string of the protocol's name. +** 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 *PRProtoEnt +** 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(). +***********************************************************************/ + +typedef struct PRProtoEnt { + char *p_name; /* official protocol name */ + char **p_aliases; /* alias list */ +#ifdef WIN32 + PRInt16 p_num; /* protocol # */ +#else + PRInt32 p_num; /* protocol # */ +#endif +} PRProtoEnt; + +NSPR_API(PRStatus) PR_GetProtoByName( + const char* protocolname, char* buffer, PRInt32 bufsize, PRProtoEnt* result); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_GetProtoByNumber() +** Lookup a protocol entry based on protocol's number +** +** INPUTS: +** PRInt32 protocolnumber +** Number assigned to the protocol. +** 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 *PRProtoEnt +** 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(). +***********************************************************************/ +NSPR_API(PRStatus) PR_GetProtoByNumber( + PRInt32 protocolnumber, char* buffer, PRInt32 bufsize, PRProtoEnt* result); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_GetAddrInfoByName() +** Look up a host by name. Equivalent to getaddrinfo(host, NULL, ...) of +** RFC 3493. +** +** INPUTS: +** char *hostname Character string defining the host name of interest +** PRUint16 af May be PR_AF_UNSPEC or PR_AF_INET. +** PRIntn flags May be either PR_AI_ADDRCONFIG or +** PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include +** PR_AI_NOCANONNAME to suppress the determination of +** the canonical name corresponding to hostname. +** RETURN: +** PRAddrInfo* Handle to a data structure containing the results +** of the host lookup. Use PR_EnumerateAddrInfo to +** inspect the PRNetAddr values stored in this object. +** When no longer needed, this handle must be destroyed +** with a call to PR_FreeAddrInfo. If a lookup error +** occurs, then NULL will be returned. +***********************************************************************/ +typedef struct PRAddrInfo PRAddrInfo; + +NSPR_API(PRAddrInfo*) PR_GetAddrInfoByName( + const char *hostname, PRUint16 af, PRIntn flags); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_FreeAddrInfo() +** Destroy the PRAddrInfo handle allocated by PR_GetAddrInfoByName(). +** +** INPUTS: +** PRAddrInfo *addrInfo +** The handle resulting from a successful call to +** PR_GetAddrInfoByName(). +** RETURN: +** void +***********************************************************************/ +NSPR_API(void) PR_FreeAddrInfo(PRAddrInfo *addrInfo); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_EnumerateAddrInfo() +** A stateless enumerator over a PRAddrInfo handle acquired from +** PR_GetAddrInfoByName() to inspect the possible network addresses. +** +** INPUTS: +** void *enumPtr Index pointer of the enumeration. The enumeration +** starts and ends with a value of NULL. +** const PRAddrInfo *addrInfo +** The PRAddrInfo handle returned by a successful +** call to PR_GetAddrInfoByName(). +** PRUint16 port The port number to be assigned as part of the +** PRNetAddr. +** OUTPUTS: +** PRNetAddr *result A pointer to an address structure that will be +** filled in by the call to the enumeration if the +** result of the call is not NULL. +** RETURN: +** void* The value that should be used for the next call +** of the enumerator ('enumPtr'). The enumeration +** is ended if this value is NULL. +***********************************************************************/ +NSPR_API(void *) PR_EnumerateAddrInfo( + void *enumPtr, const PRAddrInfo *addrInfo, PRUint16 port, PRNetAddr *result); + +NSPR_API(PRStatus) PR_GetPrefLoopbackAddrInfo(PRNetAddr *result, + PRUint16 port); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_GetCanonNameFromAddrInfo() +** Extracts the canonical name of the hostname passed to +** PR_GetAddrInfoByName(). +** +** INPUTS: +** const PRAddrInfo *addrInfo +** The PRAddrInfo handle returned by a successful +** call to PR_GetAddrInfoByName(). +** RETURN: +** const char * A const pointer to the canonical hostname stored +** in the given PRAddrInfo handle. This pointer is +** invalidated once the PRAddrInfo handle is destroyed +** by a call to PR_FreeAddrInfo(). +***********************************************************************/ +NSPR_API(const char *) PR_GetCanonNameFromAddrInfo( + const PRAddrInfo *addrInfo); + +/*********************************************************************** +** FUNCTIONS: PR_ntohs, PR_ntohl, PR_ntohll, PR_htons, PR_htonl, PR_htonll +** +** DESCRIPTION: API entries for the common byte ordering routines. +** +** PR_ntohs 16 bit conversion from network to host +** PR_ntohl 32 bit conversion from network to host +** PR_ntohll 64 bit conversion from network to host +** PR_htons 16 bit conversion from host to network +** PR_htonl 32 bit conversion from host to network +** PR_ntonll 64 bit conversion from host to network +** +***********************************************************************/ +NSPR_API(PRUint16) PR_ntohs(PRUint16); +NSPR_API(PRUint32) PR_ntohl(PRUint32); +NSPR_API(PRUint64) PR_ntohll(PRUint64); +NSPR_API(PRUint16) PR_htons(PRUint16); +NSPR_API(PRUint32) PR_htonl(PRUint32); +NSPR_API(PRUint64) PR_htonll(PRUint64); + +PR_END_EXTERN_C + +#endif /* prnetdb_h___ */ diff --git a/nsprpub/pr/include/prolock.h b/nsprpub/pr/include/prolock.h new file mode 100644 index 0000000000..7b57b3c71e --- /dev/null +++ b/nsprpub/pr/include/prolock.h @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prolock_h___ +#define prolock_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** A locking mechanism, built on the existing PRLock definition, +** is provided that will permit applications to define a Lock +** Hierarchy (or Lock Ordering) schema. An application designed +** using the Ordered Lock functions will terminate with a +** diagnostic message when a lock inversion condition is +** detected. +** +** The lock ordering detection is compile-time enabled only. In +** optimized builds of NSPR, the Ordered Lock functions map +** directly to PRLock functions, providing no lock order +** detection. +** +** The Ordered Lock Facility is compiled in when DEBUG is defined at +** compile-time. Ordered Lock can be forced on in optimized builds by +** defining FORCE_NSPR_ORDERED_LOCK at compile-time. Both the +** application using Ordered Lock and NSPR must be compiled with the +** facility enabled to achieve the desired results. +** +** Application designers should use the macro interfaces to the Ordered +** Lock facility to ensure that it is compiled out in optimized builds. +** +** Application designers are responsible for defining their own +** lock hierarchy. +** +** Ordered Lock is thread-safe and SMP safe. +** +** See Also: prlock.h +** +** /lth. 10-Jun-1998. +** +*/ + +/* +** Opaque type for ordered lock. +** ... Don't even think of looking in here. +** +*/ + +#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS) +typedef void * PROrderedLock; +#else +/* +** Map PROrderedLock and methods onto PRLock when ordered locking +** is not compiled in. +** +*/ +#include "prlock.h" + +typedef PRLock PROrderedLock; +#endif + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_CreateOrderedLock() -- Create an Ordered Lock +** +** DESCRIPTION: PR_CreateOrderedLock() creates an ordered lock. +** +** INPUTS: +** order: user defined order of this lock. +** name: name of the lock. For debugging purposes. +** +** OUTPUTS: returned +** +** RETURNS: PR_OrderedLock pointer +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS) +#define PR_CREATE_ORDERED_LOCK(order,name)\ + PR_CreateOrderedLock((order),(name)) +#else +#define PR_CREATE_ORDERED_LOCK(order) PR_NewLock() +#endif + +NSPR_API(PROrderedLock *) +PR_CreateOrderedLock( + PRInt32 order, + const char *name +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_DestroyOrderedLock() -- Destroy an Ordered Lock +** +** DESCRIPTION: PR_DestroyOrderedLock() destroys the ordered lock +** referenced by lock. +** +** INPUTS: lock: pointer to a PROrderedLock +** +** OUTPUTS: the lock is destroyed +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS) +#define PR_DESTROY_ORDERED_LOCK(lock) PR_DestroyOrderedLock((lock)) +#else +#define PR_DESTROY_ORDERED_LOCK(lock) PR_DestroyLock((lock)) +#endif + +NSPR_API(void) +PR_DestroyOrderedLock( + PROrderedLock *lock +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_LockOrderedLock() -- Lock an ordered lock +** +** DESCRIPTION: PR_LockOrderedLock() locks the ordered lock +** referenced by lock. If the order of lock is less than or equal +** to the order of the highest lock held by the locking thread, +** the function asserts. +** +** INPUTS: lock: a pointer to a PROrderedLock +** +** OUTPUTS: The lock is held or the function asserts. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS) +#define PR_LOCK_ORDERED_LOCK(lock) PR_LockOrderedLock((lock)) +#else +#define PR_LOCK_ORDERED_LOCK(lock) PR_Lock((lock)) +#endif + +NSPR_API(void) +PR_LockOrderedLock( + PROrderedLock *lock +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_UnlockOrderedLock() -- unlock and Ordered Lock +** +** DESCRIPTION: PR_UnlockOrderedLock() unlocks the lock referenced +** by lock. +** +** INPUTS: lock: a pointer to a PROrderedLock +** +** OUTPUTS: the lock is unlocked +** +** RETURNS: +** PR_SUCCESS +** PR_FAILURE +** +** RESTRICTIONS: +** +*/ +#if defined(DEBUG) || defined(FORCE_NSPR_ORDERED_LOCKS) +#define PR_UNLOCK_ORDERED_LOCK(lock) PR_UnlockOrderedLock((lock)) +#else +#define PR_UNLOCK_ORDERED_LOCK(lock) PR_Unlock((lock)) +#endif + +NSPR_API(PRStatus) +PR_UnlockOrderedLock( + PROrderedLock *lock +); + +PR_END_EXTERN_C + +#endif /* prolock_h___ */ diff --git a/nsprpub/pr/include/prpdce.h b/nsprpub/pr/include/prpdce.h new file mode 100644 index 0000000000..bf9ea9e2b7 --- /dev/null +++ b/nsprpub/pr/include/prpdce.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: prpdce.h + * Description: This file is the API defined to allow for DCE (aka POSIX) + * thread emulation in an NSPR environment. It is not the + * intent that this be a fully supported API. + */ + +#if !defined(PRPDCE_H) +#define PRPDCE_H + +#include "prlock.h" +#include "prcvar.h" +#include "prtypes.h" +#include "prinrval.h" + +PR_BEGIN_EXTERN_C + +#define _PR_NAKED_CV_LOCK (PRLock*)0xdce1dce1 + +/* +** Test and acquire a lock. +** +** If the lock is acquired by the calling thread, the +** return value will be PR_SUCCESS. If the lock is +** already held, by another thread or this thread, the +** result will be PR_FAILURE. +*/ +NSPR_API(PRStatus) PRP_TryLock(PRLock *lock); + +/* +** Create a naked condition variable +** +** A "naked" condition variable is one that is not created bound +** to a lock. The CV created with this function is the only type +** that may be used in the subsequent "naked" condition variable +** operations (see PRP_NakedWait, PRP_NakedNotify, PRP_NakedBroadcast); +*/ +NSPR_API(PRCondVar*) PRP_NewNakedCondVar(void); + +/* +** Destroy a naked condition variable +** +** Destroy the condition variable created by PR_NewNakedCondVar. +*/ +NSPR_API(void) PRP_DestroyNakedCondVar(PRCondVar *cvar); + +/* +** Wait on a condition +** +** Wait on the condition variable 'cvar'. It is asserted that +** the lock protecting the condition 'lock' is held by the +** calling thread. If more time expires than that declared in +** 'timeout' the condition will be notified. Waits can be +** interrupted by another thread. +** +** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar. +*/ +NSPR_API(PRStatus) PRP_NakedWait( + PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout); + +/* +** Notify a thread waiting on a condition +** +** Notify the condition specified 'cvar'. +** +** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar. +*/ +NSPR_API(PRStatus) PRP_NakedNotify(PRCondVar *cvar); + +/* +** Notify all threads waiting on a condition +** +** Notify the condition specified 'cvar'. +** +** NB: The CV ('cvar') must be one created using PR_NewNakedCondVar. +*/ +NSPR_API(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar); + +PR_END_EXTERN_C + +#endif /* PRPDCE_H */ diff --git a/nsprpub/pr/include/prprf.h b/nsprpub/pr/include/prprf.h new file mode 100644 index 0000000000..759ef4053a --- /dev/null +++ b/nsprpub/pr/include/prprf.h @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prprf_h___ +#define prprf_h___ + +/* +** API for PR printf like routines. Supports the following formats +** %d - decimal +** %u - unsigned decimal +** %x - unsigned hex +** %X - unsigned uppercase hex +** %o - unsigned octal +** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above +** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above +** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above +** %s - string +** %c - character +** %p - pointer (deals with machine dependent pointer size) +** %f - float +** %g - float +*/ +#include "prtypes.h" +#include "prio.h" +#include <stdio.h> +#include <stdarg.h> + +PR_BEGIN_EXTERN_C + +/* +** sprintf into a fixed size buffer. Guarantees that a NUL is at the end +** of the buffer. Returns the length of the written output, NOT including +** the NUL, or (PRUint32)-1 if an error occurs. +*/ +NSPR_API(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...); + +/* +** sprintf into a PR_MALLOC'd buffer. Return a pointer to the malloc'd +** buffer on success, NULL on failure. Call "PR_smprintf_free" to release +** the memory returned. +*/ +NSPR_API(char*) PR_smprintf(const char *fmt, ...); + +/* +** Free the memory allocated, for the caller, by PR_smprintf +*/ +NSPR_API(void) PR_smprintf_free(char *mem); + +/* +** "append" sprintf into a PR_MALLOC'd buffer. "last" is the last value of +** the PR_MALLOC'd buffer. sprintf will append data to the end of last, +** growing it as necessary using realloc. If last is NULL, PR_sprintf_append +** will allocate the initial string. The return value is the new value of +** last for subsequent calls, or NULL if there is a malloc failure. +*/ +NSPR_API(char*) PR_sprintf_append(char *last, const char *fmt, ...); + +/* +** sprintf into a function. The function "f" is called with a string to +** place into the output. "arg" is an opaque pointer used by the stuff +** function to hold any state needed to do the storage of the output +** data. The return value is a count of the number of characters fed to +** the stuff function, or (PRUint32)-1 if an error occurs. +*/ +typedef PRIntn (*PRStuffFunc)(void *arg, const char *s, PRUint32 slen); + +NSPR_API(PRUint32) PR_sxprintf(PRStuffFunc f, void *arg, const char *fmt, ...); + +/* +** fprintf to a PRFileDesc +*/ +NSPR_API(PRUint32) PR_fprintf(struct PRFileDesc* fd, const char *fmt, ...); + +/* +** va_list forms of the above. +*/ +NSPR_API(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen, const char *fmt, va_list ap); +NSPR_API(char*) PR_vsmprintf(const char *fmt, va_list ap); +NSPR_API(char*) PR_vsprintf_append(char *last, const char *fmt, va_list ap); +NSPR_API(PRUint32) PR_vsxprintf(PRStuffFunc f, void *arg, const char *fmt, va_list ap); +NSPR_API(PRUint32) PR_vfprintf(struct PRFileDesc* fd, const char *fmt, va_list ap); + +/* +*************************************************************************** +** FUNCTION: PR_sscanf +** DESCRIPTION: +** PR_sscanf() scans the input character string, performs data +** conversions, and stores the converted values in the data objects +** pointed to by its arguments according to the format control +** string. +** +** PR_sscanf() behaves the same way as the sscanf() function in the +** Standard C Library (stdio.h), with the following exceptions: +** - PR_sscanf() handles the NSPR integer and floating point types, +** such as PRInt16, PRInt32, PRInt64, and PRFloat64, whereas +** sscanf() handles the standard C types like short, int, long, +** and double. +** - PR_sscanf() has no multibyte character support, while sscanf() +** does. +** INPUTS: +** const char *buf +** a character string holding the input to scan +** const char *fmt +** the format control string for the conversions +** ... +** variable number of arguments, each of them is a pointer to +** a data object in which the converted value will be stored +** OUTPUTS: none +** RETURNS: PRInt32 +** The number of values converted and stored. +** RESTRICTIONS: +** Multibyte characters in 'buf' or 'fmt' are not allowed. +*************************************************************************** +*/ + +NSPR_API(PRInt32) PR_sscanf(const char *buf, const char *fmt, ...); + +PR_END_EXTERN_C + +#endif /* prprf_h___ */ diff --git a/nsprpub/pr/include/prproces.h b/nsprpub/pr/include/prproces.h new file mode 100644 index 0000000000..e23504ac57 --- /dev/null +++ b/nsprpub/pr/include/prproces.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prproces_h___ +#define prproces_h___ + +#include "prtypes.h" +#include "prio.h" + +PR_BEGIN_EXTERN_C + +/************************************************************************/ +/*****************************PROCESS OPERATIONS*************************/ +/************************************************************************/ + +typedef struct PRProcess PRProcess; +typedef struct PRProcessAttr PRProcessAttr; + +NSPR_API(PRProcessAttr *) PR_NewProcessAttr(void); + +NSPR_API(void) PR_ResetProcessAttr(PRProcessAttr *attr); + +NSPR_API(void) PR_DestroyProcessAttr(PRProcessAttr *attr); + +NSPR_API(void) PR_ProcessAttrSetStdioRedirect( + PRProcessAttr *attr, + PRSpecialFD stdioFd, + PRFileDesc *redirectFd +); + +/* + * OBSOLETE -- use PR_ProcessAttrSetStdioRedirect instead. + */ +NSPR_API(void) PR_SetStdioRedirect( + PRProcessAttr *attr, + PRSpecialFD stdioFd, + PRFileDesc *redirectFd +); + +NSPR_API(PRStatus) PR_ProcessAttrSetCurrentDirectory( + PRProcessAttr *attr, + const char *dir +); + +NSPR_API(PRStatus) PR_ProcessAttrSetInheritableFD( + PRProcessAttr *attr, + PRFileDesc *fd, + const char *name +); + +/* +** Create a new process +** +** Create a new process executing the file specified as 'path' and with +** the supplied arguments and environment. +** +** This function may fail because of illegal access (permissions), +** invalid arguments or insufficient resources. +** +** A process may be created such that the creator can later synchronize its +** termination using PR_WaitProcess(). +*/ + +NSPR_API(PRProcess*) PR_CreateProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr); + +NSPR_API(PRStatus) PR_CreateProcessDetached( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr); + +NSPR_API(PRStatus) PR_DetachProcess(PRProcess *process); + +NSPR_API(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode); + +NSPR_API(PRStatus) PR_KillProcess(PRProcess *process); + +PR_END_EXTERN_C + +#endif /* prproces_h___ */ diff --git a/nsprpub/pr/include/prrng.h b/nsprpub/pr/include/prrng.h new file mode 100644 index 0000000000..db7dc68c66 --- /dev/null +++ b/nsprpub/pr/include/prrng.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* +** prrng.h -- NSPR Random Number Generator +** +** +** lth. 29-Oct-1999. +*/ + +#ifndef prrng_h___ +#define prrng_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** PR_GetRandomNoise() -- Get random noise from the host platform +** +** Description: +** PR_GetRandomNoise() provides, depending on platform, a random value. +** The length of the random value is dependent on platform and the +** platform's ability to provide a random value at that moment. +** +** The intent of PR_GetRandomNoise() is to provide a "seed" value for a +** another random number generator that may be suitable for +** cryptographic operations. This implies that the random value +** provided may not be, by itself, cryptographically secure. The value +** generated by PR_GetRandomNoise() is at best, extremely difficult to +** predict and is as non-deterministic as the underlying platfrom can +** provide. +** +** Inputs: +** buf -- pointer to a caller supplied buffer to contain the +** generated random number. buf must be at least as large as +** is specified in the 'size' argument. +** +** size -- the requested size of the generated random number +** +** Outputs: +** a random number provided in 'buf'. +** +** Returns: +** PRSize value equal to the size of the random number actually +** generated, or zero. The generated size may be less than the size +** requested. A return value of zero means that PR_GetRandomNoise() is +** not implemented on this platform, or there is no available noise +** available to be returned at the time of the call. +** +** Restrictions: +** Calls to PR_GetRandomNoise() may use a lot of CPU on some platforms. +** Some platforms may block for up to a few seconds while they +** accumulate some noise. Busy machines generate lots of noise, but +** care is advised when using PR_GetRandomNoise() frequently in your +** application. +** +** History: +** Parts of the model dependent implementation for PR_GetRandomNoise() +** were taken in whole or part from code previously in Netscape's NSS +** component. +** +*/ +NSPR_API(PRSize) PR_GetRandomNoise( + void *buf, + PRSize size +); + +PR_END_EXTERN_C + +#endif /* prrng_h___ */ +/* end prrng.h */ diff --git a/nsprpub/pr/include/prrwlock.h b/nsprpub/pr/include/prrwlock.h new file mode 100644 index 0000000000..ab47b53fb0 --- /dev/null +++ b/nsprpub/pr/include/prrwlock.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prrwlock.h +** Description: API to basic reader-writer lock functions of NSPR. +** +**/ + +#ifndef prrwlock_h___ +#define prrwlock_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* + * PRRWLock -- + * + * The reader writer lock, PRRWLock, is an opaque object to the clients + * of NSPR. All routines operate on a pointer to this opaque entity. + */ + + +typedef struct PRRWLock PRRWLock; + +#define PR_RWLOCK_RANK_NONE 0 + + +/*********************************************************************** +** FUNCTION: PR_NewRWLock +** DESCRIPTION: +** Returns a pointer to a newly created reader-writer lock object. +** INPUTS: Lock rank +** Lock name +** OUTPUTS: void +** RETURN: PRRWLock* +** If the lock cannot be created because of resource constraints, NULL +** is returned. +** +***********************************************************************/ +NSPR_API(PRRWLock*) PR_NewRWLock(PRUint32 lock_rank, const char *lock_name); + +/*********************************************************************** +** FUNCTION: PR_DestroyRWLock +** DESCRIPTION: +** Destroys a given RW lock object. +** INPUTS: PRRWLock *lock - Lock to be freed. +** OUTPUTS: void +** RETURN: None +***********************************************************************/ +NSPR_API(void) PR_DestroyRWLock(PRRWLock *lock); + +/*********************************************************************** +** FUNCTION: PR_RWLock_Rlock +** DESCRIPTION: +** Apply a read lock (non-exclusive) on a RWLock +** INPUTS: PRRWLock *lock - Lock to be read-locked. +** OUTPUTS: void +** RETURN: None +***********************************************************************/ +NSPR_API(void) PR_RWLock_Rlock(PRRWLock *lock); + +/*********************************************************************** +** FUNCTION: PR_RWLock_Wlock +** DESCRIPTION: +** Apply a write lock (exclusive) on a RWLock +** INPUTS: PRRWLock *lock - Lock to write-locked. +** OUTPUTS: void +** RETURN: None +***********************************************************************/ +NSPR_API(void) PR_RWLock_Wlock(PRRWLock *lock); + +/*********************************************************************** +** FUNCTION: PR_RWLock_Unlock +** DESCRIPTION: +** Release a RW lock. Unlocking an unlocked lock has undefined results. +** INPUTS: PRRWLock *lock - Lock to unlocked. +** OUTPUTS: void +** RETURN: void +***********************************************************************/ +NSPR_API(void) PR_RWLock_Unlock(PRRWLock *lock); + +PR_END_EXTERN_C + +#endif /* prrwlock_h___ */ diff --git a/nsprpub/pr/include/prshm.h b/nsprpub/pr/include/prshm.h new file mode 100644 index 0000000000..3588aed4e6 --- /dev/null +++ b/nsprpub/pr/include/prshm.h @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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" +#include "prio.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(); +** +*/ +NSPR_API( 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(); +** +** +*/ +NSPR_API( 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 +** +*/ +NSPR_API( 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 +** +*/ +NSPR_API( 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 +** +*/ +NSPR_API( PRStatus ) +PR_DeleteSharedMemory( + const char *name +); + +PR_END_EXTERN_C + +#endif /* prshm_h___ */ diff --git a/nsprpub/pr/include/prshma.h b/nsprpub/pr/include/prshma.h new file mode 100644 index 0000000000..2a1990ad6b --- /dev/null +++ b/nsprpub/pr/include/prshma.h @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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. +** +*/ +NSPR_API( 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 +** +*/ +NSPR_API(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. +** +*/ +NSPR_API( 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 +** +*/ +NSPR_API( 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. +** +*/ +NSPR_API( PRFileMap * ) +PR_ImportFileMapFromString( + const char *fmstring +); + +PR_END_EXTERN_C +#endif /* prshma_h___ */ diff --git a/nsprpub/pr/include/prsystem.h b/nsprpub/pr/include/prsystem.h new file mode 100644 index 0000000000..577e60e061 --- /dev/null +++ b/nsprpub/pr/include/prsystem.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prsystem_h___ +#define prsystem_h___ + +/* +** API to NSPR functions returning system info. +*/ +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** Get the host' directory separator. +** Pathnames are then assumed to be of the form: +** [<sep><root_component><sep>]*(<component><sep>)<leaf_name> +*/ + +NSPR_API(char) PR_GetDirectorySeparator(void); + +/* +** OBSOLETE -- the function name is misspelled. +** Use PR_GetDirectorySeparator instead. +*/ + +NSPR_API(char) PR_GetDirectorySepartor(void); + +/* +** Get the host' path separator. +** Paths are assumed to be of the form: +** <directory>[<sep><directory>]* +*/ + +NSPR_API(char) PR_GetPathSeparator(void); + +/* Types of information available via PR_GetSystemInfo(...) */ +typedef enum { + PR_SI_HOSTNAME, /* the hostname with the domain name (if any) + * removed */ + PR_SI_SYSNAME, + PR_SI_RELEASE, + PR_SI_ARCHITECTURE, + PR_SI_HOSTNAME_UNTRUNCATED, /* the hostname exactly as configured + * on the system */ + PR_SI_RELEASE_BUILD +} PRSysInfo; + + +/* +** If successful returns a null termintated string in 'buf' for +** the information indicated in 'cmd'. If unseccussful the reason for +** the failure can be retrieved from PR_GetError(). +** +** The buffer is allocated by the caller and should be at least +** SYS_INFO_BUFFER_LENGTH bytes in length. +*/ + +#define SYS_INFO_BUFFER_LENGTH 256 + +NSPR_API(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen); + +/* +** Return the number of bytes in a page +*/ +NSPR_API(PRInt32) PR_GetPageSize(void); + +/* +** Return log2 of the size of a page +*/ +NSPR_API(PRInt32) PR_GetPageShift(void); + +/* +** PR_GetNumberOfProcessors() -- returns the number of CPUs +** +** Description: +** PR_GetNumberOfProcessors() extracts the number of processors +** (CPUs available in an SMP system) and returns the number. +** +** Parameters: +** none +** +** Returns: +** The number of available processors or -1 on error +** +*/ +NSPR_API(PRInt32) PR_GetNumberOfProcessors( void ); + +/* +** PR_GetPhysicalMemorySize() -- returns the amount of system RAM +** +** Description: +** PR_GetPhysicalMemorySize() determines the amount of physical RAM +** in the system and returns the size in bytes. +** +** Parameters: +** none +** +** Returns: +** The amount of system RAM, or 0 on failure. +** +*/ +NSPR_API(PRUint64) PR_GetPhysicalMemorySize(void); + +PR_END_EXTERN_C + +#endif /* prsystem_h___ */ diff --git a/nsprpub/pr/include/prthread.h b/nsprpub/pr/include/prthread.h new file mode 100644 index 0000000000..cacd3891bf --- /dev/null +++ b/nsprpub/pr/include/prthread.h @@ -0,0 +1,272 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prthread_h___ +#define prthread_h___ + +/* +** API for NSPR threads. On some architectures (Mac OS Classic +** notably) pre-emptibility is not guaranteed. Hard priority scheduling +** is not guaranteed, so programming using priority based synchronization +** is a no-no. +** +** NSPR threads are scheduled based loosely on their client set priority. +** In general, a thread of a higher priority has a statistically better +** chance of running relative to threads of lower priority. However, +** NSPR uses multiple strategies to provide execution vehicles for thread +** abstraction of various host platforms. As it turns out, there is little +** NSPR can do to affect the scheduling attributes of "GLOBAL" threads. +** However, a semblance of GLOBAL threads is used to implement "LOCAL" +** threads. An arbitrary number of such LOCAL threads can be assigned to +** a single GLOBAL thread. +** +** For scheduling, NSPR will attempt to run the highest priority LOCAL +** thread associated with a given GLOBAL thread. It is further assumed +** that the host OS will apply some form of "fair" scheduling on the +** GLOBAL threads. +** +** Threads have a "system flag" which when set indicates the thread +** doesn't count for determining when the process should exit (the +** process exits when the last user thread exits). +** +** Threads also have a "scope flag" which controls whether the threads +** are scheduled in the local scope or scheduled by the OS globally. This +** indicates whether a thread is permanently bound to a native OS thread. +** An unbound thread competes for scheduling resources in the same process. +** +** Another flag is "state flag" which control whether the thread is joinable. +** It allows other threads to wait for the created thread to reach completion. +** +** Threads can have "per-thread-data" attached to them. Each thread has a +** per-thread error number and error string which are updated when NSPR +** operations fail. +*/ +#include "prtypes.h" +#include "prinrval.h" + +PR_BEGIN_EXTERN_C + +typedef struct PRThread PRThread; +typedef struct PRThreadStack PRThreadStack; + +typedef enum PRThreadType { + PR_USER_THREAD, + PR_SYSTEM_THREAD +} PRThreadType; + +typedef enum PRThreadScope { + PR_LOCAL_THREAD, + PR_GLOBAL_THREAD, + PR_GLOBAL_BOUND_THREAD +} PRThreadScope; + +typedef enum PRThreadState { + PR_JOINABLE_THREAD, + PR_UNJOINABLE_THREAD +} PRThreadState; + +typedef enum PRThreadPriority +{ + PR_PRIORITY_FIRST = 0, /* just a placeholder */ + PR_PRIORITY_LOW = 0, /* the lowest possible priority */ + PR_PRIORITY_NORMAL = 1, /* most common expected priority */ + PR_PRIORITY_HIGH = 2, /* slightly more aggressive scheduling */ + PR_PRIORITY_URGENT = 3, /* it does little good to have more than one */ + PR_PRIORITY_LAST = 3 /* this is just a placeholder */ +} PRThreadPriority; + +/* +** Create a new thread: +** "type" is the type of thread to create +** "start(arg)" will be invoked as the threads "main" +** "priority" will be created thread's priority +** "scope" will specify whether the thread is local or global +** "state" will specify whether the thread is joinable or not +** "stackSize" the size of the stack, in bytes. The value can be zero +** and then a machine specific stack size will be chosen. +** +** This can return NULL if some kind of error occurs, such as if memory is +** tight. +** +** If you want the thread to start up waiting for the creator to do +** something, enter a lock before creating the thread and then have the +** threads start routine enter and exit the same lock. When you are ready +** for the thread to run, exit the lock. +** +** If you want to detect the completion of the created thread, the thread +** should be created joinable. Then, use PR_JoinThread to synchrnoize the +** termination of another thread. +** +** When the start function returns the thread exits. If it is the last +** PR_USER_THREAD to exit then the process exits. +*/ +NSPR_API(PRThread*) PR_CreateThread(PRThreadType type, + void (PR_CALLBACK *start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize); + +/* +** Wait for thread termination: +** "thread" is the target thread +** +** This can return PR_FAILURE if no joinable thread could be found +** corresponding to the specified target thread. +** +** The calling thread is blocked until the target thread completes. +** Several threads cannot wait for the same thread to complete; one thread +** will operate successfully and others will terminate with an error PR_FAILURE. +** The calling thread will not be blocked if the target thread has already +** terminated. +*/ +NSPR_API(PRStatus) PR_JoinThread(PRThread *thread); + +/* +** Return the current thread object for the currently running code. +** Never returns NULL. +*/ +NSPR_API(PRThread*) PR_GetCurrentThread(void); +#ifndef NO_NSPR_10_SUPPORT +#define PR_CurrentThread() PR_GetCurrentThread() /* for nspr1.0 compat. */ +#endif /* NO_NSPR_10_SUPPORT */ + +/* +** Get the priority of "thread". +*/ +NSPR_API(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread); + +/* +** Change the priority of the "thread" to "priority". +** +** PR_SetThreadPriority works in a best-effort manner. On some platforms a +** special privilege, such as root access, is required to change thread +** priorities, especially to raise thread priorities. If the caller doesn't +** have enough privileges to change thread priorites, the function has no +** effect except causing a future PR_GetThreadPriority call to return +** |priority|. +*/ +NSPR_API(void) PR_SetThreadPriority(PRThread *thread, PRThreadPriority priority); + +/* +** Set the name of the current thread, which will be visible in a debugger +** and accessible via a call to PR_GetThreadName(). +*/ +NSPR_API(PRStatus) PR_SetCurrentThreadName(const char *name); + +/* +** Return the name of "thread", if set. Otherwise return NULL. +*/ +NSPR_API(const char *) PR_GetThreadName(const PRThread *thread); + +/* +** This routine returns a new index for per-thread-private data table. +** The index is visible to all threads within a process. This index can +** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines +** to save and retrieve data associated with the index for a thread. +** +** Each index is associationed with a destructor function ('dtor'). The function +** may be specified as NULL when the index is created. If it is not NULL, the +** function will be called when: +** - the thread exits and the private data for the associated index +** is not NULL, +** - new thread private data is set and the current private data is +** not NULL. +** +** The index independently maintains specific values for each binding thread. +** A thread can only get access to its own thread-specific-data. +** +** Upon a new index return the value associated with the index for all threads +** is NULL, and upon thread creation the value associated with all indices for +** that thread is NULL. +** +** Returns PR_FAILURE if the total number of indices will exceed the maximun +** allowed. +*/ +typedef void (PR_CALLBACK *PRThreadPrivateDTOR)(void *priv); + +NSPR_API(PRStatus) PR_NewThreadPrivateIndex( + PRUintn *newIndex, PRThreadPrivateDTOR destructor); + +/* +** Define some per-thread-private data. +** "tpdIndex" is an index into the per-thread private data table +** "priv" is the per-thread-private data +** +** If the per-thread private data table has a previously registered +** destructor function and a non-NULL per-thread-private data value, +** the destructor function is invoked. +** +** This can return PR_FAILURE if the index is invalid. +*/ +NSPR_API(PRStatus) PR_SetThreadPrivate(PRUintn tpdIndex, void *priv); + +/* +** Recover the per-thread-private data for the current thread. "tpdIndex" is +** the index into the per-thread private data table. +** +** The returned value may be NULL which is indistinguishable from an error +** condition. +** +** A thread can only get access to its own thread-specific-data. +*/ +NSPR_API(void*) PR_GetThreadPrivate(PRUintn tpdIndex); + +/* +** This routine sets the interrupt request for a target thread. The interrupt +** request remains in the thread's state until it is delivered exactly once +** or explicitly canceled. +** +** A thread that has been interrupted will fail all NSPR blocking operations +** that return a PRStatus (I/O, waiting on a condition, etc). +** +** PR_Interrupt may itself fail if the target thread is invalid. +*/ +NSPR_API(PRStatus) PR_Interrupt(PRThread *thread); + +/* +** Clear the interrupt request for the calling thread. If no such request +** is pending, this operation is a noop. +*/ +NSPR_API(void) PR_ClearInterrupt(void); + +/* +** Block the interrupt for the calling thread. +*/ +NSPR_API(void) PR_BlockInterrupt(void); + +/* +** Unblock the interrupt for the calling thread. +*/ +NSPR_API(void) PR_UnblockInterrupt(void); + +/* +** Make the current thread sleep until "ticks" time amount of time +** has expired. If "ticks" is PR_INTERVAL_NO_WAIT then the call is +** equivalent to calling PR_Yield. Calling PR_Sleep with an argument +** equivalent to PR_INTERVAL_NO_TIMEOUT is an error and will result +** in a PR_FAILURE error return. +*/ +NSPR_API(PRStatus) PR_Sleep(PRIntervalTime ticks); + +/* +** Get the scoping of this thread. +*/ +NSPR_API(PRThreadScope) PR_GetThreadScope(const PRThread *thread); + +/* +** Get the type of this thread. +*/ +NSPR_API(PRThreadType) PR_GetThreadType(const PRThread *thread); + +/* +** Get the join state of this thread. +*/ +NSPR_API(PRThreadState) PR_GetThreadState(const PRThread *thread); + +PR_END_EXTERN_C + +#endif /* prthread_h___ */ diff --git a/nsprpub/pr/include/prtime.h b/nsprpub/pr/include/prtime.h new file mode 100644 index 0000000000..917a16159d --- /dev/null +++ b/nsprpub/pr/include/prtime.h @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + *---------------------------------------------------------------------- + * + * prtime.h -- + * + * NSPR date and time functions + * + *----------------------------------------------------------------------- + */ + +#ifndef prtime_h___ +#define prtime_h___ + +#include "prlong.h" + +PR_BEGIN_EXTERN_C + +/**********************************************************************/ +/************************* TYPES AND CONSTANTS ************************/ +/**********************************************************************/ + +#define PR_MSEC_PER_SEC 1000L +#define PR_USEC_PER_SEC 1000000L +#define PR_NSEC_PER_SEC 1000000000L +#define PR_USEC_PER_MSEC 1000L +#define PR_NSEC_PER_MSEC 1000000L + +/* + * PRTime -- + * + * NSPR represents basic time as 64-bit signed integers relative + * to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT). + * (GMT is also known as Coordinated Universal Time, UTC.) + * The units of time are in microseconds. Negative times are allowed + * to represent times prior to the January 1970 epoch. Such values are + * intended to be exported to other systems or converted to human + * readable form. + * + * Notes on porting: PRTime corresponds to time_t in ANSI C. NSPR 1.0 + * simply uses PRInt64. + */ + +typedef PRInt64 PRTime; + +/* + * Time zone and daylight saving time corrections applied to GMT to + * obtain the local time of some geographic location + */ + +typedef struct PRTimeParameters { + PRInt32 tp_gmt_offset; /* the offset from GMT in seconds */ + PRInt32 tp_dst_offset; /* contribution of DST in seconds */ +} PRTimeParameters; + +/* + * PRExplodedTime -- + * + * Time broken down into human-readable components such as year, month, + * day, hour, minute, second, and microsecond. Time zone and daylight + * saving time corrections may be applied. If they are applied, the + * offsets from the GMT must be saved in the 'tm_params' field so that + * all the information is available to reconstruct GMT. + * + * Notes on porting: PRExplodedTime corrresponds to struct tm in + * ANSI C, with the following differences: + * - an additional field tm_usec; + * - replacing tm_isdst by tm_params; + * - the month field is spelled tm_month, not tm_mon; + * - we use absolute year, AD, not the year since 1900. + * The corresponding type in NSPR 1.0 is called PRTime. Below is + * a table of date/time type correspondence in the three APIs: + * API time since epoch time in components + * ANSI C time_t struct tm + * NSPR 1.0 PRInt64 PRTime + * NSPR 2.0 PRTime PRExplodedTime + */ + +typedef struct PRExplodedTime { + PRInt32 tm_usec; /* microseconds past tm_sec (0-99999) */ + PRInt32 tm_sec; /* seconds past tm_min (0-61, accomodating + up to two leap seconds) */ + PRInt32 tm_min; /* minutes past tm_hour (0-59) */ + PRInt32 tm_hour; /* hours past tm_day (0-23) */ + PRInt32 tm_mday; /* days past tm_mon (1-31, note that it + starts from 1) */ + PRInt32 tm_month; /* months past tm_year (0-11, Jan = 0) */ + PRInt16 tm_year; /* absolute year, AD (note that we do not + count from 1900) */ + + PRInt8 tm_wday; /* calculated day of the week + (0-6, Sun = 0) */ + PRInt16 tm_yday; /* calculated day of the year + (0-365, Jan 1 = 0) */ + + PRTimeParameters tm_params; /* time parameters used by conversion */ +} PRExplodedTime; + +/* + * PRTimeParamFn -- + * + * A function of PRTimeParamFn type returns the time zone and + * daylight saving time corrections for some geographic location, + * given the current time in GMT. The input argument gmt should + * point to a PRExplodedTime that is in GMT, i.e., whose + * tm_params contains all 0's. + * + * For any time zone other than GMT, the computation is intended to + * consist of two steps: + * - Figure out the time zone correction, tp_gmt_offset. This number + * usually depends on the geographic location only. But it may + * also depend on the current time. For example, all of China + * is one time zone right now. But this situation may change + * in the future. + * - Figure out the daylight saving time correction, tp_dst_offset. + * This number depends on both the geographic location and the + * current time. Most of the DST rules are expressed in local + * current time. If so, one should apply the time zone correction + * to GMT before applying the DST rules. + */ + +typedef PRTimeParameters (PR_CALLBACK *PRTimeParamFn)(const PRExplodedTime *gmt); + +/**********************************************************************/ +/****************************** FUNCTIONS *****************************/ +/**********************************************************************/ + +/* + * The PR_Now routine returns the current time relative to the + * epoch, midnight, January 1, 1970 UTC. The units of the returned + * value are microseconds since the epoch. + * + * The values returned are not guaranteed to advance in a linear fashion + * due to the application of time correction protocols which synchronize + * computer clocks to some external time source. Consequently it should + * not be depended on for interval timing. + * + * The implementation is machine dependent. + * Cf. time_t time(time_t *tp) in ANSI C. + */ +NSPR_API(PRTime) +PR_Now(void); + +/* + * Expand time binding it to time parameters provided by PRTimeParamFn. + * The calculation is envisoned to proceed in the following steps: + * - From given PRTime, calculate PRExplodedTime in GMT + * - Apply the given PRTimeParamFn to the GMT that we just calculated + * to obtain PRTimeParameters. + * - Add the PRTimeParameters offsets to GMT to get the local time + * as PRExplodedTime. + */ + +NSPR_API(void) PR_ExplodeTime( + PRTime usecs, PRTimeParamFn params, PRExplodedTime *exploded); + +/* Reverse operation of PR_ExplodeTime */ +NSPR_API(PRTime) +PR_ImplodeTime(const PRExplodedTime *exploded); + +/* + * Adjust exploded time to normalize field overflows after manipulation. + * Note that the following fields of PRExplodedTime should not be + * manipulated: + * - tm_month and tm_year: because the number of days in a month and + * number of days in a year are not constant, it is ambiguous to + * manipulate the month and year fields, although one may be tempted + * to. For example, what does "a month from January 31st" mean? + * - tm_wday and tm_yday: these fields are calculated by NSPR. Users + * should treat them as "read-only". + */ + +NSPR_API(void) PR_NormalizeTime( + PRExplodedTime *exploded, PRTimeParamFn params); + +/**********************************************************************/ +/*********************** TIME PARAMETER FUNCTIONS *********************/ +/**********************************************************************/ + +/* Time parameters that suit current host machine */ +NSPR_API(PRTimeParameters) PR_LocalTimeParameters(const PRExplodedTime *gmt); + +/* Time parameters that represent Greenwich Mean Time */ +NSPR_API(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt); + +/* + * Time parameters that represent the US Pacific Time Zone, with the + * current daylight saving time rules (for testing only) + */ +NSPR_API(PRTimeParameters) PR_USPacificTimeParameters(const PRExplodedTime *gmt); + +/* + * This parses a time/date string into a PRExplodedTime + * struct. It populates all fields but it can't split + * the offset from UTC into tp_gmt_offset and tp_dst_offset in + * most cases (exceptions: PST/PDT, MST/MDT, CST/CDT, EST/EDT, GMT/BST). + * In those cases tp_gmt_offset will be the sum of these two and + * tp_dst_offset will be 0. + * It returns PR_SUCCESS on success, and PR_FAILURE + * if the time/date string can't be parsed. + * + * Many formats are handled, including: + * + * 14 Apr 89 03:20:12 + * 14 Apr 89 03:20 GMT + * Fri, 17 Mar 89 4:01:33 + * Fri, 17 Mar 89 4:01 GMT + * Mon Jan 16 16:12 PDT 1989 + * Mon Jan 16 16:12 +0130 1989 + * 6 May 1992 16:41-JST (Wednesday) + * 22-AUG-1993 10:59:12.82 + * 22-AUG-1993 10:59pm + * 22-AUG-1993 12:59am + * 22-AUG-1993 12:59 PM + * Friday, August 04, 1995 3:54 PM + * 06/21/95 04:24:34 PM + * 20/06/95 21:07 + * 95-06-08 19:32:48 EDT + * + * If the input string doesn't contain a description of the timezone, + * we consult the `default_to_gmt' to decide whether the string should + * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE). + * The correct value for this argument depends on what standard specified + * the time string which you are parsing. + */ + +NSPR_API(PRStatus) PR_ParseTimeStringToExplodedTime ( + const char *string, + PRBool default_to_gmt, + PRExplodedTime *result); + +/* + * This uses PR_ParseTimeStringToExplodedTime to parse + * a time/date string and PR_ImplodeTime to transform it into + * a PRTime (microseconds after "1-Jan-1970 00:00:00 GMT"). + * It returns PR_SUCCESS on success, and PR_FAILURE + * if the time/date string can't be parsed. + */ + +NSPR_API(PRStatus) PR_ParseTimeString ( + const char *string, + PRBool default_to_gmt, + PRTime *result); + +/* Format a time value into a buffer. Same semantics as strftime() */ +NSPR_API(PRUint32) PR_FormatTime(char *buf, int buflen, const char *fmt, + const PRExplodedTime *time); + +/* Format a time value into a buffer. Time is always in US English format, + * regardless of locale setting. + */ +NSPR_API(PRUint32) +PR_FormatTimeUSEnglish(char *buf, PRUint32 bufSize, + const char *format, const PRExplodedTime *time); + +PR_END_EXTERN_C + +#endif /* prtime_h___ */ diff --git a/nsprpub/pr/include/prtpool.h b/nsprpub/pr/include/prtpool.h new file mode 100644 index 0000000000..b4d18d4152 --- /dev/null +++ b/nsprpub/pr/include/prtpool.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prtpool_h___ +#define prtpool_h___ + +#include "prtypes.h" +#include "prthread.h" +#include "prio.h" +#include "prerror.h" + +/* + * NOTE: + * THIS API IS A PRELIMINARY VERSION IN NSPR 4.0 AND IS SUBJECT TO + * CHANGE + */ + +PR_BEGIN_EXTERN_C + +typedef struct PRJobIoDesc { + PRFileDesc *socket; + PRErrorCode error; + PRIntervalTime timeout; +} PRJobIoDesc; + +typedef struct PRThreadPool PRThreadPool; +typedef struct PRJob PRJob; +typedef void (PR_CALLBACK *PRJobFn) (void *arg); + +/* Create thread pool */ +NSPR_API(PRThreadPool *) +PR_CreateThreadPool(PRInt32 initial_threads, PRInt32 max_threads, + PRUint32 stacksize); + +/* queue a job */ +NSPR_API(PRJob *) +PR_QueueJob(PRThreadPool *tpool, PRJobFn fn, void *arg, PRBool joinable); + +/* queue a job, when a socket is readable */ +NSPR_API(PRJob *) +PR_QueueJob_Read(PRThreadPool *tpool, PRJobIoDesc *iod, + PRJobFn fn, void * arg, PRBool joinable); + +/* queue a job, when a socket is writeable */ +NSPR_API(PRJob *) +PR_QueueJob_Write(PRThreadPool *tpool, PRJobIoDesc *iod, + PRJobFn fn, void * arg, PRBool joinable); + +/* queue a job, when a socket has a pending connection */ +NSPR_API(PRJob *) +PR_QueueJob_Accept(PRThreadPool *tpool, PRJobIoDesc *iod, + PRJobFn fn, void * arg, PRBool joinable); + +/* queue a job, when the socket connection to addr succeeds or fails */ +NSPR_API(PRJob *) +PR_QueueJob_Connect(PRThreadPool *tpool, PRJobIoDesc *iod, + const PRNetAddr *addr, PRJobFn fn, void * arg, PRBool joinable); + +/* queue a job, when a timer exipres */ +NSPR_API(PRJob *) +PR_QueueJob_Timer(PRThreadPool *tpool, PRIntervalTime timeout, + PRJobFn fn, void * arg, PRBool joinable); +/* cancel a job */ +NSPR_API(PRStatus) +PR_CancelJob(PRJob *job); + +/* join a job */ +NSPR_API(PRStatus) +PR_JoinJob(PRJob *job); + +/* shutdown pool */ +NSPR_API(PRStatus) +PR_ShutdownThreadPool(PRThreadPool *tpool); + +/* join pool, wait for exit of all threads */ +NSPR_API(PRStatus) +PR_JoinThreadPool(PRThreadPool *tpool); + +PR_END_EXTERN_C + +#endif /* prtpool_h___ */ diff --git a/nsprpub/pr/include/prtrace.h b/nsprpub/pr/include/prtrace.h new file mode 100644 index 0000000000..49bd3edb74 --- /dev/null +++ b/nsprpub/pr/include/prtrace.h @@ -0,0 +1,646 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prtrace_h___ +#define prtrace_h___ +/* +** prtrace.h -- NSPR's Trace Facility. +** +** The Trace Facility provides a means to trace application +** program events within a process. When implementing an +** application program an engineer may insert a "Trace" function +** call, passing arguments to be traced. The "Trace" function +** combines the user trace data with identifying data and +** writes this data in time ordered sequence into a circular +** in-memory buffer; when the buffer fills, it wraps. +** +** Functions are provided to set and/or re-configure the size of +** the trace buffer, control what events are recorded in the +** buffer, enable and disable tracing based on specific user +** supplied data and other control functions. Methods are provided +** to record the trace entries in the in-memory trace buffer to +** a file. +** +** Tracing may cause a performance degredation to the application +** depending on the number and placement of calls to the tracing +** facility. When tracing is compiled in and all tracing is +** disabled via the runtime controls, the overhead should be +** minimal. ... Famous last words, eh? +** +** When DEBUG is defined at compile time, the Trace Facility is +** compiled as part of NSPR and any application using NSPR's +** header files will have tracing compiled in. When DEBUG is not +** defined, the Trace Facility is not compiled into NSPR nor +** exported in its header files. If the Trace Facility is +** desired in a non-debug build, then FORCE_NSPR_TRACE may be +** defined at compile time for both the optimized build of NSPR +** and the application. NSPR and any application using NSPR's +** Trace Facility must be compiled with the same level of trace +** conditioning or unresolved references may be realized at link +** time. +** +** For any of the Trace Facility methods that requires a trace +** handle as an input argument, the caller must ensure that the +** trace handle argument is valid. An invalid trace handle +** argument may cause unpredictable results. +** +** Trace Facility methods are thread-safe and SMP safe. +** +** Users of the Trace Facility should use the defined macros to +** invoke trace methods, not the function calls directly. e.g. +** PR_TRACE( h1,0,1,2, ...); not PR_Trace(h1,0,1,2, ...); +** +** Application designers should be aware of the effects of +** debug and optimized build differences when using result of the +** Trace Facility macros in expressions. +** +** See Also: prcountr.h +** +** /lth. 08-Jun-1998. +*/ + +#include "prtypes.h" +#include "prthread.h" +#include "prtime.h" + +PR_BEGIN_EXTERN_C + +/* +** Opaque type for the trace handle +** ... Don't even think about looking in here. +** +*/ +typedef void * PRTraceHandle; + +/* +** PRTraceEntry -- A trace entry in the in-memory trace buffer +** looks like this. +** +*/ +typedef struct PRTraceEntry +{ + PRThread *thread; /* The thread creating the trace entry */ + PRTraceHandle handle; /* PRTraceHandle creating the trace entry */ + PRTime time; /* Value of PR_Now() at time of trace entry */ + PRUint32 userData[8]; /* user supplied trace data */ +} PRTraceEntry; + +/* +** PRTraceOption -- command operands to +** PR_[Set|Get]TraceOption(). See descriptive meanings there. +** +*/ +typedef enum PRTraceOption +{ + PRTraceBufSize, + PRTraceEnable, + PRTraceDisable, + PRTraceSuspend, + PRTraceResume, + PRTraceSuspendRecording, + PRTraceResumeRecording, + PRTraceLockHandles, + PRTraceUnLockHandles, + PRTraceStopRecording +} PRTraceOption; + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_DEFINE_TRACE() -- Define a PRTraceHandle +** +** DESCRIPTION: PR_DEFINE_TRACE() is used to define a trace +** handle. +** +*/ +#define PR_DEFINE_TRACE(name) PRTraceHandle name + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_INIT_TRACE_HANDLE() -- Set the value of a PRTraceHandle +** +** DESCRIPTION: +** PR_INIT_TRACE_HANDLE() sets the value of a PRTraceHandle +** to value. e.g. PR_INIT_TRACE_HANDLE( myHandle, NULL ); +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_INIT_TRACE_HANDLE(handle,value)\ + (handle) = (PRCounterHandle)(value) +#else +#define PR_INIT_TRACE_HANDLE(handle,value) +#endif + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_CreateTrace() -- Create a trace handle +** +** DESCRIPTION: +** PR_CreateTrace() creates a new trace handle. Tracing is +** enabled for this handle when it is created. The trace handle +** is intended for use in other Trace Facility calls. +** +** PR_CreateTrace() registers the QName, RName and description +** data so that this data can be retrieved later. +** +** INPUTS: +** qName: pointer to string. QName for this trace handle. +** +** rName: pointer to string. RName for this trace handle. +** +** description: pointer to string. Descriptive data about this +** trace handle. +** +** OUTPUTS: +** Creates the trace handle. +** Registers the QName and RName with the trace facility. +** +** RETURNS: +** PRTraceHandle +** +** RESTRICTIONS: +** qName is limited to 31 characters. +** rName is limited to 31 characters. +** description is limited to 255 characters. +** +*/ +#define PRTRACE_NAME_MAX 31 +#define PRTRACE_DESC_MAX 255 + +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_CREATE_TRACE(handle,qName,rName,description)\ + (handle) = PR_CreateTrace((qName),(rName),(description)) +#else +#define PR_CREATE_TRACE(handle,qName,rName,description) +#endif + +NSPR_API(PRTraceHandle) +PR_CreateTrace( + const char *qName, /* QName for this trace handle */ + const char *rName, /* RName for this trace handle */ + const char *description /* description for this trace handle */ +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_DestroyTrace() -- Destroy a trace handle +** +** DESCRIPTION: +** PR_DestroyTrace() removes the referenced trace handle and +** associated QName, RName and description data from the Trace +** Facility. +** +** INPUTS: handle. A PRTraceHandle +** +** OUTPUTS: +** The trace handle is unregistered. +** The QName, RName and description are removed. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_DESTROY_TRACE(handle)\ + PR_DestroyTrace((handle)) +#else +#define PR_DESTROY_TRACE(handle) +#endif + +NSPR_API(void) +PR_DestroyTrace( + PRTraceHandle handle /* Handle to be destroyed */ +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_Trace() -- Make a trace entry in the in-memory trace +** +** DESCRIPTION: +** PR_Trace() makes an entry in the in-memory trace buffer for +** the referenced trace handle. The next logically available +** PRTraceEntry is used; when the next trace entry would overflow +** the trace table, the table wraps. +** +** PR_Trace() for a specific trace handle may be disabled by +** calling PR_SetTraceOption() specifying PRTraceDisable for the +** trace handle to be disabled. +** +** INPUTS: +** handle: PRTraceHandle. The trace handle for this trace. +** +** userData[0..7]: unsigned 32bit integers. user supplied data +** that is copied into the PRTraceEntry +** +** OUTPUTS: +** A PRTraceEntry is (conditionally) formatted in the in-memory +** trace buffer. +** +** RETURNS: void. +** +** RESTRICTIONS: +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_TRACE(handle,ud0,ud1,ud2,ud3,ud4,ud5,ud6,ud7)\ + PR_Trace((handle),(ud0),(ud1),(ud2),(ud3),(ud4),(ud5),(ud6),(ud7)) +#else +#define PR_TRACE(handle,ud0,ud1,ud2,ud3,ud4,ud5,ud6,ud7) +#endif + +NSPR_API(void) +PR_Trace( + PRTraceHandle handle, /* use this trace handle */ + PRUint32 userData0, /* User supplied data word 0 */ + PRUint32 userData1, /* User supplied data word 1 */ + PRUint32 userData2, /* User supplied data word 2 */ + PRUint32 userData3, /* User supplied data word 3 */ + PRUint32 userData4, /* User supplied data word 4 */ + PRUint32 userData5, /* User supplied data word 5 */ + PRUint32 userData6, /* User supplied data word 6 */ + PRUint32 userData7 /* User supplied data word 7 */ +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_SetTraceOption() -- Control the Trace Facility +** +** DESCRIPTION: +** PR_SetTraceOption() controls the Trace Facility. Depending on +** command and value, attributes of the Trace Facility may be +** changed. +** +** INPUTS: +** command: An enumerated value in the set of PRTraceOption. +** value: pointer to the data to be set. Type of the data is +** dependent on command; for each value of command, the type +** and meaning of dereferenced value is shown. +** +** PRTraceBufSize: unsigned long: the size of the trace buffer, +** in bytes. +** +** PRTraceEnable: PRTraceHandle. The trace handle to be +** enabled. +** +** PRTraceDisable: PRTraceHandle. The trace handle to be +** disabled. +** +** PRTraceSuspend: void. value must be NULL. All tracing is +** suspended. +** +** PRTraceResume: void. value must be NULL. Tracing for all +** previously enabled, prior to a PRTraceSuspend, is resumed. +** +** PRTraceStopRecording: void. value must be NULL. If recording +** (see: ** PR_RecordTraceEntries()) is being done, +** PRTraceStopRecording causes PR_RecordTraceEntries() to return +** to its caller. If recording is not being done, this function +** has no effect. +** +** PRTraceSuspendRecording: void. Must be NULL. If recording is +** being done, PRTraceSuspendRecording causes further writes to +** the trace file to be suspended. Data in the in-memory +** trace buffer that would ordinarily be written to the +** trace file will not be written. Trace entries will continue +** to be entered in the in-memory buffer. If the Trace Facility +** recording is already in a suspended state, the call has no +** effect. +** +** PRTraceResumeRecording: void. value must be NULL. If +** recording for the Trace Facility has been previously been +** suspended, this causes recording to resume. Recording resumes +** with the next in-memory buffer segment that would be written +** if trace recording had not been suspended. If recording is +** not currently suspended, the call has no effect. +** +** PRTraceLockHandles: void. value must be NULL. Locks the +** trace handle lock. While the trace handle lock is held, +** calls to PR_CreateTrace() will block until the lock is +** released. +** +** PRTraceUnlockHandles: void. value must be NULL. Unlocks the +** trace handle lock. +** +** OUTPUTS: +** The operation of the Trace Facility may be changed. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_SET_TRACE_OPTION(command,value)\ + PR_SetTraceOption((command),(value)) +#else +#define PR_SET_TRACE_OPTION(command,value) +#endif + +NSPR_API(void) +PR_SetTraceOption( + PRTraceOption command, /* One of the enumerated values */ + void *value /* command value or NULL */ +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetTraceOption() -- Retrieve settings from the Trace Facility +** +** DESCRIPTION: +** PR_GetTraceOption() retrieves the current setting of the +** Trace Facility control depending on command. +** +** +** PRTraceBufSize: unsigned long: the size of the trace buffer, +** in bytes. +** +** +** INPUTS: +** command: one of the enumerated values in PRTraceOptions +** valid for PR_GetTraceOption(). +** +** OUTPUTS: +** dependent on command. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_GET_TRACE_OPTION(command,value)\ + PR_GetTraceOption((command),(value)) +#else +#define PR_GET_TRACE_OPTION(command,value) +#endif + +NSPR_API(void) +PR_GetTraceOption( + PRTraceOption command, /* One of the enumerated values */ + void *value /* command value or NULL */ +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetTraceHandleFromName() -- Retrieve an existing +** handle by name. +** +** DESCRIPTION: +** PR_GetTraceHandleFromName() retreives an existing tracehandle +** using the name specified by qName and rName. +** +** INPUTS: +** qName: pointer to string. QName for this trace handle. +** +** rName: pointer to string. RName for this trace handle. +** +** +** OUTPUTS: returned. +** +** RETURNS: +** PRTraceHandle associated with qName and rName or NULL when +** there is no match. +** +** RESTRICTIONS: +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_GET_TRACE_HANDLE_FROM_NAME(handle,qName,rName)\ + (handle) = PR_GetTraceHandleFromName((qName),(rName)) +#else +#define PR_GET_TRACE_HANDLE_FROM_NAME(handle,qName,rName) +#endif + +NSPR_API(PRTraceHandle) +PR_GetTraceHandleFromName( + const char *qName, /* QName search argument */ + const char *rName /* RName search argument */ +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetTraceNameFromHandle() -- Retreive trace name +** by bandle. +** +** DESCRIPTION: +** PR_GetTraceNameFromHandle() retreives the existing qName, +** rName, and description for the referenced trace handle. +** +** INPUTS: handle: PRTraceHandle. +** +** OUTPUTS: pointers to the Trace Facility's copy of qName, +** rName and description. ... Don't mess with these values. +** They're mine. +** +** RETURNS: void +** +** RESTRICTIONS: +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_GET_TRACE_NAME_FROM_HANDLE(handle,qName,rName,description)\ + PR_GetTraceNameFromHandle((handle),(qName),(rName),(description)) +#else +#define PR_GET_TRACE_NAME_FROM_HANDLE(handle,qName,rName,description) +#endif + +NSPR_API(void) +PR_GetTraceNameFromHandle( + PRTraceHandle handle, /* handle as search argument */ + const char **qName, /* pointer to associated QName */ + const char **rName, /* pointer to associated RName */ + const char **description /* pointer to associated description */ +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_FindNextTraceQname() -- Retrieive a QName handle +** iterator. +** +** DESCRIPTION: +** PR_FindNextTraceQname() retreives the first or next trace +** QName handle, depending on the value of handle, from the trace +** database. The PRTraceHandle returned can be used as an +** iterator to traverse the QName handles in the Trace database. +** +** INPUTS: +** handle: When NULL, PR_FindNextQname() returns the first QName +** handle. When a handle is a valid PRTraceHandle previously +** retreived using PR_FindNextQname() the next QName handle is +** retreived. +** +** OUTPUTS: returned. +** +** RETURNS: +** PRTraceHandle or NULL when there are no trace handles. +** +** RESTRICTIONS: +** Iterating thru the trace handles via FindFirst/FindNext +** should be done under protection of the trace handle lock. +** See: PR_SetTraceOption( PRLockTraceHandles ). +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_FIND_NEXT_TRACE_QNAME(next,handle)\ + (next) = PR_FindNextTraceQname((handle)) +#else +#define PR_FIND_NEXT_TRACE_QNAME(next,handle) +#endif + +NSPR_API(PRTraceHandle) +PR_FindNextTraceQname( + PRTraceHandle handle +); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_FindNextTraceRname() -- Retrieive an RName handle +** iterator. +** +** DESCRIPTION: +** PR_FindNextTraceRname() retreives the first or next trace +** RName handle, depending on the value of handle, from the trace +** database. The PRTraceHandle returned can be used as an +** iterator to traverse the RName handles in the Trace database. +** +** INPUTS: +** rhandle: When NULL, PR_FindNextRname() returns the first +** RName handle. When a handle is a valid PRTraceHandle +** previously retreived using PR_FindNextRname() the next RName +** handle is retreived. +** qhandle: A valid PRTraceHandle retruned from a previous call +** to PR_FIND_NEXT_TRACE_QNAME(). +** +** OUTPUTS: returned. +** +** RETURNS: +** PRTraceHandle or NULL when there are no trace handles. +** +** RESTRICTIONS: +** Iterating thru the trace handles via FindNext should be done +** under protection of the trace handle lock. See: ( +** PR_SetTraceOption( PRLockTraceHandles ). +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_FIND_NEXT_TRACE_RNAME(next,rhandle,qhandle)\ + (next) = PR_FindNextTraceRname((rhandle),(qhandle)) +#else +#define PR_FIND_NEXT_TRACE_RNAME(next,rhandle,qhandle) +#endif + +NSPR_API(PRTraceHandle) +PR_FindNextTraceRname( + PRTraceHandle rhandle, + PRTraceHandle qhandle +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_RecordTraceEntries() -- Write trace entries to external media +** +** DESCRIPTION: +** PR_RecordTraceEntries() causes entries in the in-memory trace +** buffer to be written to external media. +** +** When PR_RecordTraceEntries() is called from an application +** thread, the function appears to block until another thread +** calls PR_SetTraceOption() with the PRTraceStopRecording +** option. This suggests that PR_RecordTraceEntries() should be +** called from a user supplied thread whose only job is to +** record trace entries. +** +** The environment variable NSPR_TRACE_LOG controls the operation +** of this function. When NSPR_TRACE_LOG is not defined in the +** environment, no recording of trace entries occurs. When +** NSPR_TRACE_LOG is defined, the value of its definition must be +** the filename of the file to receive the trace entry buffer. +** +** PR_RecordTraceEntries() attempts to record the in-memory +** buffer to a file, subject to the setting of the environment +** variable NSPR_TRACE_LOG. It is possible because of system +** load, the thread priority of the recording thread, number of +** active trace records being written over time, and other +** variables that some trace records can be lost. ... In other +** words: don't bet the farm on getting everything. +** +** INPUTS: none +** +** OUTPUTS: none +** +** RETURNS: PR_STATUS +** PR_SUCCESS no errors were found. +** PR_FAILURE errors were found. +** +** RESTRICTIONS: +** Only one thread can call PR_RecordTraceEntries() within a +** process. +** +** On error, PR_RecordTraceEntries() may return prematurely. +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_RECORD_TRACE_ENTRIES()\ + PR_RecordTraceEntries() +#else +#define PR_RECORD_TRACE_ENTRIES() +#endif + +NSPR_API(void) +PR_RecordTraceEntries( + void +); + +/* ----------------------------------------------------------------------- +** FUNCTION: PR_GetTraceEntries() -- Retreive trace entries from +** the Trace Facility +** +** DESCRIPTION: +** PR_GetTraceEntries() retreives trace entries from the Trace +** Facility. Up to count trace entries are copied from the Trace +** Facility into buffer. Only those trace entries that have not +** been copied via a previous call to PR_GetTraceEntries() are +** copied. The actual number copied is placed in the PRInt32 +** variable pointed to by found. +** +** If more than count trace entries have entered the Trace +** Facility since the last call to PR_GetTraceEntries() +** a lost data condition is returned. In this case, the most +** recent count trace entries are copied into buffer and found is +** set to count. +** +** INPUTS: +** count. The number of trace entries to be copied into buffer. +** +** +** OUTPUTS: +** buffer. An array of PRTraceEntries. The buffer is supplied +** by the caller. +** +** found: 32bit signed integer. The number of PRTraceEntries +** actually copied. found is always less than or equal to count. +** +** RETURNS: +** zero when there is no lost data. +** non-zero when some PRTraceEntries have been lost. +** +** RESTRICTIONS: +** This is a real performance pig. The copy out operation is bad +** enough, but depending on then frequency of calls to the +** function, serious performance impact to the operating +** application may be realized. ... YMMV. +** +*/ +#if defined (DEBUG) || defined (FORCE_NSPR_TRACE) +#define PR_GET_TRACE_ENTRIES(buffer,count,found)\ + PR_GetTraceEntries((buffer),(count),(found)) +#else +#define PR_GET_TRACE_ENTRIES(buffer,count,found) +#endif + +NSPR_API(PRIntn) +PR_GetTraceEntries( + PRTraceEntry *buffer, /* where to write output */ + PRInt32 count, /* number to get */ + PRInt32 *found /* number you got */ +); + +PR_END_EXTERN_C + +#endif /* prtrace_h___ */ + diff --git a/nsprpub/pr/include/prtypes.h b/nsprpub/pr/include/prtypes.h new file mode 100644 index 0000000000..7400367f89 --- /dev/null +++ b/nsprpub/pr/include/prtypes.h @@ -0,0 +1,561 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prtypes.h +** Description: Definitions of NSPR's basic types +** +** Prototypes and macros used to make up for deficiencies that we have found +** in ANSI environments. +** +** Since we do not wrap <stdlib.h> and all the other standard headers, authors +** of portable code will not know in general that they need these definitions. +** Instead of requiring these authors to find the dependent uses in their code +** and take the following steps only in those C files, we take steps once here +** for all C files. +**/ + +#ifndef prtypes_h___ +#define prtypes_h___ + +#ifdef MDCPUCFG +#include MDCPUCFG +#else +#include "prcpucfg.h" +#endif + +#include <stddef.h> + +/*********************************************************************** +** MACROS: PR_EXTERN +** PR_IMPLEMENT +** DESCRIPTION: +** These are only for externally visible routines and globals. For +** internal routines, just use "extern" for type checking and that +** will not export internal cross-file or forward-declared symbols. +** Define a macro for declaring procedures return types. We use this to +** deal with windoze specific type hackery for DLL definitions. Use +** PR_EXTERN when the prototype for the method is declared. Use +** PR_IMPLEMENT for the implementation of the method. +** +** Example: +** in dowhim.h +** PR_EXTERN( void ) DoWhatIMean( void ); +** in dowhim.c +** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; } +** +** +***********************************************************************/ +#if defined(WIN32) + +#define PR_EXPORT(__type) extern __declspec(dllexport) __type +#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPORT(__type) __declspec(dllimport) __type +#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type + +#define PR_EXTERN(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT(__type) __declspec(dllexport) __type +#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type + +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#elif defined(XP_OS2) && defined(__declspec) + +#define PR_EXPORT(__type) extern __declspec(dllexport) __type +#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPORT(__type) extern __declspec(dllimport) __type +#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type + +#define PR_EXTERN(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT(__type) __declspec(dllexport) __type +#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type + +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#else /* Unix */ + +/* GCC 3.3 and later support the visibility attribute. */ +#if (__GNUC__ >= 4) || \ + (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default"))) +#else +#define PR_VISIBILITY_DEFAULT +#endif + +#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type + +#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type +#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#endif + +#if defined(_NSPR_BUILD_) +#define NSPR_API(__type) PR_EXPORT(__type) +#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type) +#else +#define NSPR_API(__type) PR_IMPORT(__type) +#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type) +#endif + +/*********************************************************************** +** MACROS: PR_BEGIN_MACRO +** PR_END_MACRO +** DESCRIPTION: +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +***********************************************************************/ +#define PR_BEGIN_MACRO do { +#define PR_END_MACRO } while (0) + +/*********************************************************************** +** MACROS: PR_BEGIN_EXTERN_C +** PR_END_EXTERN_C +** DESCRIPTION: +** Macro shorthands for conditional C++ extern block delimiters. +***********************************************************************/ +#ifdef __cplusplus +#define PR_BEGIN_EXTERN_C extern "C" { +#define PR_END_EXTERN_C } +#else +#define PR_BEGIN_EXTERN_C +#define PR_END_EXTERN_C +#endif + +/*********************************************************************** +** MACROS: PR_BIT +** PR_BITMASK +** DESCRIPTION: +** Bit masking macros. XXX n must be <= 31 to be portable +***********************************************************************/ +#define PR_BIT(n) ((PRUint32)1 << (n)) +#define PR_BITMASK(n) (PR_BIT(n) - 1) + +/*********************************************************************** +** MACROS: PR_ROUNDUP +** PR_MIN +** PR_MAX +** PR_ABS +** DESCRIPTION: +** Commonly used macros for operations on compatible types. +***********************************************************************/ +#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y)) +#define PR_MIN(x,y) ((x)<(y)?(x):(y)) +#define PR_MAX(x,y) ((x)>(y)?(x):(y)) +#define PR_ABS(x) ((x)<0?-(x):(x)) + +/*********************************************************************** +** MACROS: PR_ARRAY_SIZE +** DESCRIPTION: +** The number of elements in an array. +***********************************************************************/ +#define PR_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) + +PR_BEGIN_EXTERN_C + +/* +** Starting in NSPR 4.9.5, NSPR's exact-width integer types should match +** the exact-width integer types defined in <stdint.h>. This allows sloppy +** code to use PRInt{N} and int{N}_t interchangeably. +** +** The 8-bit and 16-bit integer types can only be defined using char and +** short. All platforms define the 32-bit integer types using int. So only +** the 64-bit integer types could be defined differently. +** +** NSPR's original strategy was to use the "shortest" 64-bit integer type: +** if long is 64-bit, then prefer it over long long. This strategy is also +** used by Linux/glibc, FreeBSD, and NetBSD. +** +** Other platforms use a different strategy: simply define the 64-bit +** integer types using long long. We define the PR_ALTERNATE_INT64_TYPEDEF +** macro on these platforms. Note that PR_ALTERNATE_INT64_TYPEDEF is for +** internal use by NSPR headers only. Do not define or test this macro in +** your code. +** +** NOTE: NSPR can't use <stdint.h> because C99 requires C++ code to define +** __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS to make all the macros +** defined in <stdint.h> available. This strange requirement is gone in +** C11. When most platforms ignore this C99 requirement, NSPR will be able +** to use <stdint.h>. A patch to do that is in NSPR bug 634793. +*/ + +#if defined(__APPLE__) || defined(__OpenBSD__) +#define PR_ALTERNATE_INT64_TYPEDEF +#endif + +/************************************************************************ +** TYPES: PRUint8 +** PRInt8 +** DESCRIPTION: +** The int8 types are known to be 8 bits each. There is no type that +** is equivalent to a plain "char". +************************************************************************/ +#if PR_BYTES_PER_BYTE == 1 +typedef unsigned char PRUint8; +/* +** There are two scenarios that require us to define PRInt8 as type 'char'. +** (1) +** Some cfront-based C++ compilers do not like 'signed char' and +** issue the warning message: +** warning: "signed" not implemented (ignored) +** For these compilers, we have to define PRInt8 as plain 'char'. +** Make sure that plain 'char' is indeed signed under these compilers. +** (2) +** Mozilla C++ code expects the PRInt{N} and int{N}_t types to match (see bug +** 634793). If a platform defines int8_t as 'char', but NSPR defines it as +** 'signed char', it results in a type mismatch. +** On such platforms we define PRInt8 as 'char' to avoid the mismatch. +*/ +#if (defined(HPUX) && defined(__cplusplus) /* reason 1*/ \ + && !defined(__GNUC__) && __cplusplus < 199707L) \ + || (defined(SCO) && defined(__cplusplus) /* reason 1 */ \ + && !defined(__GNUC__) && __cplusplus == 1L) \ + || (defined(__sun) && defined(__cplusplus)) /* reason 2 */ +typedef char PRInt8; +#else +typedef signed char PRInt8; +#endif +#else +#error No suitable type for PRInt8/PRUint8 +#endif + +/************************************************************************ + * MACROS: PR_INT8_MAX + * PR_INT8_MIN + * PR_UINT8_MAX + * DESCRIPTION: + * The maximum and minimum values of a PRInt8 or PRUint8. +************************************************************************/ + +#define PR_INT8_MAX 127 +#define PR_INT8_MIN (-128) +#define PR_UINT8_MAX 255U + +/************************************************************************ +** TYPES: PRUint16 +** PRInt16 +** DESCRIPTION: +** The int16 types are known to be 16 bits each. +************************************************************************/ +#if PR_BYTES_PER_SHORT == 2 +typedef unsigned short PRUint16; +typedef short PRInt16; +#else +#error No suitable type for PRInt16/PRUint16 +#endif + +/************************************************************************ + * MACROS: PR_INT16_MAX + * PR_INT16_MIN + * PR_UINT16_MAX + * DESCRIPTION: + * The maximum and minimum values of a PRInt16 or PRUint16. +************************************************************************/ + +#define PR_INT16_MAX 32767 +#define PR_INT16_MIN (-32768) +#define PR_UINT16_MAX 65535U + +/************************************************************************ +** TYPES: PRUint32 +** PRInt32 +** DESCRIPTION: +** The int32 types are known to be 32 bits each. +************************************************************************/ +#if PR_BYTES_PER_INT == 4 +typedef unsigned int PRUint32; +typedef int PRInt32; +#define PR_INT32(x) x +#define PR_UINT32(x) x ## U +#elif PR_BYTES_PER_LONG == 4 +typedef unsigned long PRUint32; +typedef long PRInt32; +#define PR_INT32(x) x ## L +#define PR_UINT32(x) x ## UL +#else +#error No suitable type for PRInt32/PRUint32 +#endif + +/************************************************************************ + * MACROS: PR_INT32_MAX + * PR_INT32_MIN + * PR_UINT32_MAX + * DESCRIPTION: + * The maximum and minimum values of a PRInt32 or PRUint32. +************************************************************************/ + +#define PR_INT32_MAX PR_INT32(2147483647) +#define PR_INT32_MIN (-PR_INT32_MAX - 1) +#define PR_UINT32_MAX PR_UINT32(4294967295) + +/************************************************************************ +** TYPES: PRUint64 +** PRInt64 +** DESCRIPTION: +** The int64 types are known to be 64 bits each. Care must be used when +** declaring variables of type PRUint64 or PRInt64. Different hardware +** architectures and even different compilers have varying support for +** 64 bit values. The only guaranteed portability requires the use of +** the LL_ macros (see prlong.h). +** +** MACROS: PR_INT64 +** PR_UINT64 +** DESCRIPTION: +** The PR_INT64 and PR_UINT64 macros provide a portable way for +** specifying 64-bit integer constants. They can only be used if +** PRInt64 and PRUint64 are defined as compiler-supported 64-bit +** integer types (i.e., if HAVE_LONG_LONG is defined, which is true +** for all the supported compilers topday). If PRInt64 and PRUint64 +** are defined as structs, the LL_INIT macro defined in prlong.h has +** to be used. +** +** MACROS: PR_INT64_MAX +** PR_INT64_MIN +** PR_UINT64_MAX +** DESCRIPTION: +** The maximum and minimum values of a PRInt64 or PRUint64. +************************************************************************/ +#ifdef HAVE_LONG_LONG +/* Keep this in sync with prlong.h. */ +#if PR_BYTES_PER_LONG == 8 && !defined(PR_ALTERNATE_INT64_TYPEDEF) +typedef long PRInt64; +typedef unsigned long PRUint64; +#define PR_INT64(x) x ## L +#define PR_UINT64(x) x ## UL +#elif defined(WIN32) && !defined(__GNUC__) +typedef __int64 PRInt64; +typedef unsigned __int64 PRUint64; +#define PR_INT64(x) x ## i64 +#define PR_UINT64(x) x ## ui64 +#else +typedef long long PRInt64; +typedef unsigned long long PRUint64; +#define PR_INT64(x) x ## LL +#define PR_UINT64(x) x ## ULL +#endif /* PR_BYTES_PER_LONG == 8 */ + +#define PR_INT64_MAX PR_INT64(0x7fffffffffffffff) +#define PR_INT64_MIN (-PR_INT64_MAX - 1) +#define PR_UINT64_MAX PR_UINT64(-1) +#else /* !HAVE_LONG_LONG */ +typedef struct { +#ifdef IS_LITTLE_ENDIAN + PRUint32 lo, hi; +#else + PRUint32 hi, lo; +#endif +} PRInt64; +typedef PRInt64 PRUint64; + +#define PR_INT64_MAX (PRInt64){0x7fffffff, 0xffffffff} +#define PR_INT64_MIN (PRInt64){0xffffffff, 0xffffffff} +#define PR_UINT64_MAX (PRUint64){0xffffffff, 0xffffffff} + +#endif /* !HAVE_LONG_LONG */ + +/************************************************************************ +** TYPES: PRUintn +** PRIntn +** DESCRIPTION: +** The PRIntn types are most appropriate for automatic variables. They are +** guaranteed to be at least 16 bits, though various architectures may +** define them to be wider (e.g., 32 or even 64 bits). These types are +** never valid for fields of a structure. +************************************************************************/ +#if PR_BYTES_PER_INT >= 2 +typedef int PRIntn; +typedef unsigned int PRUintn; +#else +#error 'sizeof(int)' not sufficient for platform use +#endif + +/************************************************************************ +** TYPES: PRFloat64 +** DESCRIPTION: +** NSPR's floating point type is always 64 bits. +************************************************************************/ +typedef double PRFloat64; + +/************************************************************************ +** TYPES: PRSize +** DESCRIPTION: +** A type for representing the size of objects. +************************************************************************/ +typedef size_t PRSize; + + +/************************************************************************ +** TYPES: PROffset32, PROffset64 +** DESCRIPTION: +** A type for representing byte offsets from some location. +************************************************************************/ +typedef PRInt32 PROffset32; +typedef PRInt64 PROffset64; + +/************************************************************************ +** TYPES: PRPtrDiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer subtraction. +************************************************************************/ +typedef ptrdiff_t PRPtrdiff; + +/************************************************************************ +** TYPES: PRUptrdiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +#ifdef _WIN64 +typedef PRUint64 PRUptrdiff; +#else +typedef unsigned long PRUptrdiff; +#endif + +/************************************************************************ +** TYPES: PRBool +** DESCRIPTION: +** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE +** for clarity of target type in assignments and actual arguments. Use +** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans +** just as you would C int-valued conditions. +************************************************************************/ +typedef PRIntn PRBool; +#define PR_TRUE 1 +#define PR_FALSE 0 + +/************************************************************************ +** TYPES: PRPackedBool +** DESCRIPTION: +** Use PRPackedBool within structs where bitfields are not desirable +** but minimum and consistant overhead matters. +************************************************************************/ +typedef PRUint8 PRPackedBool; + +/* +** Status code used by some routines that have a single point of failure or +** special status return. +*/ +typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; + +#ifndef __PRUNICHAR__ +#define __PRUNICHAR__ +#ifdef WIN32 +typedef wchar_t PRUnichar; +#else +typedef PRUint16 PRUnichar; +#endif +#endif + +/* +** WARNING: The undocumented data types PRWord and PRUword are +** only used in the garbage collection and arena code. Do not +** use PRWord and PRUword in new code. +** +** A PRWord is an integer that is the same size as a void*. +** It implements the notion of a "word" in the Java Virtual +** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine +** Specification, Addison-Wesley, September 1996. +** http://java.sun.com/docs/books/vmspec/index.html.) +*/ +#ifdef _WIN64 +typedef PRInt64 PRWord; +typedef PRUint64 PRUword; +#else +typedef long PRWord; +typedef unsigned long PRUword; +#endif + +/* + * PR_PRETEND_NORETURN, specified at the end of a function declaration, + * indicates that for the purposes of static analysis, this function does not + * return. (The function definition does not need to be annotated.) + * + * void PR_Assert(const char *s, const char *file, PRIntn ln) + * PR_PRETEND_NORETURN; + * + * Some static analyzers, like scan-build from clang, can use this information + * to eliminate false positives. From the upstream documentation of + * scan-build: + * This attribute is useful for annotating assertion handlers that actually + * can return, but for the purpose of using the analyzer we want to pretend + * that such functions do not return. + */ +#ifdef __clang_analyzer__ +#if __has_extension(attribute_analyzer_noreturn) +#define PR_PRETEND_NORETURN __attribute__((analyzer_noreturn)) +#endif +#endif + +#ifndef PR_PRETEND_NORETURN +#define PR_PRETEND_NORETURN /* no support */ +#endif + +#if defined(NO_NSPR_10_SUPPORT) +#else +/********* ???????????????? FIX ME ??????????????????????????? *****/ +/********************** Some old definitions until pr=>ds transition is done ***/ +/********************** Also, we are still using NSPR 1.0. GC ******************/ +/* +** Fundamental NSPR macros, used nearly everywhere. +*/ + +#define PR_PUBLIC_API PR_IMPLEMENT + +/* +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +*/ +#define NSPR_BEGIN_MACRO do { +#define NSPR_END_MACRO } while (0) + +/* +** Macro shorthands for conditional C++ extern block delimiters. +*/ +#ifdef NSPR_BEGIN_EXTERN_C +#undef NSPR_BEGIN_EXTERN_C +#endif +#ifdef NSPR_END_EXTERN_C +#undef NSPR_END_EXTERN_C +#endif + +#ifdef __cplusplus +#define NSPR_BEGIN_EXTERN_C extern "C" { +#define NSPR_END_EXTERN_C } +#else +#define NSPR_BEGIN_EXTERN_C +#define NSPR_END_EXTERN_C +#endif + +#include "obsolete/protypes.h" + +/********* ????????????? End Fix me ?????????????????????????????? *****/ +#endif /* NO_NSPR_10_SUPPORT */ + +/* +** Compile-time assert. "condition" must be a constant expression. +** The macro can be used only in places where an "extern" declaration is +** allowed. +*/ +#define PR_STATIC_ASSERT(condition) \ + extern void pr_static_assert(int arg[(condition) ? 1 : -1]) + +PR_END_EXTERN_C + +#endif /* prtypes_h___ */ + diff --git a/nsprpub/pr/include/prvrsion.h b/nsprpub/pr/include/prvrsion.h new file mode 100755 index 0000000000..99ed1db6c2 --- /dev/null +++ b/nsprpub/pr/include/prvrsion.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* author: jstewart */ + +#if defined(_PRVERSION_H) +#else +#define _PRVERSION_H + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* All components participating in the PR version protocol must expose + * a structure and a function. The structure is defined below and named + * according to the naming conventions outlined further below. The function + * is called libVersionPoint and returns a pointer to this structure. + */ + +/* on NT, always pack the structure the same. */ +#ifdef _WIN32 +#pragma pack(push, 8) +#endif + +typedef struct { + /* + * The first field defines which version of this structure is in use. + * At this time, only version 2 is specified. If this value is not + * 2, you must read no further into the structure. + */ + PRInt32 version; + + /* for Version 2, this is the body format. */ + PRInt64 buildTime; /* 64 bits - usecs since midnight, 1/1/1970 */ + char * buildTimeString;/* a human readable version of the time */ + + PRUint8 vMajor; /* Major version of this component */ + PRUint8 vMinor; /* Minor version of this component */ + PRUint8 vPatch; /* Patch level of this component */ + + PRBool beta; /* true if this is a beta component */ + PRBool debug; /* true if this is a debug component */ + PRBool special; /* true if this component is a special build */ + + char * filename; /* The original filename */ + char * description; /* description of this component */ + char * security; /* level of security in this component */ + char * copyright; /* The copyright for this file */ + char * comment; /* free form field for misc usage */ + char * specialString; /* the special variant for this build */ +} PRVersionDescription; + +/* on NT, restore the previous packing */ +#ifdef _WIN32 +#pragma pack(pop) +#endif + +/* + * All components must define an entrypoint named libVersionPoint which + * is of type versionEntryPointType. + * + * For example, for a library named libfoo, we would have: + * + * PRVersionDescription prVersionDescription_libfoo = + * { + * ... + * }; + * + * PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint(void) + * { + * return &prVersionDescription_libfoo; + * } + */ +typedef const PRVersionDescription *(*versionEntryPointType)(void); + +/* + * Where you declare your libVersionPoint, do it like this: + * PR_IMPLEMENT(const PRVersionDescription *) libVersionPoint(void) { + * fill it in... + * } + */ + +/* + * NAMING CONVENTION FOR struct + * + * all components should also expose a static PRVersionDescription + * The name of the struct should be calculated as follows: + * Take the value of filename. (If filename is not specified, calculate + * a short, unique string.) Convert all non-alphanumeric characters + * to '_'. To this, prepend "PRVersionDescription_". Thus for libfoo.so, + * the symbol name is "PRVersionDescription_libfoo_so". + * so the file should have + * PRVersionDescription PRVersionDescription_libfoo_so { fill it in }; + * on NT, this file should be declspec export. + */ + +PR_END_EXTERN_C + +#endif /* defined(_PRVERSION_H) */ + +/* prvrsion.h */ + diff --git a/nsprpub/pr/include/prwin16.h b/nsprpub/pr/include/prwin16.h new file mode 100644 index 0000000000..b545f04af0 --- /dev/null +++ b/nsprpub/pr/include/prwin16.h @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef prwin16_h___ +#define prwin16_h___ + +/* +** Condition use of this header on platform. +*/ +#if (defined(XP_PC) && !defined(_WIN32) && !defined(XP_OS2) && defined(MOZILLA_CLIENT)) || defined(WIN16) +#include <stdio.h> + +PR_BEGIN_EXTERN_C +/* +** Win16 stdio special case. +** To get stdio to work for Win16, all calls to printf() and related +** things must be called from the environment of the .EXE; calls to +** printf() from the .DLL send output to the bit-bucket. +** +** To make sure that PR_fprintf(), and related functions, work correctly, +** the actual stream I/O to stdout, stderr, stdin must be done in the +** .EXE. To do this, a hack is placed in _MD_Write() such that the +** fd for stdio handles results in a call to the .EXE. +** +** file w16stdio.c contains the functions that get called from NSPR +** to do the actual I/O. w16stdio.o must be statically linked with +** any application needing stdio for Win16. +** +** The address of these functions must be made available to the .DLL +** so he can call back to the .EXE. To do this, function +** PR_MD_RegisterW16StdioCallbacks() is called from the .EXE. +** The arguments are the functions defined in w16stdio.c +** At runtime, MD_Write() calls the registered functions, if any +** were registered. +** +** prinit.h contains a macro PR_STDIO_INIT() that calls the registration +** function for Win16; For other platforms, the macro is a No-Op. +** +** Note that stdio is not operational at all on Win16 GUI applications. +** This special case exists to provide stdio capability from the NSPR +** .DLL for command line applications only. NSPR's test cases are +** almost exclusively command line applications. +** +** See also: w16io.c, w16stdio.c +*/ +typedef PRInt32 (PR_CALLBACK *PRStdinRead)( void *buf, PRInt32 amount); +typedef PRInt32 (PR_CALLBACK *PRStdoutWrite)( void *buf, PRInt32 amount); +typedef PRInt32 (PR_CALLBACK *PRStderrWrite)( void *buf, PRInt32 amount); + +NSPR_API(PRStatus) +PR_MD_RegisterW16StdioCallbacks( + PRStdinRead inReadf, /* i: function pointer for stdin read */ + PRStdoutWrite outWritef, /* i: function pointer for stdout write */ + PRStderrWrite errWritef /* i: function pointer for stderr write */ +); + +NSPR_API(PRInt32) +_PL_W16StdioWrite( void *buf, PRInt32 amount ); + +NSPR_API(PRInt32) +_PL_W16StdioRead( void *buf, PRInt32 amount ); + +#define PR_STDIO_INIT() PR_MD_RegisterW16StdioCallbacks( \ + _PL_W16StdioRead, _PL_W16StdioWrite, _PL_W16StdioWrite ); \ + PR_INIT_CALLBACKS(); + +/* +** Win16 hackery. +** +*/ +struct PRMethodCallbackStr { + int (PR_CALLBACK *auxOutput)(const char *outputString); + size_t (PR_CALLBACK *strftime)(char *s, size_t len, const char *fmt, const struct tm *p); + void * (PR_CALLBACK *malloc)( size_t size ); + void * (PR_CALLBACK *calloc)(size_t n, size_t size ); + void * (PR_CALLBACK *realloc)( void* old_blk, size_t size ); + void (PR_CALLBACK *free)( void *ptr ); + void * (PR_CALLBACK *getenv)( const char *name); + int (PR_CALLBACK *putenv)( const char *assoc); + /* void * (PR_CALLBACK *perror)( const char *prefix ); */ +}; + +NSPR_API(void) PR_MDRegisterCallbacks(struct PRMethodCallbackStr *); + +int PR_CALLBACK _PL_W16CallBackPuts( const char *outputString ); +size_t PR_CALLBACK _PL_W16CallBackStrftime( + char *s, + size_t len, + const char *fmt, + const struct tm *p ); +void * PR_CALLBACK _PL_W16CallBackMalloc( size_t size ); +void * PR_CALLBACK _PL_W16CallBackCalloc( size_t n, size_t size ); +void * PR_CALLBACK _PL_W16CallBackRealloc( + void *old_blk, + size_t size ); +void PR_CALLBACK _PL_W16CallBackFree( void *ptr ); +void * PR_CALLBACK _PL_W16CallBackGetenv( const char *name ); +int PR_CALLBACK _PL_W16CallBackPutenv( const char *assoc ); + +/* +** Hackery! +** +** These functions are provided as static link points. +** This is to satisfy the quick port of Gromit to NSPR 2.0 +** ... Don't do this! ... alas, It may never go away. +** +*/ +NSPR_API(int) PR_MD_printf(const char *, ...); +NSPR_API(void) PR_MD_exit(int); +NSPR_API(size_t) PR_MD_strftime(char *, size_t, const char *, const struct tm *); +NSPR_API(int) PR_MD_sscanf(const char *, const char *, ...); +NSPR_API(void*) PR_MD_malloc( size_t size ); +NSPR_API(void*) PR_MD_calloc( size_t n, size_t size ); +NSPR_API(void*) PR_MD_realloc( void* old_blk, size_t size ); +NSPR_API(void) PR_MD_free( void *ptr ); +NSPR_API(char*) PR_MD_getenv( const char *name ); +NSPR_API(int) PR_MD_putenv( const char *assoc ); +NSPR_API(int) PR_MD_fprintf(FILE *fPtr, const char *fmt, ...); + +#define PR_INIT_CALLBACKS() \ + { \ + static struct PRMethodCallbackStr cbf = { \ + _PL_W16CallBackPuts, \ + _PL_W16CallBackStrftime, \ + _PL_W16CallBackMalloc, \ + _PL_W16CallBackCalloc, \ + _PL_W16CallBackRealloc, \ + _PL_W16CallBackFree, \ + _PL_W16CallBackGetenv, \ + _PL_W16CallBackPutenv, \ + }; \ + PR_MDRegisterCallbacks( &cbf ); \ + } + + +/* +** Get the exception context for Win16 MFC applications threads +*/ +NSPR_API(void *) PR_W16GetExceptionContext(void); +/* +** Set the exception context for Win16 MFC applications threads +*/ +NSPR_API(void) PR_W16SetExceptionContext(void *context); + +PR_END_EXTERN_C +#else +/* +** For platforms other than Win16, define +** PR_STDIO_INIT() as a No-Op. +*/ +#define PR_STDIO_INIT() +#endif /* WIN16 || MOZILLA_CLIENT */ + +#endif /* prwin16_h___ */ + + + + + + + + diff --git a/nsprpub/pr/src/Makefile.in b/nsprpub/pr/src/Makefile.in new file mode 100644 index 0000000000..d7c633daf5 --- /dev/null +++ b/nsprpub/pr/src/Makefile.in @@ -0,0 +1,355 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +DIRS = io linking malloc md memory misc threads + +ifeq ($(USE_PTHREADS), 1) + DIRS += pthreads +endif + +ifeq ($(USE_BTHREADS), 1) + DIRS += bthreads +endif + +ifeq ($(USE_CPLUS), 1) + DIRS += cplus +endif + +# +# Define platform-dependent OS_LIBS +# + +ifeq ($(OS_ARCH),SunOS) +MAPFILE = $(OBJDIR)/nsprmap.sun +GARBAGE += $(MAPFILE) +ifdef NS_USE_GCC +ifdef GCC_USE_GNU_LD +MKSHLIB += -Wl,--version-script,$(MAPFILE) +else +MKSHLIB += -Wl,-M,$(MAPFILE) +endif +else +MKSHLIB += -M $(MAPFILE) +endif +# +# In Solaris 2.6 or earlier, -lrt is called -lposix4. +# +LIBRT_TEST=$(firstword $(sort 5.7 $(OS_RELEASE))) +ifeq (5.7, $(LIBRT_TEST)) +LIBRT=-lrt +else +LIBRT=-lposix4 +endif + +ifdef USE_PTHREADS +OS_LIBS = -lpthread ${LIBRT} -lsocket -lnsl -ldl -lc +else +OS_LIBS = -lsocket -lnsl -ldl -lc +endif # USE_PTHREADS +ifeq ($(CPU_ARCH),sparc) +ifndef USE_64 +DSO_LDOPTS += -Wl,-f,\$$ORIGIN/cpu/\$$ISALIST/lib$(ULTRASPARC_LIBRARY)$(LIBRARY_VERSION).so +endif +endif # sparc +endif # SunOS + +ifeq ($(OS_ARCH),AIX) +DSO_LDOPTS += -binitfini::_PR_Fini +OS_LIBS = -lodm -lcfg +ifeq ($(CLASSIC_NSPR),1) +ifeq ($(OS_RELEASE),4.1) +OS_LIBS += -lsvld -lc +else +OS_LIBS += -ldl -lc +endif +else +ifeq ($(OS_RELEASE),4.1) +OS_LIBS += -lpthreads -lsvld -lC_r -lC -lc_r -lm /usr/lib/libc.a +else +OS_LIBS += -lpthreads -ldl -lC_r -lC -lc_r -lm /usr/lib/libc.a +endif +endif +endif + +# On AIX, we override malloc in non-pthread versions. On AIX 4.2 or +# above, this requires that we use the rtl-enabled version of libc.a. +ifeq ($(OS_ARCH),AIX) +ifneq (,$(filter-out 3.2 4.1,$(OS_RELEASE))) +ifneq ($(USE_PTHREADS),1) +BUILD_AIX_RTL_LIBC = 1 +AIX_RTL_LIBC = $(OBJDIR)/libc.a +endif +endif +endif + +ifeq ($(OS_ARCH),OS2) +MAPFILE = $(OBJDIR)/$(LIBRARY_NAME)$(LIBRARY_VERSION).def +ADD_TO_DEF_FILE = cat $(srcdir)/os2extra.def >> $(MAPFILE) +GARBAGE += $(MAPFILE) +MKSHLIB += $(MAPFILE) +endif + +# Linux, GNU/Hurd, and GNU/kFreeBSD systems +ifneq (,$(filter Linux GNU%,$(OS_ARCH))) +ifeq ($(USE_PTHREADS), 1) +ifeq ($(OS_TARGET),Android) +# Android has no libpthread.so in NDK +OS_LIBS = -ldl +else +OS_LIBS = -lpthread -ldl +endif +else +OS_LIBS = -ldl +endif +ifneq ($(OS_TARGET),Android) +# Android has no librt - realtime functions are in libc +OS_LIBS += -lrt +endif +endif + +ifeq ($(OS_ARCH),HP-UX) +ifeq ($(USE_PTHREADS), 1) +OS_LIBS = -lpthread -lrt +endif +ifeq ($(PTHREADS_USER), 1) +OS_LIBS = -lpthread +endif +ifeq ($(basename $(OS_RELEASE)),A.09) +OS_LIBS += -ldld -L/lib/pa1.1 -lm +else +OS_LIBS += -ldld -lm -lc +endif +ifneq ($(OS_TEST),ia64) +ifndef USE_64 +DSO_LDOPTS += +I PR_HPUX10xInit +endif +endif +endif + +ifeq ($(OS_ARCH),UNIXWARE) +OS_LIBS = -lsocket -lc +endif + +ifeq ($(OS_ARCH),WINNT) +ifdef NS_USE_GCC +OS_LIBS = -ladvapi32 -lws2_32 -lmswsock -lwinmm +else +OS_LIBS = advapi32.lib ws2_32.lib mswsock.lib winmm.lib +endif +endif + +ifeq ($(OS_ARCH),WINCE) +OS_LIBS = ws2.lib +endif + +ifeq ($(OS_TARGET),Android) +OS_LIBS += -llog +endif + +EXTRA_LIBS += $(OS_LIBS) + +# +# Define platform-dependent OBJS +# + +OBJS = \ + $(OBJDIR)/prvrsion.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prfdcach.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prmwait.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prmapopt.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/priometh.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/pripv6.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prlayer.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prlog.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prmmap.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prpolevt.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prprf.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prscanf.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prstdio.$(OBJ_SUFFIX) \ + threads/$(OBJDIR)/prcmon.$(OBJ_SUFFIX) \ + threads/$(OBJDIR)/prrwlock.$(OBJ_SUFFIX) \ + threads/$(OBJDIR)/prtpd.$(OBJ_SUFFIX) \ + linking/$(OBJDIR)/prlink.$(OBJ_SUFFIX) \ + 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) \ + misc/$(OBJDIR)/prcountr.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prdtoa.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prenv.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prerr.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prerror.$(OBJ_SUFFIX) \ + 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) \ + misc/$(OBJDIR)/praton.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prolock.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prrng.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prsystem.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prthinfo.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prtpool.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prtrace.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/prtime.$(OBJ_SUFFIX) + +ifdef USE_PTHREADS +OBJS += \ + pthreads/$(OBJDIR)/ptsynch.$(OBJ_SUFFIX) \ + pthreads/$(OBJDIR)/ptio.$(OBJ_SUFFIX) \ + pthreads/$(OBJDIR)/ptthread.$(OBJ_SUFFIX) \ + pthreads/$(OBJDIR)/ptmisc.$(OBJ_SUFFIX) +else +OBJS += \ + io/$(OBJDIR)/prdir.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prfile.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prio.$(OBJ_SUFFIX) \ + io/$(OBJDIR)/prsocket.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/pripcsem.$(OBJ_SUFFIX) + +ifndef USE_BTHREADS +OBJS += \ + threads/$(OBJDIR)/prcthr.$(OBJ_SUFFIX) \ + threads/$(OBJDIR)/prdump.$(OBJ_SUFFIX) \ + threads/$(OBJDIR)/prmon.$(OBJ_SUFFIX) \ + threads/$(OBJDIR)/prsem.$(OBJ_SUFFIX) \ + threads/combined/$(OBJDIR)/prucpu.$(OBJ_SUFFIX) \ + threads/combined/$(OBJDIR)/prucv.$(OBJ_SUFFIX) \ + threads/combined/$(OBJDIR)/prulock.$(OBJ_SUFFIX) \ + threads/combined/$(OBJDIR)/prustack.$(OBJ_SUFFIX) \ + threads/combined/$(OBJDIR)/pruthr.$(OBJ_SUFFIX) +endif + +endif + +ifeq ($(USE_CPLUS), 1) +OBJS += \ + cplus/$(OBJDIR)/rcbase.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rccv.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rcfileio.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rcinrval.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rcio.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rclock.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rcnetdb.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rcnetio.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rcthread.$(OBJ_SUFFIX) \ + cplus/$(OBJDIR)/rctime.$(OBJ_SUFFIX) +endif + +ifeq ($(OS_ARCH), WINNT) +RES=$(OBJDIR)/nspr.res +RESNAME=nspr.rc +endif # WINNT + +include $(srcdir)/md/$(PR_MD_ARCH_DIR)/objs.mk +ifdef USE_BTHREADS +include $(srcdir)/bthreads/objs.mk +endif + +LIBRARY_NAME = nspr +LIBRARY_VERSION = $(MOD_MAJOR_VERSION) + +RELEASE_LIBS = $(TARGETS) + +include $(topsrcdir)/config/rules.mk + +ifeq ($(BUILD_AIX_RTL_LIBC),1) +TARGETS += $(AIX_RTL_LIBC) +# XXX is this a shared library? +endif + +# +# Version information generation (begin) +# +ECHO = echo +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private +TINC = $(OBJDIR)/_pr_bld.h + +ifeq ($(OS_TARGET),OS2) +PROD = nspr$(MOD_MAJOR_VERSION).$(DLL_SUFFIX) +else +PROD = $(notdir $(SHARED_LIBRARY)) +endif + +NOW = $(MOD_DEPTH)/config/$(OBJDIR)/now +SH_DATE = $(shell date "+%Y-%m-%d %T") +SH_NOW = $(shell $(NOW)) + +ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT) + SUF = i64 +else + SUF = LL +endif + +DEFINES += -D_NSPR_BUILD_ + +GARBAGE += $(TINC) + +$(TINC): + @$(MAKE_OBJDIR) + @$(ECHO) '#define _BUILD_STRING "$(SH_DATE)"' > $(TINC) + @if test ! -z "$(SH_NOW)"; then \ + $(ECHO) '#define _BUILD_TIME $(SH_NOW)$(SUF)' >> $(TINC); \ + else \ + true; \ + fi + @$(ECHO) '#define _PRODUCTION "$(PROD)"' >> $(TINC) + + +$(OBJDIR)/prvrsion.$(OBJ_SUFFIX): prvrsion.c $(TINC) +ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT) + $(CC) -Fo$@ -c $(CFLAGS) -I$(OBJDIR) $< +else + $(CC) -o $@ -c $(CFLAGS) -I$(OBJDIR) $< +endif +# +# Version information generation (end) +# + + +# We use a 'build' target here to ensure that we build $(TARGETS) after +# looping over $(DIRS) to create the object files in a parallel build. +# Recipe commands are executed sequentially in a parallel build while +# target dependencies are executed in parallel. +export:: + $(MAKE) build + +# +# The Client build wants the shared libraries in $(dist_bindir) +# so we also install them there. +# + +build:: $(TARGETS) + $(INSTALL) -m 444 $(TARGETS) $(dist_libdir) +ifdef SHARED_LIBRARY +ifeq ($(OS_ARCH),HP-UX) + $(INSTALL) -m 755 $(SHARED_LIBRARY) $(dist_libdir) + $(INSTALL) -m 755 $(SHARED_LIBRARY) $(dist_bindir) +else + $(INSTALL) -m 444 $(SHARED_LIBRARY) $(dist_bindir) +endif +endif + +ifeq ($(BUILD_AIX_RTL_LIBC),1) +$(AIX_RTL_LIBC): /usr/ccs/lib/libc.a + rtl_enable -o $@ $< +endif diff --git a/nsprpub/pr/src/cplus/Makefile.in b/nsprpub/pr/src/cplus/Makefile.in new file mode 100644 index 0000000000..ec08eab1fb --- /dev/null +++ b/nsprpub/pr/src/cplus/Makefile.in @@ -0,0 +1,43 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CXXSRCS = \ + rcbase.cpp \ + rccv.cpp \ + rcfileio.cpp \ + rcinrval.cpp \ + rcio.cpp \ + rclock.cpp \ + rcnetdb.cpp \ + rcnetio.cpp \ + rcthread.cpp \ + rctime.cpp \ + $(NULL) + +OBJS = $(addprefix $(OBJDIR)/,$(CXXSRCS:.cpp=.$(OBJ_SUFFIX))) + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +HEADERS = $(wildcard $(srcdir)/*.h) + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/cplus/rcascii.h b/nsprpub/pr/src/cplus/rcascii.h new file mode 100644 index 0000000000..f8b59ce0f9 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcascii.h @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Class definitions to format ASCII data. +*/ + +#if defined(_RCASCII_H) +#else +#define _RCASCII_H + +/* +** RCFormatStuff +** This class maintains no state - that is the responsibility of +** the class' client. For each call to Sx_printf(), the StuffFunction +** will be called for each embedded "%" in the 'fmt' string and once +** for each interveaning literal. +*/ +class PR_IMPLEMENT(RCFormatStuff) +{ +public: + RCFormatStuff(); + virtual ~RCFormatStuff(); + + /* + ** Process the arbitrary argument list using as indicated by + ** the 'fmt' string. Each segment of the processing the stuff + ** function is called with the relavent translation. + */ + virtual PRInt32 Sx_printf(void *state, const char *fmt, ...); + + /* + ** The 'state' argument is not processed by the runtime. It + ** is merely passed from the Sx_printf() call. It is intended + ** to be used by the client to figure out what to do with the + ** new string. + ** + ** The new string ('stuff') is ASCII translation driven by the + ** Sx_printf()'s 'fmt' string. It is not guaranteed to be a null + ** terminated string. + ** + ** The return value is the number of bytes copied from the 'stuff' + ** string. It is accumulated for each of the calls to the stuff + ** function and returned from the original caller of Sx_printf(). + */ + virtual PRSize StuffFunction( + void *state, const char *stuff, PRSize stufflen) = 0; +}; /* RCFormatStuff */ + + +/* +** RCFormatBuffer +** The caller is supplying the buffer, the runtime is doing all +** the conversion. The object contains no state, so is reusable +** and reentrant. +*/ +class PR_IMPLEMENT(RCFormatBuffer): public RCFormatStuff +{ +public: + RCFormatBuffer(); + virtual ~RCFormatBuffer(); + + /* + ** Format the trailing arguments as indicated by the 'fmt' + ** string. Put the result in 'buffer'. Return the number + ** of bytes moved into 'buffer'. 'buffer' will always be + ** a properly terminated string even if the convresion fails. + */ + virtual PRSize Sn_printf( + char *buffer, PRSize length, const char *fmt, ...); + + virtual char *Sm_append(char *buffer, const char *fmt, ...); + +private: + /* + ** This class overrides the stuff function, does not preserve + ** its virtual-ness and no longer allows the clients to call + ** it in the clear. In other words, it is now the implementation + ** for this class. + */ + PRSize StuffFunction(void*, const char*, PRSize); + +}; /* RCFormatBuffer */ + +/* +** RCFormat +** The runtime is supplying the buffer. The object has state - the +** buffer. Each operation must run to completion before the object +** can be reused. When it is, the buffer is reset (whatever that +** means). The result of a conversion is available via the extractor. +** After extracted, the memory still belongs to the class - if the +** caller wants to retain or modify, it must first be copied. +*/ +class PR_IMPLEMENT(RCFormat): pubic RCFormatBuffer +{ +public: + RCFormat(); + virtual ~RCFormat(); + + /* + ** Translate the trailing arguments according to the 'fmt' + ** string and store the results in the object. + */ + virtual PRSize Sm_printf(const char *fmt, ...); + + /* + ** Extract the latest translation. + ** The object does not surrender the memory occupied by + ** the string. If the caller wishes to modify the data, + ** it must first be copied. + */ + const char*(); + +private: + char *buffer; + + RCFormat(const RCFormat&); + RCFormat& operator=(const RCFormat&); +}; /* RCFormat */ + +/* +** RCPrint +** The output is formatted and then written to an associated file +** descriptor. The client can provide a suitable file descriptor +** or can indicate that the output should be directed to one of +** the well-known "console" devices. +*/ +class PR_IMPLEMENT(RCPrint): public RCFormat +{ + virtual ~RCPrint(); + RCPrint(RCIO* output); + RCPrint(RCFileIO::SpecialFile output); + + virtual PRSize Printf(const char *fmt, ...); +private: + RCPrint(); +}; /* RCPrint */ + +#endif /* defined(_RCASCII_H) */ + +/* RCAscii.h */ diff --git a/nsprpub/pr/src/cplus/rcbase.cpp b/nsprpub/pr/src/cplus/rcbase.cpp new file mode 100644 index 0000000000..7c12e6d3d4 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcbase.cpp @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** RCBase.cpp - Mixin class for NSPR C++ wrappers +*/ + +#include "rcbase.h" + +RCBase::~RCBase() { } + +PRSize RCBase::GetErrorTextLength() { + return PR_GetErrorTextLength(); +} +PRSize RCBase::CopyErrorText(char *text) { + return PR_GetErrorText(text); +} + +void RCBase::SetError(PRErrorCode error, PRInt32 oserror) +{ + PR_SetError(error, oserror); +} + +void RCBase::SetErrorText(PRSize text_length, const char *text) +{ + PR_SetErrorText(text_length, text); +} + +/* rcbase.cpp */ diff --git a/nsprpub/pr/src/cplus/rcbase.h b/nsprpub/pr/src/cplus/rcbase.h new file mode 100644 index 0000000000..3cc91f8041 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcbase.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** RCBase.h - Mixin class for NSPR C++ wrappers +*/ + +#if defined(_RCRUNTIME_H) +#else +#define _RCRUNTIME_H + +#include <prerror.h> + +/* +** Class: RCBase (mixin) +** +** Generally mixed into every base class. The functions in this class are all +** static. Therefore this entire class is just syntatic sugar. It gives the +** illusion that errors (in particular) are retrieved via the same object +** that just reported a failure. It also (unfortunately) might lead one to +** believe that the errors are persistent in that object. They're not. +*/ + +class PR_IMPLEMENT(RCBase) +{ +public: + virtual ~RCBase(); + + static void AbortSelf(); + + static PRErrorCode GetError(); + static PRInt32 GetOSError(); + + static PRSize GetErrorTextLength(); + static PRSize CopyErrorText(char *text); + + static void SetError(PRErrorCode error, PRInt32 oserror); + static void SetErrorText(PRSize textLength, const char *text); + +protected: + RCBase() { } +}; /* RCObject */ + +inline PRErrorCode RCBase::GetError() { + return PR_GetError(); +} +inline PRInt32 RCBase::GetOSError() { + return PR_GetOSError(); +} + +#endif /* defined(_RCRUNTIME_H) */ + +/* rcbase.h */ diff --git a/nsprpub/pr/src/cplus/rccv.cpp b/nsprpub/pr/src/cplus/rccv.cpp new file mode 100644 index 0000000000..f124385350 --- /dev/null +++ b/nsprpub/pr/src/cplus/rccv.cpp @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** RCCondition - C++ wrapper around NSPR's PRCondVar +*/ + +#include "rccv.h" + +#include <prlog.h> +#include <prerror.h> +#include <prcvar.h> + +RCCondition::RCCondition(class RCLock *lock): RCBase() +{ + cv = PR_NewCondVar(lock->lock); + PR_ASSERT(NULL != cv); + timeout = PR_INTERVAL_NO_TIMEOUT; +} /* RCCondition::RCCondition */ + +RCCondition::~RCCondition() +{ + if (NULL != cv) { + PR_DestroyCondVar(cv); + } +} /* RCCondition::~RCCondition */ + +PRStatus RCCondition::Wait() +{ + PRStatus rv; + PR_ASSERT(NULL != cv); + if (NULL == cv) + { + SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + } + else { + rv = PR_WaitCondVar(cv, timeout.interval); + } + return rv; +} /* RCCondition::Wait */ + +PRStatus RCCondition::Notify() +{ + return PR_NotifyCondVar(cv); +} /* RCCondition::Notify */ + +PRStatus RCCondition::Broadcast() +{ + return PR_NotifyAllCondVar(cv); +} /* RCCondition::Broadcast */ + +PRStatus RCCondition::SetTimeout(const RCInterval& tmo) +{ + if (NULL == cv) + { + SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + timeout = tmo; + return PR_SUCCESS; +} /* RCCondition::SetTimeout */ + +RCInterval RCCondition::GetTimeout() const { + return timeout; +} + +/* rccv.cpp */ diff --git a/nsprpub/pr/src/cplus/rccv.h b/nsprpub/pr/src/cplus/rccv.h new file mode 100644 index 0000000000..afeec61614 --- /dev/null +++ b/nsprpub/pr/src/cplus/rccv.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** RCCondition - C++ wrapper around NSPR's PRCondVar +** +** Conditions have a notion of timeouts. A thread that waits on a condition +** will resume execution when the condition is notified OR when a specified +** interval of time has expired. +** +** Most applications don't adjust the timeouts on conditions. The literature +** would argue that all threads waiting on a single condition must have the +** same semantics. But if an application wishes to modify the timeout with +** (perhaps) each wait call, that modification should be done consistantly +** and under protection of the condition's associated lock. +** +** The default timeout is infinity. +*/ + +#if defined(_RCCOND_H) +#else +#define _RCCOND_H + +#include "rclock.h" +#include "rcbase.h" +#include "rcinrval.h" + +struct PRCondVar; + +class PR_IMPLEMENT(RCCondition): public RCBase +{ +public: + RCCondition(RCLock*); /* associates CV with a lock and infinite tmo */ + virtual ~RCCondition(); + + virtual PRStatus Wait(); /* applies object's current timeout */ + + virtual PRStatus Notify(); /* perhaps ready one thread */ + virtual PRStatus Broadcast(); /* perhaps ready many threads */ + + virtual PRStatus SetTimeout(const RCInterval&); + /* set object's current timeout value */ + +private: + PRCondVar *cv; + RCInterval timeout; + + RCCondition(); + RCCondition(const RCCondition&); + void operator=(const RCCondition&); + +public: + RCInterval GetTimeout() const; +}; /* RCCondition */ + +inline RCCondition::RCCondition(): RCBase() { } +inline RCCondition::RCCondition(const RCCondition&): RCBase() { } +inline void RCCondition::operator=(const RCCondition&) { } + +#endif /* defined(_RCCOND_H) */ + +/* RCCond.h */ diff --git a/nsprpub/pr/src/cplus/rcfileio.cpp b/nsprpub/pr/src/cplus/rcfileio.cpp new file mode 100644 index 0000000000..91c5fde6ff --- /dev/null +++ b/nsprpub/pr/src/cplus/rcfileio.cpp @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Class implementation for normal and special file I/O (ref: prio.h) +*/ + +#include "rcfileio.h" + +#include <string.h> + +RCFileIO::RCFileIO(): RCIO(RCIO::file) { } + +RCFileIO::~RCFileIO() { + if (NULL != fd) { + (void)Close(); + } +} + +PRInt64 RCFileIO::Available() +{ + return fd->methods->available(fd); +} + +PRStatus RCFileIO::Close() +{ + PRStatus rv = fd->methods->close(fd); + fd = NULL; + return rv; +} + +PRStatus RCFileIO::Delete(const char* filename) { + return PR_Delete(filename); +} + +PRStatus RCFileIO::FileInfo(RCFileInfo* info) const +{ + return fd->methods->fileInfo64(fd, &info->info); +} + +PRStatus RCFileIO::FileInfo(const char *name, RCFileInfo* info) +{ + return PR_GetFileInfo64(name, &info->info); +} + +PRStatus RCFileIO::Fsync() +{ + return fd->methods->fsync(fd); +} + +PRStatus RCFileIO::Open(const char *filename, PRIntn flags, PRIntn mode) +{ + fd = PR_Open(filename, flags, mode); + return (NULL == fd) ? PR_FAILURE : PR_SUCCESS; +} /* RCFileIO::Open */ + +PRInt32 RCFileIO::Read(void *buf, PRSize amount) +{ + return fd->methods->read(fd, buf, amount); +} + +PRInt64 RCFileIO::Seek(PRInt64 offset, RCIO::Whence how) +{ + PRSeekWhence whence; + switch (how) + { + case RCFileIO::set: whence = PR_SEEK_SET; break; + case RCFileIO::current: whence = PR_SEEK_CUR; break; + case RCFileIO::end: whence = PR_SEEK_END; break; + default: whence = (PRSeekWhence)-1; + } + return fd->methods->seek64(fd, offset, whence); +} /* RCFileIO::Seek */ + +PRInt32 RCFileIO::Write(const void *buf, PRSize amount) +{ + return fd->methods->write(fd, buf, amount); +} + +PRInt32 RCFileIO::Writev( + const PRIOVec *iov, PRSize size, const RCInterval& timeout) +{ + return fd->methods->writev(fd, iov, size, timeout); +} + +RCIO *RCFileIO::GetSpecialFile(RCFileIO::SpecialFile special) +{ + PRFileDesc* fd; + PRSpecialFD which; + RCFileIO* spec = NULL; + + switch (special) + { + case RCFileIO::input: which = PR_StandardInput; break; + case RCFileIO::output: which = PR_StandardOutput; break; + case RCFileIO::error: which = PR_StandardError; break; + default: which = (PRSpecialFD)-1; + } + fd = PR_GetSpecialFD(which); + if (NULL != fd) + { + spec = new RCFileIO(); + if (NULL != spec) { + spec->fd = fd; + } + } + return spec; +} /* RCFileIO::GetSpecialFile */ + + +/* +** The following methods have been made non-virtual and private. These +** default implementations are intended to NEVER be called. They +** are not valid for this type of I/O class (normal and special file). +*/ +PRStatus RCFileIO::Connect(const RCNetAddr&, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCFileIO::GetLocalName(RCNetAddr*) const +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCFileIO::GetPeerName(RCNetAddr*) const +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCFileIO::GetSocketOption(PRSocketOptionData*) const +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCFileIO::Listen(PRIntn) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRInt16 RCFileIO::Poll(PRInt16, PRInt16*) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return 0; +} + +PRInt32 RCFileIO::Recv(void*, PRSize, PRIntn, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +PRInt32 RCFileIO::Recvfrom(void*, PRSize, PRIntn, RCNetAddr*, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +PRInt32 RCFileIO::Send( + const void*, PRSize, PRIntn, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +PRInt32 RCFileIO::Sendto( + const void*, PRSize, PRIntn, const RCNetAddr&, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +RCIO* RCFileIO::Accept(RCNetAddr*, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} + +PRStatus RCFileIO::Bind(const RCNetAddr&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRInt32 RCFileIO::AcceptRead( + RCIO**, RCNetAddr**, void*, PRSize, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +PRStatus RCFileIO::SetSocketOption(const PRSocketOptionData*) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCFileIO::Shutdown(RCIO::ShutdownHow) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRInt32 RCFileIO::TransmitFile( + RCIO*, const void*, PRSize, RCIO::FileDisposition, const RCInterval&) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +/* +** Class implementation for file information object (ref: prio.h) +*/ + +RCFileInfo::~RCFileInfo() { } + +RCFileInfo::RCFileInfo(const RCFileInfo& her): RCBase() +{ + info = her.info; /* RCFileInfo::RCFileInfo */ +} + +RCTime RCFileInfo::CreationTime() const { + return RCTime(info.creationTime); +} + +RCTime RCFileInfo::ModifyTime() const { + return RCTime(info.modifyTime); +} + +RCFileInfo::FileType RCFileInfo::Type() const +{ + RCFileInfo::FileType type; + switch (info.type) + { + case PR_FILE_FILE: type = RCFileInfo::file; break; + case PR_FILE_DIRECTORY: type = RCFileInfo::directory; break; + default: type = RCFileInfo::other; + } + return type; +} /* RCFileInfo::Type */ diff --git a/nsprpub/pr/src/cplus/rcfileio.h b/nsprpub/pr/src/cplus/rcfileio.h new file mode 100644 index 0000000000..9e28b36ace --- /dev/null +++ b/nsprpub/pr/src/cplus/rcfileio.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Class definitions for normal and special file I/O (ref: prio.h) +*/ + +#if defined(_RCFILEIO_H) +#else +#define _RCFILEIO_H + +#include "rcio.h" +#include "rctime.h" + +/* +** One would normally create a concrete class, such as RCFileIO, but then +** pass around more generic references, ie., RCIO. +** +** This subclass of RCIO hides (makes private) the methods that are not +** applicable to normal files. +*/ + +class RCFileInfo; + +class PR_IMPLEMENT(RCFileIO): public RCIO +{ +public: + RCFileIO(); + virtual ~RCFileIO(); + + virtual PRInt64 Available(); + virtual PRStatus Close(); + static PRStatus Delete(const char *name); + virtual PRStatus FileInfo(RCFileInfo* info) const; + static PRStatus FileInfo(const char *name, RCFileInfo* info); + virtual PRStatus Fsync(); + virtual PRStatus Open(const char *name, PRIntn flags, PRIntn mode); + virtual PRInt32 Read(void *buf, PRSize amount); + virtual PRInt64 Seek(PRInt64 offset, RCIO::Whence how); + virtual PRInt32 Write(const void *buf, PRSize amount); + virtual PRInt32 Writev( + const PRIOVec *iov, PRSize size, + const RCInterval& timeout); + +private: + + /* These methods made private are unavailable for this object */ + RCFileIO(const RCFileIO&); + void operator=(const RCFileIO&); + + RCIO* Accept(RCNetAddr* addr, const RCInterval& timeout); + PRInt32 AcceptRead( + RCIO **newfd, RCNetAddr **address, void *buffer, + PRSize amount, const RCInterval& timeout); + PRStatus Bind(const RCNetAddr& addr); + PRStatus Connect(const RCNetAddr& addr, const RCInterval& timeout); + PRStatus GetLocalName(RCNetAddr *addr) const; + PRStatus GetPeerName(RCNetAddr *addr) const; + PRStatus GetSocketOption(PRSocketOptionData *data) const; + PRStatus Listen(PRIntn backlog); + PRInt16 Poll(PRInt16 in_flags, PRInt16 *out_flags); + PRInt32 Recv( + void *buf, PRSize amount, PRIntn flags, + const RCInterval& timeout); + PRInt32 Recvfrom( + void *buf, PRSize amount, PRIntn flags, + RCNetAddr* addr, const RCInterval& timeout); + PRInt32 Send( + const void *buf, PRSize amount, PRIntn flags, + const RCInterval& timeout); + PRInt32 Sendto( + const void *buf, PRSize amount, PRIntn flags, + const RCNetAddr& addr, + const RCInterval& timeout); + PRStatus SetSocketOption(const PRSocketOptionData *data); + PRStatus Shutdown(RCIO::ShutdownHow how); + PRInt32 TransmitFile( + RCIO *source, const void *headers, + PRSize hlen, RCIO::FileDisposition flags, + const RCInterval& timeout); +public: + + /* + ** The following function return a valid normal file object, + ** Such objects can be used for scanned input and console output. + */ + typedef enum { + input = PR_StandardInput, + output = PR_StandardOutput, + error = PR_StandardError + } SpecialFile; + + static RCIO *GetSpecialFile(RCFileIO::SpecialFile special); + +}; /* RCFileIO */ + +class PR_IMPLEMENT(RCFileInfo): public RCBase +{ +public: + typedef enum { + file = PR_FILE_FILE, + directory = PR_FILE_DIRECTORY, + other = PR_FILE_OTHER + } FileType; + +public: + RCFileInfo(); + RCFileInfo(const RCFileInfo&); + + virtual ~RCFileInfo(); + + PRInt64 Size() const; + RCTime CreationTime() const; + RCTime ModifyTime() const; + RCFileInfo::FileType Type() const; + + friend PRStatus RCFileIO::FileInfo(RCFileInfo*) const; + friend PRStatus RCFileIO::FileInfo(const char *name, RCFileInfo*); + +private: + PRFileInfo64 info; +}; /* RCFileInfo */ + +inline RCFileInfo::RCFileInfo(): RCBase() { } +inline PRInt64 RCFileInfo::Size() const { + return info.size; +} + +#endif /* defined(_RCFILEIO_H) */ diff --git a/nsprpub/pr/src/cplus/rcinrval.cpp b/nsprpub/pr/src/cplus/rcinrval.cpp new file mode 100644 index 0000000000..3805a57663 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcinrval.cpp @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** C++ interval times (ref: prinrval.h) +** +** An interval is a period of time. The start of the interval (epoch) +** must be defined by the application. The unit of time of an interval +** is platform dependent, therefore so is the maximum interval that is +** representable. However, that interval is never less that ~12 hours. +*/ + +#include "rcinrval.h" + +RCInterval::~RCInterval() { } + +RCInterval::RCInterval(RCInterval::RCReservedInterval special): RCBase() +{ + switch (special) + { + case RCInterval::now: + interval = PR_IntervalNow(); + break; + case RCInterval::no_timeout: + interval = PR_INTERVAL_NO_TIMEOUT; + break; + case RCInterval::no_wait: + interval = PR_INTERVAL_NO_WAIT; + break; + default: + break; + } +} /* RCInterval::RCInterval */ + +/* rcinrval.cpp */ diff --git a/nsprpub/pr/src/cplus/rcinrval.h b/nsprpub/pr/src/cplus/rcinrval.h new file mode 100644 index 0000000000..333209e618 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcinrval.h @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** C++ interval times (ref: prinrval.h) +** +** An interval is a period of time. The start of the interval (epoch) +** must be defined by the application. The unit of time of an interval +** is platform dependent, therefore so is the maximum interval that is +** representable. However, that interval is never less than ~6 hours. +*/ +#if defined(_RCINTERVAL_H) +#else +#define _RCINTERVAL_H + +#include "rcbase.h" +#include <prinrval.h> + +class PR_IMPLEMENT(RCInterval): public RCBase +{ +public: + typedef enum {now, no_timeout, no_wait} RCReservedInterval; + + virtual ~RCInterval(); + + RCInterval(); + + RCInterval(PRIntervalTime interval); + RCInterval(const RCInterval& copy); + RCInterval(RCReservedInterval special); + + void SetToNow(); + + void operator=(const RCInterval&); + void operator=(PRIntervalTime interval); + + PRBool operator<(const RCInterval&); + PRBool operator>(const RCInterval&); + PRBool operator==(const RCInterval&); + PRBool operator>=(const RCInterval&); + PRBool operator<=(const RCInterval&); + + RCInterval operator+(const RCInterval&); + RCInterval operator-(const RCInterval&); + RCInterval& operator+=(const RCInterval&); + RCInterval& operator-=(const RCInterval&); + + RCInterval operator/(PRUint32); + RCInterval operator*(PRUint32); + RCInterval& operator/=(PRUint32); + RCInterval& operator*=(PRUint32); + + + PRUint32 ToSeconds() const; + PRUint32 ToMilliseconds() const; + PRUint32 ToMicroseconds() const; + operator PRIntervalTime() const; + + static PRIntervalTime FromSeconds(PRUint32 seconds); + static PRIntervalTime FromMilliseconds(PRUint32 milli); + static PRIntervalTime FromMicroseconds(PRUint32 micro); + + friend class RCCondition; + +private: + PRIntervalTime interval; + +}; /* RCInterval */ + + +inline RCInterval::RCInterval(): RCBase() { } + +inline RCInterval::RCInterval(const RCInterval& his): RCBase() +{ + interval = his.interval; +} + +inline RCInterval::RCInterval(PRIntervalTime ticks): RCBase() +{ + interval = ticks; +} + +inline void RCInterval::SetToNow() { + interval = PR_IntervalNow(); +} + +inline void RCInterval::operator=(const RCInterval& his) +{ + interval = his.interval; +} + +inline void RCInterval::operator=(PRIntervalTime his) +{ + interval = his; +} + +inline PRBool RCInterval::operator==(const RCInterval& his) +{ + return (interval == his.interval) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCInterval::operator<(const RCInterval& his) +{ + return (interval < his.interval)? PR_TRUE : PR_FALSE; +} +inline PRBool RCInterval::operator>(const RCInterval& his) +{ + return (interval > his.interval) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCInterval::operator<=(const RCInterval& his) +{ + return (interval <= his.interval) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCInterval::operator>=(const RCInterval& his) +{ + return (interval <= his.interval) ? PR_TRUE : PR_FALSE; +} + +inline RCInterval RCInterval::operator+(const RCInterval& his) +{ + return RCInterval((PRIntervalTime)(interval + his.interval)); +} +inline RCInterval RCInterval::operator-(const RCInterval& his) +{ + return RCInterval((PRIntervalTime)(interval - his.interval)); +} +inline RCInterval& RCInterval::operator+=(const RCInterval& his) +{ + interval += his.interval; + return *this; +} +inline RCInterval& RCInterval::operator-=(const RCInterval& his) +{ + interval -= his.interval; + return *this; +} + +inline RCInterval RCInterval::operator/(PRUint32 him) +{ + return RCInterval((PRIntervalTime)(interval / him)); +} +inline RCInterval RCInterval::operator*(PRUint32 him) +{ + return RCInterval((PRIntervalTime)(interval * him)); +} + +inline RCInterval& RCInterval::operator/=(PRUint32 him) +{ + interval /= him; + return *this; +} + +inline RCInterval& RCInterval::operator*=(PRUint32 him) +{ + interval *= him; + return *this; +} + +inline PRUint32 RCInterval::ToSeconds() const +{ + return PR_IntervalToSeconds(interval); +} +inline PRUint32 RCInterval::ToMilliseconds() const +{ + return PR_IntervalToMilliseconds(interval); +} +inline PRUint32 RCInterval::ToMicroseconds() const +{ + return PR_IntervalToMicroseconds(interval); +} +inline RCInterval::operator PRIntervalTime() const { + return interval; +} + +inline PRIntervalTime RCInterval::FromSeconds(PRUint32 seconds) +{ + return PR_SecondsToInterval(seconds); +} +inline PRIntervalTime RCInterval::FromMilliseconds(PRUint32 milli) +{ + return PR_MillisecondsToInterval(milli); +} +inline PRIntervalTime RCInterval::FromMicroseconds(PRUint32 micro) +{ + return PR_MicrosecondsToInterval(micro); +} + +#endif /* defined(_RCINTERVAL_H) */ + +/* RCInterval.h */ diff --git a/nsprpub/pr/src/cplus/rcio.cpp b/nsprpub/pr/src/cplus/rcio.cpp new file mode 100644 index 0000000000..081a9f694a --- /dev/null +++ b/nsprpub/pr/src/cplus/rcio.cpp @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Base class implmenation for I/O (ref: prio.h) +*/ + +#include "rcio.h" + +RCIO::~RCIO() { } + +RCIO::RCIO(RCIO::RCIOType): RCBase() { } diff --git a/nsprpub/pr/src/cplus/rcio.h b/nsprpub/pr/src/cplus/rcio.h new file mode 100644 index 0000000000..1bd3ac4140 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcio.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Base class definitions for I/O (ref: prio.h) +** +** This class is a virtual base class. Construction must be done by a +** subclass, but the I/O operations can be done on a RCIO object reference. +*/ + +#if defined(_RCIO_H) +#else +#define _RCIO_H + +#include "rcbase.h" +#include "rcnetdb.h" +#include "rcinrval.h" + +#include "prio.h" + +class RCFileInfo; + +class PR_IMPLEMENT(RCIO): public RCBase +{ +public: + typedef enum { + open = PR_TRANSMITFILE_KEEP_OPEN, /* socket is left open after file + * is transmitted. */ + close = PR_TRANSMITFILE_CLOSE_SOCKET/* socket is closed after file + * is transmitted. */ + } FileDisposition; + + typedef enum { + set = PR_SEEK_SET, /* Set to value specified */ + current = PR_SEEK_CUR, /* Seek relative to current position */ + end = PR_SEEK_END /* seek past end of current eof */ + } Whence; + + typedef enum { + recv = PR_SHUTDOWN_RCV, /* receives will be disallowed */ + send = PR_SHUTDOWN_SEND, /* sends will be disallowed */ + both = PR_SHUTDOWN_BOTH /* sends & receives will be disallowed */ + } ShutdownHow; + +public: + virtual ~RCIO(); + + virtual RCIO* Accept(RCNetAddr* addr, const RCInterval& timeout) = 0; + virtual PRInt32 AcceptRead( + RCIO **nd, RCNetAddr **raddr, void *buf, + PRSize amount, const RCInterval& timeout) = 0; + virtual PRInt64 Available() = 0; + virtual PRStatus Bind(const RCNetAddr& addr) = 0; + virtual PRStatus Close() = 0; + virtual PRStatus Connect( + const RCNetAddr& addr, + const RCInterval& timeout) = 0; + virtual PRStatus FileInfo(RCFileInfo *info) const = 0; + virtual PRStatus Fsync() = 0; + virtual PRStatus GetLocalName(RCNetAddr *addr) const = 0; + virtual PRStatus GetPeerName(RCNetAddr *addr) const = 0; + virtual PRStatus GetSocketOption(PRSocketOptionData *data) const = 0; + virtual PRStatus Listen(PRIntn backlog) = 0; + virtual PRStatus Open(const char *name, PRIntn flags, PRIntn mode) = 0; + virtual PRInt16 Poll(PRInt16 in_flags, PRInt16 *out_flags) = 0; + virtual PRInt32 Read(void *buf, PRSize amount) = 0; + virtual PRInt32 Recv( + void *buf, PRSize amount, PRIntn flags, + const RCInterval& timeout) = 0; + virtual PRInt32 Recvfrom( + void *buf, PRSize amount, PRIntn flags, + RCNetAddr* addr, const RCInterval& timeout) = 0; + virtual PRInt64 Seek(PRInt64 offset, Whence how) = 0; + virtual PRInt32 Send( + const void *buf, PRSize amount, PRIntn flags, + const RCInterval& timeout) = 0; + virtual PRInt32 Sendto( + const void *buf, PRSize amount, PRIntn flags, + const RCNetAddr& addr, + const RCInterval& timeout) = 0; + virtual PRStatus SetSocketOption(const PRSocketOptionData *data) = 0; + virtual PRStatus Shutdown(ShutdownHow how) = 0; + virtual PRInt32 TransmitFile( + RCIO *source, const void *headers, + PRSize hlen, RCIO::FileDisposition flags, + const RCInterval& timeout) = 0; + virtual PRInt32 Write(const void *buf, PRSize amount) = 0; + virtual PRInt32 Writev( + const PRIOVec *iov, PRSize size, + const RCInterval& timeout) = 0; + +protected: + typedef enum { + file = PR_DESC_FILE, + tcp = PR_DESC_SOCKET_TCP, + udp = PR_DESC_SOCKET_UDP, + layered = PR_DESC_LAYERED + } RCIOType; + + RCIO(RCIOType); + + PRFileDesc *fd; /* where the real code hides */ + +private: + /* no default construction and no copies allowed */ + RCIO(); + RCIO(const RCIO&); + +}; /* RCIO */ + +#endif /* defined(_RCIO_H) */ + +/* RCIO.h */ + + diff --git a/nsprpub/pr/src/cplus/rclock.cpp b/nsprpub/pr/src/cplus/rclock.cpp new file mode 100644 index 0000000000..8c106de088 --- /dev/null +++ b/nsprpub/pr/src/cplus/rclock.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* +** C++ access to NSPR locks (PRLock) +*/ + +#include "rclock.h" +#include <prlog.h> + +RCLock::RCLock() +{ + lock = PR_NewLock(); /* it might be NULL */ + PR_ASSERT(NULL != lock); +} /* RCLock::RCLock */ + +RCLock::~RCLock() +{ + if (NULL != lock) { + PR_DestroyLock(lock); + } + lock = NULL; +} /* RCLock::~RCLock */ + +void RCLock::Acquire() +{ + PR_ASSERT(NULL != lock); + PR_Lock(lock); +} /* RCLock::Acquire */ + +void RCLock::Release() +{ + PRStatus rv; + PR_ASSERT(NULL != lock); + rv = PR_Unlock(lock); + PR_ASSERT(PR_SUCCESS == rv); +} /* RCLock::Release */ + +/* RCLock.cpp */ + diff --git a/nsprpub/pr/src/cplus/rclock.h b/nsprpub/pr/src/cplus/rclock.h new file mode 100644 index 0000000000..c86c2f17b6 --- /dev/null +++ b/nsprpub/pr/src/cplus/rclock.h @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** C++ access to NSPR locks (PRLock) +*/ + +#if defined(_RCLOCK_H) +#else +#define _RCLOCK_H + +#include "rcbase.h" + +#include <prlock.h> + +class PR_IMPLEMENT(RCLock): public RCBase +{ +public: + RCLock(); + virtual ~RCLock(); + + void Acquire(); /* non-reentrant */ + void Release(); /* should be by owning thread */ + + friend class RCCondition; + +private: + RCLock(const RCLock&); /* can't do that */ + void operator=(const RCLock&); /* nor that */ + + PRLock *lock; +}; /* RCLock */ + +/* +** Class: RCEnter +** +** In scope locks. You can only allocate them on the stack. The language +** will insure that they get released (by calling the destructor) when +** the thread leaves scope, even if via an exception. +*/ +class PR_IMPLEMENT(RCEnter) +{ +public: + ~RCEnter(); /* releases the lock */ + RCEnter(RCLock*); /* acquires the lock */ + +private: + RCLock *lock; + + RCEnter(); + RCEnter(const RCEnter&); + void operator=(const RCEnter&); + + void *operator new(PRSize) { + return NULL; + } + void operator delete(void*) { } +}; /* RCEnter */ + + +inline RCEnter::RCEnter(RCLock* ml) { + lock = ml; + lock->Acquire(); +} +inline RCEnter::~RCEnter() { + lock->Release(); + lock = NULL; +} + +#endif /* defined(_RCLOCK_H) */ + +/* RCLock.h */ diff --git a/nsprpub/pr/src/cplus/rcmon.h b/nsprpub/pr/src/cplus/rcmon.h new file mode 100644 index 0000000000..5d084efdea --- /dev/null +++ b/nsprpub/pr/src/cplus/rcmon.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Class: RCMonitor (ref prmonitor.h) +** +** RCMonitor.h - C++ wrapper around NSPR's monitors +*/ +#if defined(_RCMONITOR_H) +#else +#define _RCMONITOR_H + +#include "rcbase.h" +#include "rcinrval.h" + +struct PRMonitor; + +class PR_IMPLEMENT(RCMonitor): public RCBase +{ +public: + RCMonitor(); /* timeout is infinity */ + virtual ~RCMonitor(); + + virtual void Enter(); /* reentrant entry */ + virtual void Exit(); + + virtual void Notify(); /* possibly enable one thread */ + virtual void NotifyAll(); /* enable all waiters */ + + virtual void Wait(); /* applies object's timeout */ + + virtual void SetTimeout(const RCInterval& timeout); + +private: + PRMonitor *monitor; + RCInterval timeout; + +public: + RCInterval GetTimeout() const; /* get the current value */ + +}; /* RCMonitor */ + +#endif /* defined(_RCMONITOR_H) */ + +/* RCMonitor.h */ diff --git a/nsprpub/pr/src/cplus/rcnetdb.cpp b/nsprpub/pr/src/cplus/rcnetdb.cpp new file mode 100644 index 0000000000..19eb66540f --- /dev/null +++ b/nsprpub/pr/src/cplus/rcnetdb.cpp @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Base class implementation for network access functions (ref: prnetdb.h) +*/ + +#include "rclock.h" +#include "rcnetdb.h" + +#include <prmem.h> +#include <prlog.h> +#include <string.h> + +RCNetAddr::RCNetAddr(const RCNetAddr& his): RCBase() +{ + address = his.address; +} + +RCNetAddr::RCNetAddr(const RCNetAddr& his, PRUint16 port): RCBase() +{ + address = his.address; + switch (address.raw.family) + { + case PR_AF_INET: address.inet.port = port; break; + case PR_AF_INET6: address.ipv6.port = port; break; + default: break; + } +} /* RCNetAddr::RCNetAddr */ + +RCNetAddr::RCNetAddr(RCNetAddr::HostValue host, PRUint16 port): RCBase() +{ + PRNetAddrValue how; + switch (host) + { + case RCNetAddr::any: how = PR_IpAddrAny; break; + case RCNetAddr::loopback: how = PR_IpAddrLoopback; break; + default: PR_NOT_REACHED("This can't happen -- and did!"); + } + (void)PR_InitializeNetAddr(how, port, &address); +} /* RCNetAddr::RCNetAddr */ + +RCNetAddr::~RCNetAddr() { } + +void RCNetAddr::operator=(const RCNetAddr& his) { + address = his.address; +} + +PRStatus RCNetAddr::FromString(const char* string) +{ + return PR_StringToNetAddr(string, &address); +} + +void RCNetAddr::operator=(const PRNetAddr* addr) { + address = *addr; +} + +PRBool RCNetAddr::operator==(const RCNetAddr& his) const +{ + PRBool rv = EqualHost(his); + if (rv) + { + switch (address.raw.family) + { + case PR_AF_INET: + rv = (address.inet.port == his.address.inet.port); break; + case PR_AF_INET6: + rv = (address.ipv6.port == his.address.ipv6.port); break; + case PR_AF_LOCAL: + default: break; + } + } + return rv; +} /* RCNetAddr::operator== */ + +PRBool RCNetAddr::EqualHost(const RCNetAddr& his) const +{ + PRBool rv; + switch (address.raw.family) + { + case PR_AF_INET: + rv = (address.inet.ip == his.address.inet.ip); break; + case PR_AF_INET6: + rv = (0 == memcmp( + &address.ipv6.ip, &his.address.ipv6.ip, + sizeof(address.ipv6.ip))); + break; +#if defined(XP_UNIX) + case PR_AF_LOCAL: + rv = (0 == strncmp( + address.local.path, his.address.local.path, + sizeof(address.local.path))); + break; +#endif + default: break; + } + return rv; +} /* RCNetAddr::operator== */ + +PRStatus RCNetAddr::ToString(char *string, PRSize size) const +{ + return PR_NetAddrToString(&address, string, size); +} + +/* +** RCHostLookup +*/ + +RCHostLookup::~RCHostLookup() +{ + if (NULL != address) { + delete [] address; + } +} /* RCHostLookup::~RCHostLookup */ + +RCHostLookup::RCHostLookup(): RCBase() +{ + address = NULL; + max_index = 0; +} /* RCHostLookup::RCHostLookup */ + +PRStatus RCHostLookup::ByName(const char* name) +{ + PRStatus rv; + PRNetAddr addr; + PRHostEnt hostentry; + PRIntn index = 0, max; + RCNetAddr* vector = NULL; + RCNetAddr* old_vector = NULL; + void* buffer = PR_Malloc(PR_NETDB_BUF_SIZE); + if (NULL == buffer) { + return PR_FAILURE; + } + rv = PR_GetHostByName(name, (char*)buffer, PR_NETDB_BUF_SIZE, &hostentry); + if (PR_SUCCESS == rv) + { + for (max = 0, index = 0;; ++max) + { + index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr); + if (0 == index) { + break; + } + } + if (max > 0) + { + vector = new RCNetAddr[max]; + while (--max > 0) + { + index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr); + if (0 == index) { + break; + } + vector[index] = &addr; + } + { + RCEnter entry(&ml); + old_vector = address; + address = vector; + max_index = max; + } + if (NULL != old_vector) { + delete [] old_vector; + } + } + } + if (NULL != buffer) { + PR_DELETE(buffer); + } + return PR_SUCCESS; +} /* RCHostLookup::ByName */ + +PRStatus RCHostLookup::ByAddress(const RCNetAddr& host_addr) +{ + PRStatus rv; + PRNetAddr addr; + PRHostEnt hostentry; + PRIntn index = 0, max; + RCNetAddr* vector = NULL; + RCNetAddr* old_vector = NULL; + char *buffer = (char*)PR_Malloc(PR_NETDB_BUF_SIZE); + if (NULL == buffer) { + return PR_FAILURE; + } + rv = PR_GetHostByAddr(host_addr, buffer, PR_NETDB_BUF_SIZE, &hostentry); + if (PR_SUCCESS == rv) + { + for (max = 0, index = 0;; ++max) + { + index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr); + if (0 == index) { + break; + } + } + if (max > 0) + { + vector = new RCNetAddr[max]; + while (--max > 0) + { + index = PR_EnumerateHostEnt(index, &hostentry, 0, &addr); + if (0 == index) { + break; + } + vector[index] = &addr; + } + { + RCEnter entry(&ml); + old_vector = address; + address = vector; + max_index = max; + } + if (NULL != old_vector) { + delete [] old_vector; + } + } + } + if (NULL != buffer) { + PR_DELETE(buffer); + } + return PR_SUCCESS; +} /* RCHostLookup::ByAddress */ + +const RCNetAddr* RCHostLookup::operator[](PRUintn which) +{ + RCNetAddr* addr = NULL; + if (which < max_index) { + addr = &address[which]; + } + return addr; +} /* RCHostLookup::operator[] */ + +/* RCNetdb.cpp */ diff --git a/nsprpub/pr/src/cplus/rcnetdb.h b/nsprpub/pr/src/cplus/rcnetdb.h new file mode 100644 index 0000000000..14e96ecd85 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcnetdb.h @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Base class definitions for network access functions (ref: prnetdb.h) +*/ + +#if defined(_RCNETDB_H) +#else +#define _RCNETDB_H + +#include "rclock.h" +#include "rcbase.h" + +#include <prnetdb.h> + +class PR_IMPLEMENT(RCNetAddr): public RCBase +{ +public: + typedef enum { + any = PR_IpAddrAny, /* assign logical INADDR_ANY */ + loopback = PR_IpAddrLoopback /* assign logical INADDR_LOOPBACK */ + } HostValue; + + RCNetAddr(); /* default constructor is unit'd object */ + RCNetAddr(const RCNetAddr&); /* copy constructor */ + RCNetAddr(HostValue, PRUint16 port);/* init'd w/ 'special' assignments */ + RCNetAddr(const RCNetAddr&, PRUint16 port); + /* copy w/ port reassigment */ + + virtual ~RCNetAddr(); + + void operator=(const RCNetAddr&); + + virtual PRBool operator==(const RCNetAddr&) const; + /* compare of all relavent fields */ + virtual PRBool EqualHost(const RCNetAddr&) const; + /* compare of just host field */ + + +public: + + void operator=(const PRNetAddr*); /* construction from more primitive data */ + operator const PRNetAddr*() const; /* extraction of underlying representation */ + virtual PRStatus FromString(const char* string); + /* initialization from an ASCII string */ + virtual PRStatus ToString(char *string, PRSize size) const; + /* convert internal fromat to a string */ + +private: + + PRNetAddr address; + +}; /* RCNetAddr */ + +/* +** Class: RCHostLookup +** +** Abstractions to look up host names and addresses. +** +** This is a stateful class. One looks up the host by name or by +** address, then enumerates over a possibly empty array of network +** addresses. The storage for the addresses is owned by the class. +*/ + +class RCHostLookup: public RCBase +{ +public: + virtual ~RCHostLookup(); + + RCHostLookup(); + + virtual PRStatus ByName(const char* name); + virtual PRStatus ByAddress(const RCNetAddr&); + + virtual const RCNetAddr* operator[](PRUintn); + +private: + RCLock ml; + PRIntn max_index; + RCNetAddr* address; + + RCHostLookup(const RCHostLookup&); + RCHostLookup& operator=(const RCHostLookup&); +}; + +inline RCNetAddr::RCNetAddr(): RCBase() { } +inline RCNetAddr::operator const PRNetAddr*() const { + return &address; +} + + +#endif /* defined(_RCNETDB_H) */ + +/* RCNetdb.h */ + + diff --git a/nsprpub/pr/src/cplus/rcnetio.cpp b/nsprpub/pr/src/cplus/rcnetio.cpp new file mode 100644 index 0000000000..e351345e43 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcnetio.cpp @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Subclass implementation for streamed network I/O (ref: prio.h) +*/ + +#include "rcnetio.h" + +#include <private/pprio.h> + +RCNetStreamIO::~RCNetStreamIO() +{ + PRStatus rv = (fd->methods->close)(fd); + fd = NULL; +} + +RCNetStreamIO::RCNetStreamIO(): RCIO(RCIO::tcp) +{ + fd = PR_NewTCPSocket(); +} + +RCNetStreamIO::RCNetStreamIO(PRIntn protocol): RCIO(RCIO::tcp) +{ + fd = PR_Socket(PR_AF_INET, PR_SOCK_STREAM, protocol); +} + +RCIO* RCNetStreamIO::Accept(RCNetAddr* addr, const RCInterval& timeout) +{ + PRNetAddr peer; + RCNetStreamIO* rcio = NULL; + PRFileDesc* newfd = fd->methods->accept(fd, &peer, timeout); + if (NULL != newfd) + { + rcio = new RCNetStreamIO(); + if (NULL != rcio) + { + *addr = &peer; + rcio->fd = newfd; + } + else { + (void)(newfd->methods->close)(newfd); + } + } + return rcio; +} /* RCNetStreamIO::Accept */ + +PRInt32 RCNetStreamIO::AcceptRead( + RCIO **nd, RCNetAddr **raddr, void *buf, + PRSize amount, const RCInterval& timeout) +{ + PRNetAddr *from; + PRFileDesc *accepted; + PRInt32 rv = (fd->methods->acceptread)( + fd, &accepted, &from, buf, amount, timeout); + if (rv >= 0) + { + RCNetStreamIO *ns = new RCNetStreamIO(); + if (NULL != *nd) { + ns->fd = accepted; + } + else { + PR_Close(accepted); + rv = -1; + } + *nd = ns; + } + return rv; +} /* RCNetStreamIO::AcceptRead */ + +PRInt64 RCNetStreamIO::Available() +{ + return (fd->methods->available64)(fd); +} + +PRStatus RCNetStreamIO::Bind(const RCNetAddr& addr) +{ + return (fd->methods->bind)(fd, addr); +} + +PRStatus RCNetStreamIO::Connect(const RCNetAddr& addr, const RCInterval& timeout) +{ + return (fd->methods->connect)(fd, addr, timeout); +} + +PRStatus RCNetStreamIO::GetLocalName(RCNetAddr *addr) const +{ + PRNetAddr local; + PRStatus rv = (fd->methods->getsockname)(fd, &local); + if (PR_SUCCESS == rv) { + *addr = &local; + } + return rv; +} /* RCNetStreamIO::GetLocalName */ + +PRStatus RCNetStreamIO::GetPeerName(RCNetAddr *addr) const +{ + PRNetAddr peer; + PRStatus rv = (fd->methods->getpeername)(fd, &peer); + if (PR_SUCCESS == rv) { + *addr = &peer; + } + return rv; +} /* RCNetStreamIO::GetPeerName */ + +PRStatus RCNetStreamIO::GetSocketOption(PRSocketOptionData *data) const +{ + return (fd->methods->getsocketoption)(fd, data); +} + +PRStatus RCNetStreamIO::Listen(PRIntn backlog) +{ + return (fd->methods->listen)(fd, backlog); +} + +PRInt16 RCNetStreamIO::Poll(PRInt16 in_flags, PRInt16 *out_flags) +{ + return (fd->methods->poll)(fd, in_flags, out_flags); +} + +PRInt32 RCNetStreamIO::Read(void *buf, PRSize amount) +{ + return (fd->methods->read)(fd, buf, amount); +} + +PRInt32 RCNetStreamIO::Recv( + void *buf, PRSize amount, PRIntn flags, const RCInterval& timeout) +{ + return (fd->methods->recv)(fd, buf, amount, flags, timeout); +} + +PRInt32 RCNetStreamIO::Recvfrom( + void *buf, PRSize amount, PRIntn flags, + RCNetAddr* addr, const RCInterval& timeout) +{ + PRNetAddr peer; + PRInt32 rv = (fd->methods->recvfrom)( + fd, buf, amount, flags, &peer, timeout); + if (-1 != rv) { + *addr = &peer; + } + return rv; +} /* RCNetStreamIO::Recvfrom */ + +PRInt32 RCNetStreamIO::Send( + const void *buf, PRSize amount, PRIntn flags, const RCInterval& timeout) +{ + return (fd->methods->send)(fd, buf, amount, flags, timeout); +} + +PRInt32 RCNetStreamIO::Sendto( + const void *buf, PRSize amount, PRIntn flags, + const RCNetAddr& addr, const RCInterval& timeout) +{ + return (fd->methods->sendto)(fd, buf, amount, flags, addr, timeout); +} + +PRStatus RCNetStreamIO::SetSocketOption(const PRSocketOptionData *data) +{ + return (fd->methods->setsocketoption)(fd, data); +} + +PRStatus RCNetStreamIO::Shutdown(RCIO::ShutdownHow how) +{ + return (fd->methods->shutdown)(fd, (PRIntn)how); +} + +PRInt32 RCNetStreamIO::TransmitFile( + RCIO *source, const void *headers, PRSize hlen, + RCIO::FileDisposition flags, const RCInterval& timeout) +{ + RCNetStreamIO *src = (RCNetStreamIO*)source; + return (fd->methods->transmitfile)( + fd, src->fd, headers, hlen, (PRTransmitFileFlags)flags, timeout); +} + +PRInt32 RCNetStreamIO::Write(const void *buf, PRSize amount) +{ + return (fd->methods->write)(fd, buf, amount); +} + +PRInt32 RCNetStreamIO::Writev( + const PRIOVec *iov, PRSize size, const RCInterval& timeout) +{ + return (fd->methods->writev)(fd, iov, size, timeout); +} + +/* +** Invalid functions +*/ + +PRStatus RCNetStreamIO::Close() +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCNetStreamIO::FileInfo(RCFileInfo*) const +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRStatus RCNetStreamIO::Fsync() +{ + return (fd->methods->fsync)(fd); +} + +PRStatus RCNetStreamIO::Open(const char*, PRIntn, PRIntn) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +PRInt64 RCNetStreamIO::Seek(PRInt64, RCIO::Whence) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +/* RCNetStreamIO.cpp */ + + diff --git a/nsprpub/pr/src/cplus/rcnetio.h b/nsprpub/pr/src/cplus/rcnetio.h new file mode 100644 index 0000000000..7f6b669d55 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcnetio.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Subclass definitions for network I/O (ref: prio.h) +*/ + +#if defined(_RCNETIO_H) +#else +#define _RCNETIO_H + +#include "rcbase.h" +#include "rcinrval.h" +#include "rcio.h" +#include "rcnetdb.h" + +#include "prio.h" + +class RCFileInfo; + +/* +** Class: RCNetStreamIO (ref prio.h) +** +** Streamed (reliable) network I/O (e.g., TCP). +** This class hides (makes private) the functions that are not applicable +** to network I/O (i.e., those for file I/O). +*/ + +class PR_IMPLEMENT(RCNetStreamIO): public RCIO +{ + +public: + RCNetStreamIO(); + virtual ~RCNetStreamIO(); + + virtual RCIO* Accept(RCNetAddr* addr, const RCInterval& timeout); + virtual PRInt32 AcceptRead( + RCIO **nd, RCNetAddr **raddr, void *buf, + PRSize amount, const RCInterval& timeout); + virtual PRInt64 Available(); + virtual PRStatus Bind(const RCNetAddr& addr); + virtual PRStatus Connect( + const RCNetAddr& addr, const RCInterval& timeout); + virtual PRStatus GetLocalName(RCNetAddr *addr) const; + virtual PRStatus GetPeerName(RCNetAddr *addr) const; + virtual PRStatus GetSocketOption(PRSocketOptionData *data) const; + virtual PRStatus Listen(PRIntn backlog); + virtual PRInt16 Poll(PRInt16 in_flags, PRInt16 *out_flags); + virtual PRInt32 Read(void *buf, PRSize amount); + virtual PRInt32 Recv( + void *buf, PRSize amount, PRIntn flags, + const RCInterval& timeout); + virtual PRInt32 Recvfrom( + void *buf, PRSize amount, PRIntn flags, + RCNetAddr* addr, const RCInterval& timeout); + virtual PRInt32 Send( + const void *buf, PRSize amount, PRIntn flags, + const RCInterval& timeout); + virtual PRInt32 Sendto( + const void *buf, PRSize amount, PRIntn flags, + const RCNetAddr& addr, + const RCInterval& timeout); + virtual PRStatus SetSocketOption(const PRSocketOptionData *data); + virtual PRStatus Shutdown(ShutdownHow how); + virtual PRInt32 TransmitFile( + RCIO *source, const void *headers, + PRSize hlen, RCIO::FileDisposition flags, + const RCInterval& timeout); + virtual PRInt32 Write(const void *buf, PRSize amount); + virtual PRInt32 Writev( + const PRIOVec *iov, PRSize size, + const RCInterval& timeout); + +private: + /* functions unavailable to this clients of this class */ + RCNetStreamIO(const RCNetStreamIO&); + + PRStatus Close(); + PRStatus Open(const char *name, PRIntn flags, PRIntn mode); + PRStatus FileInfo(RCFileInfo *info) const; + PRStatus Fsync(); + PRInt64 Seek(PRInt64 offset, RCIO::Whence how); + +public: + RCNetStreamIO(PRIntn protocol); +}; /* RCNetIO */ + +#endif /* defined(_RCNETIO_H) */ + +/* RCNetStreamIO.h */ + + diff --git a/nsprpub/pr/src/cplus/rcthread.cpp b/nsprpub/pr/src/cplus/rcthread.cpp new file mode 100644 index 0000000000..471e580a2c --- /dev/null +++ b/nsprpub/pr/src/cplus/rcthread.cpp @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* RCThread.cpp - C++ wrapper on NSPR */ + +#include "rcthread.h" +#include "rcinrval.h" + +#include <prmem.h> +#include <prlog.h> +#include <stdio.h> +#include <prinit.h> + +static RCPrimordialThread *primordial = NULL; + +void nas_Root(void *arg) +{ + RCThread *him = (RCThread*)arg; + while (RCThread::ex_unstarted == him->execution) { + (void)PR_Sleep(PR_INTERVAL_NO_TIMEOUT); /* wait for Start() */ + } + him->RootFunction(); /* he gets a self reference */ + if (PR_UNJOINABLE_THREAD == PR_GetThreadState(him->identity)) { + delete him; + } +} /* nas_Root */ + +RCThread::~RCThread() { } + +RCThread::RCThread(): RCBase() { } + +RCThread::RCThread(const RCThread&): RCBase() +{ + PR_NOT_REACHED("Cannot call thread copy constructor"); +} /* RCThread::RCThread */ + +RCThread::RCThread( + RCThread::Scope scope, RCThread::State join, PRUint32 stackSize): + RCBase() +{ + execution = ex_unstarted; + identity = PR_CreateThread( + PR_USER_THREAD, nas_Root, this, + PR_GetThreadPriority(PR_GetCurrentThread()), + (PRThreadScope)scope, (PRThreadState)join, stackSize); +} /* RCThread::RCThread */ + +void RCThread::operator=(const RCThread&) +{ + PR_NOT_REACHED("Cannot call thread assignment operator"); +} /* RCThread::operator= */ + + +PRStatus RCThread::Start() +{ + PRStatus rv; + /* This is an unsafe check, but not too critical */ + if (RCThread::ex_unstarted == execution) + { + execution = RCThread::ex_started; + rv = PR_Interrupt(identity); + PR_ASSERT(PR_SUCCESS == rv); + } + else + { + rv = PR_FAILURE; + PR_SetError(PR_INVALID_STATE_ERROR, 0); + } + return rv; +} /* RCThread::Start */ + +PRStatus RCThread::Join() +{ + PRStatus rv; + if (RCThread::ex_unstarted == execution) + { + rv = PR_FAILURE; + PR_SetError(PR_INVALID_STATE_ERROR, 0); + } + else { + rv = PR_JoinThread(identity); + } + if (PR_SUCCESS == rv) { + delete this; + } + return rv; +} /* RCThread::Join */ + +PRStatus RCThread::Interrupt() +{ + PRStatus rv; + if (RCThread::ex_unstarted == execution) + { + rv = PR_FAILURE; + PR_SetError(PR_INVALID_STATE_ERROR, 0); + } + else { + rv = PR_Interrupt(identity); + } + return rv; +} /* RCThread::Interrupt */ + +void RCThread::ClearInterrupt() { + PR_ClearInterrupt(); +} + +void RCThread::SetPriority(RCThread::Priority new_priority) +{ + PR_SetThreadPriority(identity, (PRThreadPriority)new_priority); +} + +PRThread *RCThread::Self() +{ + return PR_GetCurrentThread(); +} + +RCThread::Scope RCThread::GetScope() const +{ + return (RCThread::Scope)PR_GetThreadScope(identity); +} + +RCThread::State RCThread::GetState() const +{ + return (RCThread::State)PR_GetThreadState(identity); +} + +RCThread::Priority RCThread::GetPriority() const +{ + return (RCThread::Priority)PR_GetThreadPriority(identity); +} + +static void _rc_PDDestructor(RCThreadPrivateData* privateData) +{ + PR_ASSERT(NULL != privateData); + privateData->Release(); +} + +static PRThreadPrivateDTOR _tpd_dtor = (PRThreadPrivateDTOR)_rc_PDDestructor; + +PRStatus RCThread::NewPrivateIndex(PRUintn* index) +{ + return PR_NewThreadPrivateIndex(index, _tpd_dtor); +} + +PRStatus RCThread::SetPrivateData(PRUintn index) +{ + return PR_SetThreadPrivate(index, NULL); +} + +PRStatus RCThread::SetPrivateData(PRUintn index, RCThreadPrivateData* data) +{ + return PR_SetThreadPrivate(index, data); +} + +RCThreadPrivateData* RCThread::GetPrivateData(PRUintn index) +{ + return (RCThreadPrivateData*)PR_GetThreadPrivate(index); +} + +PRStatus RCThread::Sleep(const RCInterval& ticks) +{ + PRIntervalTime tmo = ticks; + return PR_Sleep(tmo); +} + +RCPrimordialThread *RCThread::WrapPrimordialThread() +{ + /* + ** This needs to take more care in insuring that the thread + ** being wrapped is really the primordial thread. This code + ** is assuming that the caller is the primordial thread, and + ** there's nothing to insure that. + */ + if (NULL == primordial) + { + /* it doesn't have to be perfect */ + RCPrimordialThread *me = new RCPrimordialThread(); + PR_ASSERT(NULL != me); + if (NULL == primordial) + { + primordial = me; + me->execution = RCThread::ex_started; + me->identity = PR_GetCurrentThread(); + } + else { + delete me; /* somebody beat us to it */ + } + } + return primordial; +} /* RCThread::WrapPrimordialThread */ + +RCPrimordialThread::RCPrimordialThread(): RCThread() { } + +RCPrimordialThread::~RCPrimordialThread() { } + +void RCPrimordialThread::RootFunction() +{ + PR_NOT_REACHED("Primordial thread calling root function"); +} /* RCPrimordialThread::RootFunction */ + +PRStatus RCPrimordialThread::Cleanup() { + return PR_Cleanup(); +} + +PRStatus RCPrimordialThread::SetVirtualProcessors(PRIntn count) +{ + PR_SetConcurrency(count); + return PR_SUCCESS; +} /* SetVirutalProcessors */ + +RCThreadPrivateData::RCThreadPrivateData() { } + +RCThreadPrivateData::RCThreadPrivateData( + const RCThreadPrivateData& him) { } + +RCThreadPrivateData::~RCThreadPrivateData() { } + +/* RCThread.c */ + diff --git a/nsprpub/pr/src/cplus/rcthread.h b/nsprpub/pr/src/cplus/rcthread.h new file mode 100644 index 0000000000..b7716d4b99 --- /dev/null +++ b/nsprpub/pr/src/cplus/rcthread.h @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* RCThread.h */ + +#if defined(_RCTHREAD_H) +#else +#define _RCTHREAD_H + +#include "rcbase.h" + +#include <prthread.h> + +class RCInterval; + +class PR_IMPLEMENT(RCThreadPrivateData) +{ +public: + RCThreadPrivateData(); + RCThreadPrivateData(const RCThreadPrivateData&); + + virtual ~RCThreadPrivateData(); + + virtual void Release() = 0; + +}; /* RCThreadPrivateData */ + +class PR_IMPLEMENT(RCThread): public RCBase +{ +public: + + typedef enum + { + local = PR_LOCAL_THREAD, global = PR_GLOBAL_THREAD + } Scope; + + typedef enum + { + joinable = PR_JOINABLE_THREAD, unjoinable = PR_UNJOINABLE_THREAD + } State; + + typedef enum + { + first = PR_PRIORITY_FIRST, + low = PR_PRIORITY_LOW, + normal = PR_PRIORITY_NORMAL, + high = PR_PRIORITY_HIGH, + urgent = PR_PRIORITY_URGENT, + last = PR_PRIORITY_LAST + } Priority; + + /* + * Create a new thread, providing scope and joinability state. + */ + RCThread(Scope scope, State state, PRUint32 stackSize=0); + + /* + * New threads are created in a suspended state. It must be 'started" + * before it begins execution in the class' defined 'RootFunction()'. + */ + virtual PRStatus Start(); + + /* + * If a thread is created joinable, then the thread's object exists + * until join is called. The thread that calls join will block until + * the target thread returns from it's root function. + */ + virtual PRStatus Join(); + + /* + * The priority of a newly created thread is the same as the creator. + * The priority may be changed either by the new thread itself, by + * the creator or any other arbitrary thread. + */ + virtual void SetPriority(Priority newPriority); + + + /* + * Interrupt another thread, causing it to stop what it + * is doing and return with a well known error code. + */ + virtual PRStatus Interrupt(); + + /* + * And in case a thread was interrupted and didn't get a chance + * to have the notification delivered, a way to cancel the pending + * status. + */ + static void ClearInterrupt(); + + /* + * Methods to discover the attributes of an existing thread. + */ + static PRThread *Self(); + Scope GetScope() const; + State GetState() const; + Priority GetPriority() const; + + /* + * Thread private data + */ + static PRStatus NewPrivateIndex(PRUintn* index); + + /* + * Getting it - if you want to modify, make a copy + */ + static RCThreadPrivateData* GetPrivateData(PRUintn index); + + /* + * Setting it to <empty> - deletes existing data + */ + static PRStatus SetPrivateData(PRUintn index); + + /* + * Setting it - runtime will make a copy, freeing old iff necessary + */ + static PRStatus SetPrivateData(PRUintn index, RCThreadPrivateData* data); + + /* + * Scheduling control + */ + static PRStatus Sleep(const RCInterval& ticks); + + friend void nas_Root(void*); + friend class RCPrimordialThread; +protected: + + /* + * The instantiator of a class must not call the destructor. The base + * implementation of Join will, and if the thread is created unjoinable, + * then the code that called the RootFunction will call the desctructor. + */ + virtual ~RCThread(); + +private: + + /* + * This is where a newly created thread begins execution. Returning + * from this function is equivalent to terminating the thread. + */ + virtual void RootFunction() = 0; + + PRThread *identity; + + /* Threads are unstarted until started - pretty startling */ + enum {ex_unstarted, ex_started} execution; + + /* There is no public default constructor or copy constructor */ + RCThread(); + RCThread(const RCThread&); + + /* And there is no assignment operator */ + void operator=(const RCThread&); + +public: + static RCPrimordialThread *WrapPrimordialThread(); + +}; + +/* +** class RCPrimordialThread +*/ +class PR_IMPLEMENT(RCPrimordialThread): public RCThread +{ +public: + /* + ** The primordial thread can (optionally) wait for all created + ** threads to terminate before allowing the process to exit. + ** Not calling Cleanup() before returning from main() will cause + ** the immediate termination of the entire process, including + ** any running threads. + */ + static PRStatus Cleanup(); + + /* + ** Only the primordial thread is allowed to adjust the number of + ** virtual processors of the runtime. It's a lame security thing. + */ + static PRStatus SetVirtualProcessors(PRIntn count=10); + + friend class RCThread; +private: + /* + ** None other than the runtime can create of destruct + ** a primordial thread. It is fabricated by the runtime + ** to wrap the thread that initiated the application. + */ + RCPrimordialThread(); + ~RCPrimordialThread(); + void RootFunction(); +}; /* RCPrimordialThread */ + +#endif /* defined(_RCTHREAD_H) */ diff --git a/nsprpub/pr/src/cplus/rctime.cpp b/nsprpub/pr/src/cplus/rctime.cpp new file mode 100644 index 0000000000..2c9823e15b --- /dev/null +++ b/nsprpub/pr/src/cplus/rctime.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Class implementation for calendar time routines (ref: prtime.h) +*/ + +#include "rctime.h" + +RCTime::~RCTime() { } + +RCTime::RCTime(PRTime time): RCBase() { + gmt = time; +} +RCTime::RCTime(const RCTime& his): RCBase() { + gmt = his.gmt; +} +RCTime::RCTime(RCTime::Current): RCBase() { + gmt = PR_Now(); +} +RCTime::RCTime(const PRExplodedTime& time): RCBase() +{ + gmt = PR_ImplodeTime(&time); +} + +void RCTime::operator=(const PRExplodedTime& time) +{ + gmt = PR_ImplodeTime(&time); +} + +RCTime RCTime::operator+(const RCTime& his) +{ + RCTime sum(gmt + his.gmt); + return sum; +} + +RCTime RCTime::operator-(const RCTime& his) +{ + RCTime difference(gmt - his.gmt); + return difference; +} + +RCTime RCTime::operator/(PRUint64 his) +{ + RCTime quotient(gmt / gmt); + return quotient; +} + +RCTime RCTime::operator*(PRUint64 his) +{ + RCTime product(gmt * his); + return product; +} + diff --git a/nsprpub/pr/src/cplus/rctime.h b/nsprpub/pr/src/cplus/rctime.h new file mode 100644 index 0000000000..3752a3d53d --- /dev/null +++ b/nsprpub/pr/src/cplus/rctime.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Class definitions for calendar time routines (ref: prtime.h) +*/ + +#if defined(_RCTIME_H) +#else +#define _RCTIME_H + +#include "rcbase.h" + +#include <prtime.h> + +/* +** Class: RCTime (ref: prtime.h) +** +** RCTimes are objects that are intended to be used to represent calendar +** times. They maintain units internally as microseconds since the defined +** epoch (midnight, January 1, 1970, GMT). Conversions to and from external +** formats (PRExplodedTime) are available. +** +** In general, NCTimes possess normal algebretic capabilities. +*/ + +class PR_IMPLEMENT(RCTime): public RCBase +{ +public: + typedef enum {now} Current; + + RCTime(); /* leaves the object unitialized */ + RCTime(Current); /* initializes to current system time */ + RCTime(const RCTime&); /* copy constructor */ + RCTime(const PRExplodedTime&); /* construction from exploded representation */ + + virtual ~RCTime(); + + /* assignment operators */ + void operator=(const RCTime&); + void operator=(const PRExplodedTime&); + + /* comparitive operators */ + PRBool operator<(const RCTime&); + PRBool operator>(const RCTime&); + PRBool operator<=(const RCTime&); + PRBool operator>=(const RCTime&); + PRBool operator==(const RCTime&); + + /* associative operators */ + RCTime operator+(const RCTime&); + RCTime operator-(const RCTime&); + RCTime& operator+=(const RCTime&); + RCTime& operator-=(const RCTime&); + + /* multiply and divide operators */ + RCTime operator/(PRUint64); + RCTime operator*(PRUint64); + RCTime& operator/=(PRUint64); + RCTime& operator*=(PRUint64); + + void Now(); /* assign current time to object */ + +private: + PRTime gmt; + +public: + + RCTime(PRTime); /* construct from raw PRTime */ + void operator=(PRTime); /* assign from raw PRTime */ + operator PRTime() const; /* extract internal representation */ +}; /* RCTime */ + +inline RCTime::RCTime(): RCBase() { } + +inline void RCTime::Now() { + gmt = PR_Now(); +} +inline RCTime::operator PRTime() const { + return gmt; +} + +inline void RCTime::operator=(PRTime his) { + gmt = his; +} +inline void RCTime::operator=(const RCTime& his) { + gmt = his.gmt; +} + +inline PRBool RCTime::operator<(const RCTime& his) +{ + return (gmt < his.gmt) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCTime::operator>(const RCTime& his) +{ + return (gmt > his.gmt) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCTime::operator<=(const RCTime& his) +{ + return (gmt <= his.gmt) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCTime::operator>=(const RCTime& his) +{ + return (gmt >= his.gmt) ? PR_TRUE : PR_FALSE; +} +inline PRBool RCTime::operator==(const RCTime& his) +{ + return (gmt == his.gmt) ? PR_TRUE : PR_FALSE; +} + +inline RCTime& RCTime::operator+=(const RCTime& his) +{ + gmt += his.gmt; + return *this; +} +inline RCTime& RCTime::operator-=(const RCTime& his) +{ + gmt -= his.gmt; + return *this; +} +inline RCTime& RCTime::operator/=(PRUint64 his) +{ + gmt /= his; + return *this; +} +inline RCTime& RCTime::operator*=(PRUint64 his) +{ + gmt *= his; + return *this; +} + +#endif /* defined(_RCTIME_H) */ + +/* RCTime.h */ diff --git a/nsprpub/pr/src/cplus/tests/Makefile.in b/nsprpub/pr/src/cplus/tests/Makefile.in new file mode 100644 index 0000000000..df48276569 --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/Makefile.in @@ -0,0 +1,199 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +ifeq ($(OS_TARGET), WIN16) +OS_CFLAGS = $(OS_EXE_CFLAGS) +endif + +CXXSRCS = \ + ranfile.cpp \ + thread.cpp \ + interval.cpp \ + time.cpp \ + fileio.cpp \ + switch.cpp \ + tpd.cpp \ + $(NULL) + +OBJS = $(addprefix $(OBJDIR)/,$(CXXSRCS:.cpp=.$(OBJ_SUFFIX))) + +ifeq (,$(filter-out WINNT OS2,$(OS_ARCH))) +PROG_SUFFIX = .exe +else +PROG_SUFFIX = +endif + +PROGS = $(addprefix $(OBJDIR)/, $(CXXSRCS:.cpp=$(PROG_SUFFIX))) + +TARGETS = $(PROGS) $(OBJS) + +INCLUDES = -I.. -I$(dist_includedir) + +# Setting the variables LDOPTS and LIBPR. We first initialize +# them to the default values, then adjust them for some platforms. +LDOPTS = -L$(dist_libdir) +LIBPR = -lnspr$(MOD_MAJOR_VERSION) +LIBPL = -lplc$(MOD_MAJOR_VERSION) + +# Solaris +ifeq ($(OS_ARCH), SunOS) + ifdef NS_USE_GCC + LDOPTS += -Xlinker -R -Xlinker $(PWD)/$(dist_libdir) + else + LDOPTS += -R $(PWD)/$(dist_libdir) + endif + +# SunOS 5.5 needs to link with -lpthread, even though we already +# linked with this system library when we built libnspr.so. + ifeq ($(OS_RELEASE), 5.5) + ifdef USE_PTHREADS + EXTRA_LIBS = -lpthread + endif + endif +endif # SunOS + +ifeq ($(OS_ARCH), WINNT) +ifeq ($(OS_TARGET), WIN16) + LIBPR = $(dist_libdir)/nspr$(MOD_MAJOR_VERSION).lib + LIBPL = $(dist_libdir)/plc$(MOD_MAJOR_VERSION).lib +else + LDOPTS = -NOLOGO -DEBUG -INCREMENTAL:NO + LIBPR = $(dist_libdir)/libnspr$(MOD_MAJOR_VERSION).$(LIB_SUFFIX) + LIBPL = $(dist_libdir)/libplc$(MOD_MAJOR_VERSION).$(LIB_SUFFIX) +endif +endif + +ifeq ($(OS_ARCH),OS2) +LDOPTS += -Zomf -Zlinker /PM:VIO -lstdcpp +endif + +ifneq ($(OS_ARCH), WINNT) +PWD = $(shell pwd) +endif + +ifeq ($(OS_ARCH), HP-UX) + LDOPTS += -Wl,+s,+b,$(PWD)/$(dist_libdir) +endif + +# AIX +ifeq ($(OS_ARCH),AIX) + LDOPTS += -blibpath:$(PWD)/$(dist_libdir):/usr/lib:/lib + ifeq ($(OS_ARCH)$(OS_RELEASE),AIX4.1) + LIBPR = -lnspr$(MOD_MAJOR_VERSION)_shr + LIBPLC = -lplc$(MOD_MAJOR_VERSION)_shr + else + LDOPTS += -brtl + EXTRA_LIBS = -ldl + endif +endif + +ifeq ($(OS_ARCH), Linux) + ifeq ($(OS_RELEASE), 1.2) + EXTRA_LIBS = -ldl + else + LDOPTS += -Xlinker -rpath $(PWD)/$(dist_libdir) + ifeq ($(USE_PTHREADS),1) + EXTRA_LIBS = -lpthread + endif + endif +endif + +ifeq ($(OS_ARCH), SCO_SV) +# SCO Unix needs to link against -lsocket again even though we +# already linked with these system libraries when we built libnspr.so. +EXTRA_LIBS = -lsocket +# This hardcodes in the executable programs the directory to find +# libnspr.so etc. at program startup. Equivalent to the -R or -rpath +# option for ld on other platforms. +export LD_RUN_PATH = $(PWD)/$(dist_libdir) +endif + +ifeq ($(OS_ARCH), UNIXWARE) +export LD_RUN_PATH = $(PWD)/$(dist_libdir) +endif + +##################################################### +# +# The rules +# +##################################################### + +include $(topsrcdir)/config/rules.mk + +AIX_PRE_4_2 = 0 +ifeq ($(OS_ARCH),AIX) +ifneq ($(OS_RELEASE),4.2) +ifneq ($(USE_PTHREADS), 1) +#AIX_PRE_4_2 = 1 +endif +endif +endif + +ifeq ($(AIX_PRE_4_2),1) + +# AIX releases prior to 4.2 need a special two-step linking hack +# in order to both override the system select() and be able to +# get at the original system select(). +# +# We use a pattern rule in ns/nspr20/config/rules.mk to generate +# the .$(OBJ_SUFFIX) file from the .c source file, then do the +# two-step linking hack below. + +$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX) + @$(MAKE_OBJDIR) + rm -f $@ $(AIX_TMP) + $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(dist_libdir)/libnspr$(MOD_MAJOR_VERSION).a + $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP) + rm -f $(AIX_TMP) + +else + +# All platforms that are not AIX pre-4.2. + +$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX) + @$(MAKE_OBJDIR) +ifeq ($(OS_ARCH), WINNT) +ifeq ($(OS_TARGET),WIN16) + echo system windows >w16link + echo option map >>w16link + echo option stack=10K >>w16link + echo option heapsize=32K >>w16link + echo debug $(DEBUGTYPE) all >>w16link + echo name $@ >>w16link + echo file >>w16link + echo $< >>w16link + echo library >>w16link + echo $(LIBPR), >>w16link + echo $(LIBPL), >>w16link + echo winsock.lib >>w16link + wlink @w16link. +else + link $(LDOPTS) $< $(LIBPR) $(LIBPL) ws2_32.lib -out:$@ +endif +else +ifeq ($(OS_ARCH),OS2) + $(LINK) $(LDOPTS) $< $(LIBGC) $(LIBPLC) $(LIBPR) $(OS_LIBS) $(EXTRA_LIBS) -o $@ +else + $(CCC) $(XCFLAGS) $< $(LDOPTS) $(LIBPR) $(LIBPL) $(EXTRA_LIBS) -o $@ +endif +endif +endif + +export:: $(TARGETS) +clean:: + rm -f $(TARGETS) + diff --git a/nsprpub/pr/src/cplus/tests/fileio.cpp b/nsprpub/pr/src/cplus/tests/fileio.cpp new file mode 100644 index 0000000000..fdffe99972 --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/fileio.cpp @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* fileio.cpp - a test program */ + +#include "rcfileio.h" + +#include <prlog.h> +#include <prprf.h> + +#define DEFAULT_ITERATIONS 100 + +PRIntn main(PRIntn argc, char **argv) +{ + PRStatus rv; + RCFileIO fd; + RCFileInfo info; + rv = fd.Open("filio.dat", PR_CREATE_FILE, 0666); + PR_ASSERT(PR_SUCCESS == rv); + rv = fd.FileInfo(&info); + PR_ASSERT(PR_SUCCESS == rv); + rv = fd.Delete("filio.dat"); + PR_ASSERT(PR_SUCCESS == rv); + fd.Close(); + PR_ASSERT(PR_SUCCESS == rv); + + return 0; +} /* main */ + +/* interval.cpp */ + diff --git a/nsprpub/pr/src/cplus/tests/interval.cpp b/nsprpub/pr/src/cplus/tests/interval.cpp new file mode 100644 index 0000000000..8acf506256 --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/interval.cpp @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* interval.cpp - a test program */ + +#include "rclock.h" +#include "rcthread.h" +#include "rcinrval.h" +#include "rccv.h" + +#include <prio.h> +#include <prlog.h> +#include <prprf.h> + +#define DEFAULT_ITERATIONS 100 + +PRIntn main(PRIntn argc, char **argv) +{ + RCLock ml; + PRStatus rv; + RCCondition cv(&ml); + + RCInterval now, timeout, epoch, elapsed; + PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput); + PRIntn msecs, seconds, loops, iterations = DEFAULT_ITERATIONS; + + /* slow, agonizing waits */ + for (seconds = 0; seconds < 10; ++seconds) + { + timeout = RCInterval::FromSeconds(seconds); + cv.SetTimeout(timeout); + { + RCEnter lock(&ml); + + epoch.SetToNow(); + + rv = cv.Wait(); + PR_ASSERT(PR_SUCCESS == rv); + + now = RCInterval(RCInterval::now); + elapsed = now - epoch; + } + + PR_fprintf( + output, "Waiting %u seconds took %s%u milliseconds\n", + seconds, ((elapsed < timeout)? "**" : ""), + elapsed.ToMilliseconds()); + } + + /* more slow, agonizing sleeps */ + for (seconds = 0; seconds < 10; ++seconds) + { + timeout = RCInterval::FromSeconds(seconds); + { + epoch.SetToNow(); + + rv = RCThread::Sleep(timeout); + PR_ASSERT(PR_SUCCESS == rv); + + now = RCInterval(RCInterval::now); + elapsed = now - epoch; + } + + PR_fprintf( + output, "Sleeping %u seconds took %s%u milliseconds\n", + seconds, ((elapsed < timeout)? "**" : ""), + elapsed.ToMilliseconds()); + } + + /* fast, spritely little devils */ + for (msecs = 10; msecs < 100; msecs += 10) + { + timeout = RCInterval::FromMilliseconds(msecs); + cv.SetTimeout(timeout); + { + RCEnter lock(&ml); + + epoch.SetToNow(); + + for (loops = 0; loops < iterations; ++loops) + { + rv = cv.Wait(); + PR_ASSERT(PR_SUCCESS == rv); + } + + now = RCInterval(RCInterval::now); + elapsed = now - epoch; + } + elapsed /= iterations; + + PR_fprintf( + output, "Waiting %u msecs took %s%u milliseconds average\n", + msecs, ((elapsed < timeout)? "**" : ""), elapsed.ToMilliseconds()); + } + return 0; +} /* main */ + +/* interval.cpp */ + diff --git a/nsprpub/pr/src/cplus/tests/ranfile.cpp b/nsprpub/pr/src/cplus/tests/ranfile.cpp new file mode 100644 index 0000000000..0fb4c5d23a --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/ranfile.cpp @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Contact: AOF<mailto:freier@netscape.com> +** +** Name: ranfile.c +** +** Description: Test to hammer on various components of NSPR +** Modification History: +** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include <plgetopt.h> +#include <prprf.h> +#include <prio.h> + +#include "rccv.h" +#include "rcthread.h" +#include "rcfileio.h" +#include "rclock.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +static PRFileDesc *output; +static PRIntn debug_mode = 0; +static PRIntn failed_already = 0; + +class HammerData +{ +public: + typedef enum { + sg_go, sg_stop, sg_done + } Action; + typedef enum { + sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek + } Problem; + + virtual ~HammerData(); + HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip); + virtual PRUint32 Random(); + + Action action; + Problem problem; + PRUint32 writes; + RCInterval timein; + friend class Hammer; +private: + RCLock *ml; + RCCondition *cv; + PRUint32 limit; + + PRFloat64 seed; +}; /* HammerData */ + +class Hammer: public HammerData, public RCThread +{ +public: + virtual ~Hammer(); + Hammer(RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip); + +private: + void RootFunction(); + +}; + +static PRInt32 pageSize = 1024; +static const char* baseName = "./"; +static const char *programName = "Random File"; + +/*********************************************************************** +** PRIVATE FUNCTION: Random +** DESCRIPTION: +** Generate a pseudo-random number +** INPUTS: None +** OUTPUTS: None +** RETURN: A pseudo-random unsigned number, 32-bits wide +** SIDE EFFECTS: +** Updates random seed (a static) +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: +** Uses the current interval timer value, promoted to a 64 bit +** float as a multiplier for a static residue (which begins +** as an uninitialized variable). The result is bits [16..48) +** of the product. Seed is then updated with the return value +** promoted to a float-64. +***********************************************************************/ +PRUint32 HammerData::Random() +{ + PRUint32 rv; + PRUint64 shift; + RCInterval now = RCInterval(RCInterval::now); + PRFloat64 random = seed * (PRFloat64)((PRIntervalTime)now); + LL_USHR(shift, *((PRUint64*)&random), 16); + LL_L2UI(rv, shift); + seed = (PRFloat64)rv; + return rv; +} /* HammerData::Random */ + +Hammer::~Hammer() { } + +Hammer::Hammer( + RCThread::Scope scope, RCLock* lock, RCCondition *cond, PRUint32 clip): + HammerData(lock, cond, clip), RCThread(scope, RCThread::joinable, 0) { } + +HammerData::~HammerData() { } + +HammerData::HammerData(RCLock* lock, RCCondition *cond, PRUint32 clip) +{ + ml = lock; + cv = cond; + writes = 0; + limit = clip; + seed = 0x58a9382; + action = HammerData::sg_go; + problem = HammerData::sg_okay; + timein = RCInterval(RCInterval::now); +} /* HammerData::HammerData */ + + +/*********************************************************************** +** PRIVATE FUNCTION: Hammer::RootFunction +** DESCRIPTION: +** Hammer on the file I/O system +** INPUTS: A pointer to the thread's private data +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** Creates, accesses and deletes a file +** RESTRICTIONS: +** (Currently) must have file create permission in "/usr/tmp". +** MEMORY: NA +** ALGORITHM: +** This function is a root of a thread +** 1) Creates a (hopefully) unique file in /usr/tmp/ +** 2) Writes a zero to a random number of sequential pages +** 3) Closes the file +** 4) Reopens the file +** 5) Seeks to a random page within the file +** 6) Writes a one byte on that page +** 7) Repeat steps [5..6] for each page in the file +** 8) Close and delete the file +** 9) Repeat steps [1..8] until told to stop +** 10) Notify complete and return +***********************************************************************/ +void Hammer::RootFunction() +{ + PRUint32 index; + RCFileIO file; + char filename[30]; + const char zero = 0; + PRStatus rv = PR_SUCCESS; + + limit = (Random() % limit) + 1; + + (void)sprintf(filename, "%ssg%04p.dat", baseName, this); + + if (debug_mode) { + PR_fprintf(output, "Starting work on %s\n", filename); + } + + while (PR_TRUE) + { + PRUint64 bytes; + PRUint32 minor = (Random() % limit) + 1; + PRUint32 random = (Random() % limit) + 1; + PRUint32 pages = (Random() % limit) + 10; + while (minor-- > 0) + { + problem = sg_okay; + if (action != sg_go) { + goto finished; + } + problem = sg_open; + rv = file.Open(filename, PR_RDWR|PR_CREATE_FILE, 0666); + if (PR_FAILURE == rv) { + goto finished; + } + for (index = 0; index < pages; index++) + { + problem = sg_okay; + if (action != sg_go) { + goto close; + } + problem = sg_seek; + bytes = file.Seek(pageSize * index, RCFileIO::set); + if (bytes != pageSize * index) { + goto close; + } + problem = sg_write; + bytes = file.Write(&zero, sizeof(zero)); + if (bytes <= 0) { + goto close; + } + writes += 1; + } + problem = sg_close; + rv = file.Close(); + if (rv != PR_SUCCESS) { + goto purge; + } + + problem = sg_okay; + if (action != sg_go) { + goto purge; + } + + problem = sg_open; + rv = file.Open(filename, PR_RDWR, 0666); + if (PR_FAILURE == rv) { + goto finished; + } + for (index = 0; index < pages; index++) + { + problem = sg_okay; + if (action != sg_go) { + goto close; + } + problem = sg_seek; + bytes = file.Seek(pageSize * index, RCFileIO::set); + if (bytes != pageSize * index) { + goto close; + } + problem = sg_write; + bytes = file.Write(&zero, sizeof(zero)); + if (bytes <= 0) { + goto close; + } + writes += 1; + random = (random + 511) % pages; + } + problem = sg_close; + rv = file.Close(); + if (rv != PR_SUCCESS) { + goto purge; + } + problem = sg_delete; + rv = file.Delete(filename); + if (rv != PR_SUCCESS) { + goto finished; + } + } + } + +close: + (void)file.Close(); +purge: + (void)file.Delete(filename); +finished: + RCEnter scope(ml); + action = HammerData::sg_done; + cv->Notify(); + + if (debug_mode) { + PR_fprintf(output, "Ending work on %s\n", filename); + } + + return; +} /* Hammer::RootFunction */ + +static Hammer* hammer[100]; +/*********************************************************************** +** PRIVATE FUNCTION: main +** DESCRIPTION: +** Hammer on the file I/O system +** INPUTS: The usual argc and argv +** argv[0] - program name (not used) +** argv[1] - the number of virtual_procs to execute the major loop +** argv[2] - the number of threads to toss into the batch +** argv[3] - the clipping number applied to randoms +** default values: max_virtual_procs = 2, threads = 10, limit = 57 +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** Creates, accesses and deletes lots of files +** RESTRICTIONS: +** (Currently) must have file create permission in "/usr/tmp". +** MEMORY: NA +** ALGORITHM: +** 1) Fork a "Thread()" +** 2) Wait for 'interleave' seconds +** 3) For [0..'threads') repeat [1..2] +** 4) Mark all objects to stop +** 5) Collect the threads, accumulating the results +** 6) For [0..'max_virtual_procs') repeat [1..5] +** 7) Print accumulated results and exit +** +** Characteristic output (from IRIX) +** Random File: Using max_virtual_procs = 2, threads = 10, limit = 57 +** Random File: [min [avg] max] writes/sec average +***********************************************************************/ +PRIntn main (PRIntn argc, char *argv[]) +{ + RCLock ml; + PLOptStatus os; + RCCondition cv(&ml); + PRUint32 writesMax = 0, durationTot = 0; + RCThread::Scope thread_scope = RCThread::local; + PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0; + PRIntn active, poll, limit = 0, max_virtual_procs = 0, threads = 0, virtual_procs; + RCInterval interleave(RCInterval::FromMilliseconds(10000)), duration(0); + + const char *where[] = {"okay", "open", "close", "delete", "write", "seek"}; + + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: + baseName = opt->value; + break; + case 'G': /* global threads */ + thread_scope = RCThread::global; + break; + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'l': /* limiting number */ + limit = atoi(opt->value); + break; + case 't': /* number of threads */ + threads = atoi(opt->value); + break; + case 'i': /* iteration counter */ + max_virtual_procs = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + output = PR_GetSpecialFD(PR_StandardOutput); + + /* main test */ + + cv.SetTimeout(interleave); + + if (max_virtual_procs == 0) { + max_virtual_procs = 2; + } + if (limit == 0) { + limit = 57; + } + if (threads == 0) { + threads = 10; + } + + if (debug_mode) PR_fprintf(output, + "%s: Using %d virtual processors, %d threads, limit = %d and %s threads\n", + programName, max_virtual_procs, threads, limit, + (thread_scope == RCThread::local) ? "LOCAL" : "GLOBAL"); + + for (virtual_procs = 0; virtual_procs < max_virtual_procs; ++virtual_procs) + { + if (debug_mode) + PR_fprintf(output, + "%s: Setting number of virtual processors to %d\n", + programName, virtual_procs + 1); + RCPrimordialThread::SetVirtualProcessors(virtual_procs + 1); + for (active = 0; active < threads; active++) + { + hammer[active] = new Hammer(thread_scope, &ml, &cv, limit); + hammer[active]->Start(); /* then make it roll */ + RCThread::Sleep(interleave); /* start them slowly */ + } + + /* + * The last thread started has had the opportunity to run for + * 'interleave' seconds. Now gather them all back in. + */ + { + RCEnter scope(&ml); + for (poll = 0; poll < threads; poll++) + { + if (hammer[poll]->action == HammerData::sg_go) { /* don't overwrite done */ + hammer[poll]->action = HammerData::sg_stop; /* ask him to stop */ + } + } + } + + while (active > 0) + { + for (poll = 0; poll < threads; poll++) + { + ml.Acquire(); + while (hammer[poll]->action < HammerData::sg_done) { + cv.Wait(); + } + ml.Release(); + + if (hammer[poll]->problem == HammerData::sg_okay) + { + duration = RCInterval(RCInterval::now) - hammer[poll]->timein; + writes = hammer[poll]->writes * 1000 / duration; + if (writes < writesMin) { + writesMin = writes; + } + if (writes > writesMax) { + writesMax = writes; + } + writesTot += hammer[poll]->writes; + durationTot += duration; + } + else + { + if (debug_mode) PR_fprintf(output, + "%s: test failed %s after %ld seconds\n", + programName, where[hammer[poll]->problem], duration); + else { + failed_already=1; + } + } + active -= 1; /* this is another one down */ + (void)hammer[poll]->Join(); + hammer[poll] = NULL; + } + } + if (debug_mode) PR_fprintf(output, + "%s: [%ld [%ld] %ld] writes/sec average\n", + programName, writesMin, + writesTot * 1000 / durationTot, writesMax); + } + + failed_already |= (PR_FAILURE == RCPrimordialThread::Cleanup()); + PR_fprintf(output, "%s\n", (failed_already) ? "FAIL\n" : "PASS\n"); + return failed_already; +} /* main */ diff --git a/nsprpub/pr/src/cplus/tests/switch.cpp b/nsprpub/pr/src/cplus/tests/switch.cpp new file mode 100644 index 0000000000..00de8e3064 --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/switch.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: switch.cpp +** Description: trying to time context switches +*/ + +#include "rccv.h" +#include "rcinrval.h" +#include "rclock.h" +#include "rcthread.h" + +#include <prio.h> +#include <prlog.h> +#include <prprf.h> +#include <plerror.h> +#include <plgetopt.h> + +#include <stdlib.h> + +#define INNER_LOOPS 100 +#define DEFAULT_LOOPS 100 +#define DEFAULT_THREADS 10 + +static PRFileDesc *debug_out = NULL; +static PRBool debug_mode = PR_FALSE, verbosity = PR_FALSE, failed = PR_FALSE; + +class Home: public RCCondition +{ +public: + virtual ~Home(); + Home(Home *link, RCLock* ml); + +public: + Home *next; + RCLock* ml; + PRBool twiddle; +}; /* Home */ + +Home::~Home() { } + +Home::Home(Home *link, RCLock* lock): RCCondition(lock) +{ + ml = lock; + next = link; + twiddle = PR_FALSE; +} /* Home::Home */ + +class Shared: public Home, public RCThread +{ +public: + Shared(RCThread::Scope scope, Home* link, RCLock* ml); + +private: + ~Shared(); + void RootFunction(); +}; /* Shared */ + +Shared::Shared(RCThread::Scope scope, Home* link, RCLock* lock): + Home(link, lock), RCThread(scope, RCThread::joinable) { } + +Shared::~Shared() { } + +void Shared::RootFunction() +{ + PRStatus status = PR_SUCCESS; + while (PR_SUCCESS == status) + { + RCEnter entry(ml); + while (twiddle && (PR_SUCCESS == status)) { + status = Wait(); + } + if (verbosity) { + PR_fprintf(debug_out, "+"); + } + twiddle = PR_TRUE; + next->twiddle = PR_FALSE; + next->Notify(); + } +} /* Shared::RootFunction */ + +static void Help(void) +{ + debug_out = PR_STDOUT; + + PR_fprintf( + debug_out, "Usage: >./switch [-d] [-c n] [-t n] [-T n] [-G]\n"); + PR_fprintf( + debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS); + PR_fprintf( + debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS); + PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n"); + PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n"); + PR_fprintf(debug_out, "-G n\tglobal threads only (default: FALSE)\n"); + PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n"); +} /* Help */ + +PRIntn main(PRIntn argc, char **argv) +{ + PLOptStatus os; + PRStatus status; + PRBool help = PR_FALSE; + PRUintn concurrency = 1; + RCThread::Scope thread_scope = RCThread::local; + PRUintn thread_count, inner_count, loop_count, average; + PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'v': /* verbose mode */ + verbosity = PR_TRUE; + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'c': /* loop counter */ + loop_limit = atoi(opt->value); + break; + case 't': /* thread limit */ + thread_limit = atoi(opt->value); + break; + case 'C': /* Concurrency limit */ + concurrency = atoi(opt->value); + break; + case 'G': /* global threads only */ + thread_scope = RCThread::global; + break; + case 'h': /* help message */ + Help(); + help = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (help) { + return -1; + } + + if (PR_TRUE == debug_mode) + { + debug_out = PR_STDOUT; + PR_fprintf(debug_out, "Test parameters\n"); + PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit); + PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit); + PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency); + PR_fprintf( + debug_out, "\tThread type: %s\n", + (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); + } + + /* + ** The interesting part starts here + */ + RCLock lock; + Shared* shared; + Home home(NULL, &lock); + Home* link = &home; + RCInterval timein, timeout = 0; + + /* Build up the string of objects */ + for (thread_count = 1; thread_count <= thread_limit; ++thread_count) + { + shared = new Shared(thread_scope, link, &lock); + shared->Start(); /* make it run */ + link = (Home*)shared; + } + + /* Pass the message around the horn a few times */ + for (loop_count = 1; loop_count <= loop_limit; ++loop_count) + { + timein.SetToNow(); + for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count) + { + RCEnter entry(&lock); + home.twiddle = PR_TRUE; + shared->twiddle = PR_FALSE; + shared->Notify(); + while (home.twiddle) + { + failed = (PR_FAILURE == home.Wait()) ? PR_TRUE : PR_FALSE; + } + } + timeout += (RCInterval(RCInterval::now) - timein); + } + + /* Figure out how well we did */ + if (debug_mode) + { + average = timeout.ToMicroseconds() + / (INNER_LOOPS * loop_limit * thread_count); + PR_fprintf( + debug_out, "Average switch times %d usecs for %d threads\n", + average, thread_limit); + } + + /* Start reclamation process */ + link = shared; + for (thread_count = 1; thread_count <= thread_limit; ++thread_count) + { + if (&home == link) { + break; + } + status = ((Shared*)link)->Interrupt(); + if (PR_SUCCESS != status) + { + failed = PR_TRUE; + if (debug_mode) { + PL_FPrintError(debug_out, "Failed to interrupt"); + } + } + link = link->next; + } + + for (thread_count = 1; thread_count <= thread_limit; ++thread_count) + { + link = shared->next; + status = shared->Join(); + if (PR_SUCCESS != status) + { + failed = PR_TRUE; + if (debug_mode) { + PL_FPrintError(debug_out, "Failed to join"); + } + } + if (&home == link) { + break; + } + shared = (Shared*)link; + } + + PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n")); + + failed |= (PR_SUCCESS == RCPrimordialThread::Cleanup()); + + return ((failed) ? 1 : 0); +} /* main */ + +/* switch.c */ diff --git a/nsprpub/pr/src/cplus/tests/thread.cpp b/nsprpub/pr/src/cplus/tests/thread.cpp new file mode 100644 index 0000000000..ff01402d8d --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/thread.cpp @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* thread.cpp - a test program */ + +#include "rcthread.h" + +#include <prlog.h> + +#include <stdio.h> + +class TestThread: public RCThread +{ +public: + TestThread(RCThread::State state, PRIntn count); + + virtual void RootFunction(); + +protected: + virtual ~TestThread(); + +private: + PRUint32 mydata; +}; + +TestThread::~TestThread() { } + +TestThread::TestThread(RCThread::State state, PRIntn count): + RCThread(RCThread::global, state, 0) { + mydata = count; +} + +void TestThread::RootFunction() +{ + SetPriority(RCThread::high); + printf("TestThread::RootFunction %d did it\n", mydata); +} /* TestThread::RootFunction */ + +class Foo1 +{ +public: + Foo1(); + virtual ~Foo1(); + + TestThread *thread; + PRIntn data; +}; + +Foo1::Foo1() +{ + data = 0xafaf; + thread = new TestThread(RCThread::joinable, 0xafaf); + thread->Start(); +} + +Foo1::~Foo1() +{ + PRStatus rv = thread->Join(); + PR_ASSERT(PR_SUCCESS == rv); +} /* Foo1::~Foo1 */ + +PRIntn main(PRIntn argc, char **agrv) +{ + PRStatus status; + PRIntn count = 100; + RCThread *thread[10]; + while (--count > 0) + { + TestThread *thread = new TestThread(RCThread::joinable, count); + status = thread->Start(); /* have to remember to start it */ + PR_ASSERT(PR_SUCCESS == status); + status = thread->Join(); /* this should work */ + PR_ASSERT(PR_SUCCESS == status); + } + while (++count < 100) + { + TestThread *thread = new TestThread(RCThread::unjoinable, count); + status = thread->Start(); /* have to remember to start it */ + PR_ASSERT(PR_SUCCESS == status); + } + + { + Foo1 *foo1 = new Foo1(); + PR_ASSERT(NULL != foo1); + delete foo1; + } + + { + for (count = 0; count < 10; ++count) + { + thread[count] = new TestThread( RCThread::joinable, count); + status = thread[count]->Start(); /* have to remember to start it */ + PR_ASSERT(PR_SUCCESS == status); + } + for (count = 0; count < 10; ++count) + { + PRStatus rv = thread[count]->Join(); + PR_ASSERT(PR_SUCCESS == rv); + } + } + + (void)RCPrimordialThread::Cleanup(); + + return 0; +} /* main */ + +/* thread.cpp */ + diff --git a/nsprpub/pr/src/cplus/tests/time.cpp b/nsprpub/pr/src/cplus/tests/time.cpp new file mode 100644 index 0000000000..ad27caaacc --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/time.cpp @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* time.cpp - a test program */ + +#include "rctime.h" + +#include <prlog.h> +#include <prprf.h> + +#define DEFAULT_ITERATIONS 100 + +PRIntn main(PRIntn argc, char **argv) +{ + RCTime unitialized; + RCTime now(PR_Now()); + RCTime current(RCTime::now); + PRTime time = current; + + unitialized = now; + now.Now(); + + return 0; +} /* main */ + +/* time.cpp */ + diff --git a/nsprpub/pr/src/cplus/tests/tpd.cpp b/nsprpub/pr/src/cplus/tests/tpd.cpp new file mode 100644 index 0000000000..2a18c9cb11 --- /dev/null +++ b/nsprpub/pr/src/cplus/tests/tpd.cpp @@ -0,0 +1,353 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: tpd.cpp +** Description: Exercising the thread private data bailywick. +*/ + +#include "prlog.h" +#include "prprf.h" +#include "rcthread.h" + +#include <string.h> + +#include "plgetopt.h" + +/* +** class MyThread +*/ +class MyThread: public RCThread +{ +public: + MyThread(); + +private: + ~MyThread(); + void RootFunction(); +}; /* MyThread */ + +/* +** class MyPrivateData +*/ +class MyPrivateData: public RCThreadPrivateData +{ +public: + virtual ~MyPrivateData(); + + MyPrivateData(); + MyPrivateData(char*); + MyPrivateData(const MyPrivateData&); + + void Release(); + +private: + char *string; +}; /* MyPrivateData */ + +static PRUintn key[128]; +static PRIntn debug = 0; +static PRBool failed = PR_FALSE; +static PRBool should = PR_TRUE; +static PRBool did = PR_TRUE; +static PRFileDesc *fout = NULL; + +static void PrintProgress(PRIntn line) +{ + failed = failed || (should && !did); + failed = failed || (!should && did); + if (debug > 0) + { + PR_fprintf( + fout, "@ line %d destructor should %shave been called and was%s\n", + line, ((should) ? "" : "NOT "), ((did) ? "" : " NOT")); + } +} /* PrintProgress */ + +static void MyAssert(const char *expr, const char *file, PRIntn line) +{ + if (debug > 0) { + (void)PR_fprintf(fout, "'%s' in file: %s: %d\n", expr, file, line); + } +} /* MyAssert */ + +#define MY_ASSERT(_expr) \ + ((_expr)?((void)0):MyAssert(# _expr,__FILE__,__LINE__)) + +int main(PRIntn argc, char *argv[]) +{ + PRStatus rv; + PRUintn keys; + MyThread *thread; + const RCThreadPrivateData *pd; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + RCThread *primordial = RCThread::WrapPrimordialThread(); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + fout = PR_STDOUT; + + MyPrivateData extension = MyPrivateData("EXTENSION"); + MyPrivateData key_string[] = { + "Key #0", "Key #1", "Key #2", "Key #3", + "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8" + }; + + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = RCThread::NewPrivateIndex(&key[keys]); + key[keys + 4] = key[keys] + 4; + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + /* the first four should be bu null, the last four undefined and null */ + did = should = PR_FALSE; + for (keys = 0; keys < 8; ++keys) + { + pd = RCThread::GetPrivateData(key[keys]); + MY_ASSERT(NULL == pd); + } + PrintProgress(__LINE__); + + /* initially set private data for new keys */ + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = RCThread::SetPrivateData(key[keys], &key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + /* re-assign the private data, albeit the same content */ + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + pd = RCThread::GetPrivateData(key[keys]); + PR_ASSERT(NULL != pd); + rv = RCThread::SetPrivateData(key[keys], &key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + /* set private to <empty> */ + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = RCThread::SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + /* should all be null now */ + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + pd = RCThread::GetPrivateData(key[keys]); + PR_ASSERT(NULL == pd); + } + PrintProgress(__LINE__); + + /* allocate another batch of keys and assign data to them */ + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = RCThread::NewPrivateIndex(&key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + rv = RCThread::SetPrivateData(key[keys], &extension); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + /* set all the extended slots to <empty> */ + did = PR_FALSE; should = PR_TRUE; + for (keys = 8; keys < 127; ++keys) + { + rv = RCThread::SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + /* set all the extended slots to <empty> again (noop) */ + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = RCThread::SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + + if (debug) { + PR_fprintf(fout, "Creating thread\n"); + } + thread = new MyThread(); + if (debug) { + PR_fprintf(fout, "Starting thread\n"); + } + thread->Start(); + if (debug) { + PR_fprintf(fout, "Joining thread\n"); + } + (void)thread->Join(); + if (debug) { + PR_fprintf(fout, "Joined thread\n"); + } + + failed |= (PR_FAILURE == RCPrimordialThread::Cleanup()); + + (void)PR_fprintf( + fout, "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED")); + + return (failed) ? 1 : 0; + +} /* main */ + +/* +** class MyPrivateData +*/ +MyPrivateData::~MyPrivateData() +{ + PR_fprintf( + fout, "MyPrivateData::~MyPrivateData[%s]\n", + (NULL != string) ? string : "NULL"); +} /* MyPrivateData::~MyPrivateData */ + +MyPrivateData::MyPrivateData(): RCThreadPrivateData() +{ + PR_fprintf(fout, "MyPrivateData::MyPrivateData()\n"); + string = NULL; +} /* MyPrivateData::MyPrivateData */ + +MyPrivateData::MyPrivateData(char* data): RCThreadPrivateData() +{ + PR_fprintf(fout, "MyPrivateData::MyPrivateData(char* data)\n"); + string = data; +} /* MyPrivateData:: MyPrivateData */ + +MyPrivateData::MyPrivateData(const MyPrivateData& him): RCThreadPrivateData(him) +{ + PR_fprintf(fout, "MyPrivateData::MyPrivateData(const MyPrivateData& him)\n"); + string = him.string; +} /* MyPrivateData:: MyPrivateData */ + +void MyPrivateData::Release() +{ + if (should) { + did = PR_TRUE; + } + else { + failed = PR_TRUE; + } +} /* MyPrivateData::operator= */ + +/* +** class MyThread +*/ +MyThread::~MyThread() { } +MyThread::MyThread(): RCThread(RCThread::global, RCThread::joinable) { } + + +void MyThread::RootFunction() +{ + PRStatus rv; + PRUintn keys; + const RCThreadPrivateData *pd; + + MyPrivateData extension = MyPrivateData("EXTENSION"); + MyPrivateData key_string[] = { + "Key #0", "Key #1", "Key #2", "Key #3", + "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8" + }; + + did = should = PR_FALSE; + for (keys = 0; keys < 8; ++keys) + { + pd = GetPrivateData(key[keys]); + MY_ASSERT(NULL == pd); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = SetPrivateData(keys, &key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + +#if !defined(DEBUG) + did = should = PR_FALSE; + for (keys = 4; keys < 8; ++keys) + { + rv = SetPrivateData(keys, &key_string[keys]); + MY_ASSERT(PR_FAILURE == rv); + } + PrintProgress(__LINE__); +#endif + + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = SetPrivateData(key[keys], &key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = SetPrivateData(key[keys], &extension); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 8; keys < 127; ++keys) + { + rv = SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = SetPrivateData(key[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } +} /* MyThread::RootFunction */ + +/* tpd.c */ diff --git a/nsprpub/pr/src/io/Makefile.in b/nsprpub/pr/src/io/Makefile.in new file mode 100644 index 0000000000..f6b5bcd6dd --- /dev/null +++ b/nsprpub/pr/src/io/Makefile.in @@ -0,0 +1,50 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = \ + prfdcach.c \ + prmwait.c \ + priometh.c \ + pripv6.c \ + prmapopt.c \ + prlayer.c \ + prlog.c \ + prmmap.c \ + prpolevt.c \ + prprf.c \ + prscanf.c \ + prstdio.c \ + $(NULL) + +ifndef USE_PTHREADS + CSRCS += \ + prdir.c \ + prfile.c \ + prio.c \ + prsocket.c \ + $(NULL) +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/io/prdir.c b/nsprpub/pr/src/io/prdir.c new file mode 100644 index 0000000000..365afefd2f --- /dev/null +++ b/nsprpub/pr/src/io/prdir.c @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name) +{ + PRDir *dir; + PRStatus sts; + + dir = PR_NEW(PRDir); + if (dir) { + sts = _PR_MD_OPEN_DIR(&dir->md,name); + if (sts != PR_SUCCESS) { + PR_DELETE(dir); + return NULL; + } + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return dir; +} + +PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags) +{ + /* _MD_READ_DIR return a char* to the name; allocation in machine-dependent code */ + char* name = _PR_MD_READ_DIR(&dir->md, flags); + dir->d.name = name; + return name ? &dir->d : NULL; +} + +PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir) +{ + PRInt32 rv; + + if (dir) { + rv = _PR_MD_CLOSE_DIR(&dir->md); + PR_DELETE(dir); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode) +{ + PRInt32 rv; + + rv = _PR_MD_MKDIR(name, mode); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_MakeDir(const char *name, PRIntn mode) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + rv = _PR_MD_MAKE_DIR(name, mode); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name) +{ + PRInt32 rv; + + rv = _PR_MD_RMDIR(name); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +#ifdef MOZ_UNICODE +/* + * UTF16 Interface + */ +PR_IMPLEMENT(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name) +{ + PRDirUTF16 *dir; + PRStatus sts; + + dir = PR_NEW(PRDirUTF16); + if (dir) { + sts = _PR_MD_OPEN_DIR_UTF16(&dir->md,name); + if (sts != PR_SUCCESS) { + PR_DELETE(dir); + return NULL; + } + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return dir; +} + +PR_IMPLEMENT(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags) +{ + /* + * _MD_READ_DIR_UTF16 return a PRUnichar* to the name; allocation in + * machine-dependent code + */ + PRUnichar* name = _PR_MD_READ_DIR_UTF16(&dir->md, flags); + dir->d.name = name; + return name ? &dir->d : NULL; +} + +PR_IMPLEMENT(PRStatus) PR_CloseDirUTF16(PRDirUTF16 *dir) +{ + PRInt32 rv; + + if (dir) { + rv = _PR_MD_CLOSE_DIR_UTF16(&dir->md); + PR_DELETE(dir); + if (rv < 0) { + return PR_FAILURE; + } + else { + return PR_SUCCESS; + } + } + return PR_SUCCESS; +} + +#endif /* MOZ_UNICODE */ diff --git a/nsprpub/pr/src/io/prfdcach.c b/nsprpub/pr/src/io/prfdcach.c new file mode 100644 index 0000000000..ecfe3d39cc --- /dev/null +++ b/nsprpub/pr/src/io/prfdcach.c @@ -0,0 +1,252 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> + +/*****************************************************************************/ +/*****************************************************************************/ +/************************** File descriptor caching **************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +/* +** This code is built into debuggable versions of NSPR to assist in +** finding misused file descriptors. Since file descritors (PRFileDesc) +** are identified by a pointer to their structure, they can be the +** target of dangling references. Furthermore, NSPR caches and tries +** to aggressively reuse file descriptors, leading to more ambiguity. +** The following code will allow a debugging client to set environment +** variables and control the number of file descriptors that will be +** preserved before they are recycled. The environment variables are +** NSPR_FD_CACHE_SIZE_LOW and NSPR_FD_CACHE_SIZE_HIGH. The former sets +** the number of descriptors NSPR will allocate before beginning to +** recycle. The latter is the maximum number permitted in the cache +** (exclusive of those in use) at a time. +*/ +typedef struct _PR_Fd_Cache +{ + PRLock *ml; + PRIntn count; + PRFileDesc *head, *tail; + PRIntn limit_low, limit_high; +} _PR_Fd_Cache; + +static _PR_Fd_Cache _pr_fd_cache; + + +/* +** Get a FileDescriptor from the cache if one exists. If not allocate +** a new one from the heap. +*/ +PRFileDesc *_PR_Getfd(void) +{ + PRFileDesc *fd; + /* + ** $$$ + ** This may look a little wasteful. We'll see. Right now I want to + ** be able to toggle between caching and not at runtime to measure + ** the differences. If it isn't too annoying, I'll leave it in. + ** $$$$ + ** + ** The test is against _pr_fd_cache.limit_high. If that's zero, + ** we're not doing the extended cache but going for performance. + */ + if (0 == _pr_fd_cache.limit_high) + { + goto allocate; + } + else + { + do + { + if (NULL == _pr_fd_cache.head) { + goto allocate; /* nothing there */ + } + if (_pr_fd_cache.count < _pr_fd_cache.limit_low) { + goto allocate; + } + + /* we "should" be able to extract an fd from the cache */ + PR_Lock(_pr_fd_cache.ml); /* need the lock to do this safely */ + fd = _pr_fd_cache.head; /* protected extraction */ + if (NULL == fd) /* unexpected, but not fatal */ + { + PR_ASSERT(0 == _pr_fd_cache.count); + PR_ASSERT(NULL == _pr_fd_cache.tail); + } + else + { + _pr_fd_cache.count -= 1; + _pr_fd_cache.head = fd->higher; + if (NULL == _pr_fd_cache.head) + { + PR_ASSERT(0 == _pr_fd_cache.count); + _pr_fd_cache.tail = NULL; + } + PR_ASSERT(&_pr_faulty_methods == fd->methods); + PR_ASSERT(PR_INVALID_IO_LAYER == fd->identity); + PR_ASSERT(_PR_FILEDESC_FREED == fd->secret->state); + } + PR_Unlock(_pr_fd_cache.ml); + + } while (NULL == fd); /* then go around and allocate a new one */ + } + +finished: + fd->dtor = NULL; + fd->lower = fd->higher = NULL; + fd->identity = PR_NSPR_IO_LAYER; + memset(fd->secret, 0, sizeof(PRFilePrivate)); + return fd; + +allocate: + fd = PR_NEW(PRFileDesc); + if (NULL != fd) + { + fd->secret = PR_NEW(PRFilePrivate); + if (NULL == fd->secret) { + PR_DELETE(fd); + } + } + if (NULL != fd) { + goto finished; + } + else { + return NULL; + } + +} /* _PR_Getfd */ + +/* +** Return a file descriptor to the cache unless there are too many in +** there already. If put in cache, clear the fields first. +*/ +void _PR_Putfd(PRFileDesc *fd) +{ + PR_ASSERT(PR_NSPR_IO_LAYER == fd->identity); + fd->methods = &_pr_faulty_methods; + fd->identity = PR_INVALID_IO_LAYER; + fd->secret->state = _PR_FILEDESC_FREED; + + if (0 != _pr_fd_cache.limit_high) + { + if (_pr_fd_cache.count < _pr_fd_cache.limit_high) + { + PR_Lock(_pr_fd_cache.ml); + if (NULL == _pr_fd_cache.tail) + { + PR_ASSERT(0 == _pr_fd_cache.count); + PR_ASSERT(NULL == _pr_fd_cache.head); + _pr_fd_cache.head = _pr_fd_cache.tail = fd; + } + else + { + PR_ASSERT(NULL == _pr_fd_cache.tail->higher); + _pr_fd_cache.tail->higher = fd; + _pr_fd_cache.tail = fd; /* new value */ + } + fd->higher = NULL; /* always so */ + _pr_fd_cache.count += 1; /* count the new entry */ + PR_Unlock(_pr_fd_cache.ml); + return; + } + } + + PR_Free(fd->secret); + PR_Free(fd); +} /* _PR_Putfd */ + +PR_IMPLEMENT(PRStatus) PR_SetFDCacheSize(PRIntn low, PRIntn high) +{ + /* + ** This can be called at any time, may adjust the cache sizes, + ** turn the caches off, or turn them on. It is not dependent + ** on the compilation setting of DEBUG. + */ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (low > high) { + low = high; /* sanity check the params */ + } + + PR_Lock(_pr_fd_cache.ml); + _pr_fd_cache.limit_high = high; + _pr_fd_cache.limit_low = low; + PR_Unlock(_pr_fd_cache.ml); + return PR_SUCCESS; +} /* PR_SetFDCacheSize */ + +void _PR_InitFdCache(void) +{ + /* + ** The fd caching is enabled by default for DEBUG builds, + ** disabled by default for OPT builds. That default can + ** be overridden at runtime using environment variables + ** or a super-wiz-bang API. + */ + const char *low = PR_GetEnv("NSPR_FD_CACHE_SIZE_LOW"); + const char *high = PR_GetEnv("NSPR_FD_CACHE_SIZE_HIGH"); + + /* + ** _low is allowed to be zero, _high is not. + ** If _high is zero, we're not doing the caching. + */ + + _pr_fd_cache.limit_low = 0; +#if defined(DEBUG) + _pr_fd_cache.limit_high = FD_SETSIZE; +#else + _pr_fd_cache.limit_high = 0; +#endif /* defined(DEBUG) */ + + if (NULL != low) { + _pr_fd_cache.limit_low = atoi(low); + } + if (NULL != high) { + _pr_fd_cache.limit_high = atoi(high); + } + + if (_pr_fd_cache.limit_low < 0) { + _pr_fd_cache.limit_low = 0; + } + if (_pr_fd_cache.limit_low > FD_SETSIZE) { + _pr_fd_cache.limit_low = FD_SETSIZE; + } + + if (_pr_fd_cache.limit_high > FD_SETSIZE) { + _pr_fd_cache.limit_high = FD_SETSIZE; + } + + if (_pr_fd_cache.limit_high < _pr_fd_cache.limit_low) { + _pr_fd_cache.limit_high = _pr_fd_cache.limit_low; + } + + _pr_fd_cache.ml = PR_NewLock(); + PR_ASSERT(NULL != _pr_fd_cache.ml); + +} /* _PR_InitFdCache */ + +void _PR_CleanupFdCache(void) +{ + PRFileDesc *fd, *next; + + for (fd = _pr_fd_cache.head; fd != NULL; fd = next) + { + next = fd->higher; + PR_DELETE(fd->secret); + PR_DELETE(fd); + } + _pr_fd_cache.head = NULL; + _pr_fd_cache.tail = NULL; + _pr_fd_cache.count = 0; + PR_DestroyLock(_pr_fd_cache.ml); + _pr_fd_cache.ml = NULL; +} /* _PR_CleanupFdCache */ + +/* prfdcach.c */ diff --git a/nsprpub/pr/src/io/prfile.c b/nsprpub/pr/src/io/prfile.c new file mode 100644 index 0000000000..4b07baf41b --- /dev/null +++ b/nsprpub/pr/src/io/prfile.c @@ -0,0 +1,818 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> +#include <fcntl.h> + +#ifdef XP_UNIX +#if defined(AIX) || defined(QNX) +/* To pick up sysconf */ +#include <unistd.h> +#else +/* To pick up getrlimit, setrlimit */ +#include <sys/time.h> +#include <sys/resource.h> +#endif +#endif /* XP_UNIX */ + +extern PRLock *_pr_flock_lock; +extern PRCondVar *_pr_flock_cv; + +static PRInt32 PR_CALLBACK FileRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PRInt32 rv = 0; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + rv = -1; + } + if (rv == -1) { + return rv; + } + + rv = _PR_MD_READ(fd, buf, amount); + if (rv < 0) { + PR_ASSERT(rv == -1); + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("read -> %d", rv)); + return rv; +} + +static PRInt32 PR_CALLBACK FileWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PRInt32 rv = 0; + PRInt32 temp, count; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + rv = -1; + } + if (rv != 0) { + return rv; + } + + count = 0; +#if !defined(_PR_HAVE_O_APPEND) /* Bugzilla: 4090, 276330 */ + if (fd->secret->appendMode) { + if (PR_Seek64(fd, 0, PR_SEEK_END) == -1) { + return -1; + } + } /* if (fd->secret->appendMode...) */ +#endif /* _PR_HAVE_O_APPEND */ + while (amount > 0) { + temp = _PR_MD_WRITE(fd, buf, amount); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("write -> %d", count)); + return count; +} + +static PROffset32 PR_CALLBACK FileSeek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) +{ + PROffset32 result; + + result = _PR_MD_LSEEK(fd, offset, whence); + return result; +} + +static PROffset64 PR_CALLBACK FileSeek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) +{ + PROffset64 result; + + result = _PR_MD_LSEEK64(fd, offset, whence); + return result; +} + +static PRInt32 PR_CALLBACK FileAvailable(PRFileDesc *fd) +{ + PRInt32 result, cur, end; + + cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR); + + if (cur >= 0) { + end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END); + } + + if ((cur < 0) || (end < 0)) { + return -1; + } + + result = end - cur; + _PR_MD_LSEEK(fd, cur, PR_SEEK_SET); + + return result; +} + +static PRInt64 PR_CALLBACK FileAvailable64(PRFileDesc *fd) +{ + PRInt64 result, cur, end; + PRInt64 minus_one; + + LL_I2L(minus_one, -1); + cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR); + + if (LL_GE_ZERO(cur)) { + end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END); + } + + if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) { + return minus_one; + } + + LL_SUB(result, end, cur); + (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET); + + return result; +} + +static PRInt32 PR_CALLBACK PipeAvailable(PRFileDesc *fd) +{ + PRInt32 rv; + rv = _PR_MD_PIPEAVAILABLE(fd); + return rv; +} + +static PRInt64 PR_CALLBACK PipeAvailable64(PRFileDesc *fd) +{ + PRInt64 rv; + LL_I2L(rv, _PR_MD_PIPEAVAILABLE(fd)); + return rv; +} + +static PRStatus PR_CALLBACK PipeSync(PRFileDesc *fd) +{ + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK FileGetInfo(PRFileDesc *fd, PRFileInfo *info) +{ + PRInt32 rv; + + rv = _PR_MD_GETOPENFILEINFO(fd, info); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +static PRStatus PR_CALLBACK FileGetInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + /* $$$$ NOT YET IMPLEMENTED */ + PRInt32 rv; + + rv = _PR_MD_GETOPENFILEINFO64(fd, info); + if (rv < 0) { + return PR_FAILURE; + } + else { + return PR_SUCCESS; + } +} + +static PRStatus PR_CALLBACK FileSync(PRFileDesc *fd) +{ + PRInt32 result; + result = _PR_MD_FSYNC(fd); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK FileClose(PRFileDesc *fd) +{ + if (!fd || !fd->secret + || (fd->secret->state != _PR_FILEDESC_OPEN + && fd->secret->state != _PR_FILEDESC_CLOSED)) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + + if (fd->secret->state == _PR_FILEDESC_OPEN) { + if (_PR_MD_CLOSE_FILE(fd->secret->md.osfd) < 0) { + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + } + PR_FreeFileDesc(fd); + return PR_SUCCESS; +} + +static PRInt16 PR_CALLBACK FilePoll( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + *out_flags = 0; + return in_flags; +} /* FilePoll */ + +static PRIOMethods _pr_fileMethods = { + PR_DESC_FILE, + FileClose, + FileRead, + FileWrite, + FileAvailable, + FileAvailable64, + FileSync, + FileSeek, + FileSeek64, + FileGetInfo, + FileGetInfo64, + (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, + FilePoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +PR_IMPLEMENT(const PRIOMethods*) PR_GetFileMethods(void) +{ + return &_pr_fileMethods; +} + +static PRIOMethods _pr_pipeMethods = { + PR_DESC_PIPE, + FileClose, + FileRead, + FileWrite, + PipeAvailable, + PipeAvailable64, + PipeSync, + (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, + FilePoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +PR_IMPLEMENT(const PRIOMethods*) PR_GetPipeMethods(void) +{ + return &_pr_pipeMethods; +} + +PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode) +{ + PROsfd osfd; + PRFileDesc *fd = 0; +#if !defined(_PR_HAVE_O_APPEND) + PRBool appendMode = ( PR_APPEND & flags )? PR_TRUE : PR_FALSE; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + /* Map pr open flags and mode to os specific flags */ + + osfd = _PR_MD_OPEN(name, flags, mode); + if (osfd != -1) { + fd = PR_AllocFileDesc(osfd, &_pr_fileMethods); + if (!fd) { + (void) _PR_MD_CLOSE_FILE(osfd); + } else { +#if !defined(_PR_HAVE_O_APPEND) + fd->secret->appendMode = appendMode; +#endif + _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE); + } + } + return fd; +} + +PR_IMPLEMENT(PRFileDesc*) PR_OpenFile( + const char *name, PRIntn flags, PRIntn mode) +{ + PROsfd osfd; + PRFileDesc *fd = 0; +#if !defined(_PR_HAVE_O_APPEND) + PRBool appendMode = ( PR_APPEND & flags )? PR_TRUE : PR_FALSE; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + /* Map pr open flags and mode to os specific flags */ + + osfd = _PR_MD_OPEN_FILE(name, flags, mode); + if (osfd != -1) { + fd = PR_AllocFileDesc(osfd, &_pr_fileMethods); + if (!fd) { + (void) _PR_MD_CLOSE_FILE(osfd); + } else { +#if !defined(_PR_HAVE_O_APPEND) + fd->secret->appendMode = appendMode; +#endif + _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE); + } + } + return fd; +} + +PR_IMPLEMENT(PRInt32) PR_GetSysfdTableMax(void) +{ +#if defined(XP_UNIX) && !defined(AIX) && !defined(QNX) + struct rlimit rlim; + + if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + /* XXX need to call PR_SetError() */ + return -1; + } + + return rlim.rlim_max; +#elif defined(AIX) || defined(QNX) + return sysconf(_SC_OPEN_MAX); +#elif defined(WIN32) + /* + * There is a systemwide limit of 65536 user handles. + */ + return 16384; +#elif defined (WIN16) + return FOPEN_MAX; +#elif defined(XP_OS2) + ULONG ulReqCount = 0; + ULONG ulCurMaxFH = 0; + DosSetRelMaxFH(&ulReqCount, &ulCurMaxFH); + return ulCurMaxFH; +#else + write me; +#endif +} + +PR_IMPLEMENT(PRInt32) PR_SetSysfdTableSize(int table_size) +{ +#if defined(XP_UNIX) && !defined(AIX) && !defined(QNX) + struct rlimit rlim; + PRInt32 tableMax = PR_GetSysfdTableMax(); + + if (tableMax < 0) { + return -1; + } + + if (tableMax > FD_SETSIZE) { + tableMax = FD_SETSIZE; + } + + rlim.rlim_max = tableMax; + + /* Grow as much as we can; even if too big */ + if ( rlim.rlim_max < table_size ) { + rlim.rlim_cur = rlim.rlim_max; + } + else { + rlim.rlim_cur = table_size; + } + + if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + /* XXX need to call PR_SetError() */ + return -1; + } + + return rlim.rlim_cur; +#elif defined(XP_OS2) + PRInt32 tableMax = PR_GetSysfdTableMax(); + if (table_size > tableMax) { + APIRET rc = NO_ERROR; + rc = DosSetMaxFH(table_size); + if (rc == NO_ERROR) { + return table_size; + } + else { + return -1; + } + } + return tableMax; +#elif defined(AIX) || defined(QNX) \ + || defined(WIN32) || defined(WIN16) + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#else + write me; +#endif +} + +PR_IMPLEMENT(PRStatus) PR_Delete(const char *name) +{ + PRInt32 rv; + + rv = _PR_MD_DELETE(name); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info) +{ + PRInt32 rv; + + rv = _PR_MD_GETFILEINFO(fn, info); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + rv = _PR_MD_GETFILEINFO64(fn, info); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to) +{ + PRInt32 rv; + + rv = _PR_MD_RENAME(from, to); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how) +{ + PRInt32 rv; + + rv = _PR_MD_ACCESS(name, how); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +/* +** Import an existing OS file to NSPR +*/ +PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PROsfd osfd) +{ + PRFileDesc *fd = NULL; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + fd = PR_AllocFileDesc(osfd, &_pr_fileMethods); + if( !fd ) { + (void) _PR_MD_CLOSE_FILE(osfd); + } else { + _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); + } + + return fd; +} + +/* +** Import an existing OS pipe to NSPR +*/ +PR_IMPLEMENT(PRFileDesc*) PR_ImportPipe(PROsfd osfd) +{ + PRFileDesc *fd = NULL; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + fd = PR_AllocFileDesc(osfd, &_pr_pipeMethods); + if( !fd ) { + (void) _PR_MD_CLOSE_FILE(osfd); + } else { + _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); +#ifdef WINNT + fd->secret->md.sync_file_io = PR_TRUE; +#endif + } + + return fd; +} + +#ifndef NO_NSPR_10_SUPPORT +/* +** PR_Stat() for Win16 is defined in w16io.c +** it is a hack to circumvent problems in Gromit and Java +** See also: BugSplat: 98516. +*/ +#if !defined(WIN16) +/* + * This function is supposed to be for backward compatibility with + * nspr 1.0. Therefore, it still uses the nspr 1.0 error-reporting + * mechanism -- returns a PRInt32, which is the error code when the call + * fails. + * + * If we need this function in nspr 2.0, it should be changed to + * return PRStatus, as follows: + * + * PR_IMPLEMENT(PRStatus) PR_Stat(const char *name, struct stat *buf) + * { + * PRInt32 rv; + * + * rv = _PR_MD_STAT(name, buf); + * if (rv < 0) + * return PR_FAILURE; + * else + * return PR_SUCCESS; + * } + * + * -- wtc, 2/14/97. + */ +PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf) +{ + PRInt32 rv; + + rv = _PR_MD_STAT(name, buf); + return rv; +} + +#endif /* !defined(WIN16) */ +#endif /* ! NO_NSPR_10_SUPPORT */ + +PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + +#ifdef WINNT + if (!fd->secret->md.io_model_committed) { + PRInt32 rv; + rv = _md_Associate((HANDLE)fd->secret->md.osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } +#endif + + PR_Lock(_pr_flock_lock); + while (fd->secret->lockCount == -1) { + PR_WaitCondVar(_pr_flock_cv, PR_INTERVAL_NO_TIMEOUT); + } + if (fd->secret->lockCount == 0) { + fd->secret->lockCount = -1; + PR_Unlock(_pr_flock_lock); + status = _PR_MD_LOCKFILE(fd->secret->md.osfd); + PR_Lock(_pr_flock_lock); + fd->secret->lockCount = (status == PR_SUCCESS) ? 1 : 0; + PR_NotifyAllCondVar(_pr_flock_cv); + } else { + fd->secret->lockCount++; + } + PR_Unlock(_pr_flock_lock); + + return status; +} + +PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + +#ifdef WINNT + if (!fd->secret->md.io_model_committed) { + PRInt32 rv; + rv = _md_Associate((HANDLE)fd->secret->md.osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } +#endif + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 0) { + status = _PR_MD_TLOCKFILE(fd->secret->md.osfd); + PR_ASSERT(status == PR_SUCCESS || fd->secret->lockCount == 0); + if (status == PR_SUCCESS) { + fd->secret->lockCount = 1; + } + } else { + fd->secret->lockCount++; + } + PR_Unlock(_pr_flock_lock); + + return status; +} + +PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd) +{ + PRStatus rv = PR_SUCCESS; + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 1) { + rv = _PR_MD_UNLOCKFILE(fd->secret->md.osfd); + if (rv == PR_SUCCESS) { + fd->secret->lockCount = 0; + } + } else { + fd->secret->lockCount--; + } + PR_Unlock(_pr_flock_lock); + + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_CreatePipe( + PRFileDesc **readPipe, + PRFileDesc **writePipe +) +{ +#if defined(WIN32) && !defined(WINCE) + HANDLE readEnd, writeEnd; + SECURITY_ATTRIBUTES pipeAttributes; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + ZeroMemory(&pipeAttributes, sizeof(pipeAttributes)); + pipeAttributes.nLength = sizeof(pipeAttributes); + pipeAttributes.bInheritHandle = TRUE; + if (CreatePipe(&readEnd, &writeEnd, &pipeAttributes, 0) == 0) { + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; + } + *readPipe = PR_AllocFileDesc((PROsfd)readEnd, &_pr_pipeMethods); + if (NULL == *readPipe) { + CloseHandle(readEnd); + CloseHandle(writeEnd); + return PR_FAILURE; + } + *writePipe = PR_AllocFileDesc((PROsfd)writeEnd, &_pr_pipeMethods); + if (NULL == *writePipe) { + PR_Close(*readPipe); + CloseHandle(writeEnd); + return PR_FAILURE; + } +#ifdef WINNT + (*readPipe)->secret->md.sync_file_io = PR_TRUE; + (*writePipe)->secret->md.sync_file_io = PR_TRUE; +#endif + (*readPipe)->secret->inheritable = _PR_TRI_TRUE; + (*writePipe)->secret->inheritable = _PR_TRI_TRUE; + return PR_SUCCESS; +#elif defined(XP_UNIX) || defined(XP_OS2) +#ifdef XP_OS2 + HFILE pipefd[2]; +#else + int pipefd[2]; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#ifdef XP_OS2 + if (DosCreatePipe(&pipefd[0], &pipefd[1], 4096) != 0) { +#else + if (pipe(pipefd) == -1) { +#endif + /* XXX map pipe error */ + PR_SetError(PR_UNKNOWN_ERROR, errno); + return PR_FAILURE; + } + *readPipe = PR_AllocFileDesc(pipefd[0], &_pr_pipeMethods); + if (NULL == *readPipe) { + close(pipefd[0]); + close(pipefd[1]); + return PR_FAILURE; + } + *writePipe = PR_AllocFileDesc(pipefd[1], &_pr_pipeMethods); + if (NULL == *writePipe) { + PR_Close(*readPipe); + close(pipefd[1]); + return PR_FAILURE; + } + _PR_MD_MAKE_NONBLOCK(*readPipe); + _PR_MD_INIT_FD_INHERITABLE(*readPipe, PR_FALSE); + _PR_MD_MAKE_NONBLOCK(*writePipe); + _PR_MD_INIT_FD_INHERITABLE(*writePipe, PR_FALSE); + return PR_SUCCESS; +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} + +#ifdef MOZ_UNICODE +/* ================ UTF16 Interfaces ================================ */ +PR_IMPLEMENT(PRFileDesc*) PR_OpenFileUTF16( + const PRUnichar *name, PRIntn flags, PRIntn mode) +{ + PROsfd osfd; + PRFileDesc *fd = 0; +#if !defined(_PR_HAVE_O_APPEND) + PRBool appendMode = ( PR_APPEND & flags )? PR_TRUE : PR_FALSE; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + /* Map pr open flags and mode to os specific flags */ + osfd = _PR_MD_OPEN_FILE_UTF16(name, flags, mode); + if (osfd != -1) { + fd = PR_AllocFileDesc(osfd, &_pr_fileMethods); + if (!fd) { + (void) _PR_MD_CLOSE_FILE(osfd); + } else { +#if !defined(_PR_HAVE_O_APPEND) + fd->secret->appendMode = appendMode; +#endif + _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE); + } + } + return fd; +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + rv = _PR_MD_GETFILEINFO64_UTF16(fn, info); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +/* ================ UTF16 Interfaces ================================ */ +#endif /* MOZ_UNICODE */ diff --git a/nsprpub/pr/src/io/prio.c b/nsprpub/pr/src/io/prio.c new file mode 100644 index 0000000000..745d7721b9 --- /dev/null +++ b/nsprpub/pr/src/io/prio.c @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> /* for memset() */ + + +/************************************************************************/ + +PRLock *_pr_flock_lock; +PRCondVar *_pr_flock_cv; + +#ifdef WINCE +/* + * There are no stdin, stdout, stderr in Windows CE. INVALID_HANDLE_VALUE + * should cause all I/O functions on the handle to fail. + */ +#define STD_INPUT_HANDLE ((DWORD)-10) +#define STD_OUTPUT_HANDLE ((DWORD)-11) +#define STD_ERROR_HANDLE ((DWORD)-12) + +static HANDLE GetStdHandle(DWORD nStdHandle) +{ + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return INVALID_HANDLE_VALUE; +} +#endif + +void _PR_InitIO(void) +{ + const PRIOMethods *methods = PR_GetFileMethods(); + + _PR_InitFdCache(); + + _pr_flock_lock = PR_NewLock(); + _pr_flock_cv = PR_NewCondVar(_pr_flock_lock); + +#ifdef WIN32 + _pr_stdin = PR_AllocFileDesc((PROsfd)GetStdHandle(STD_INPUT_HANDLE), + methods); + _pr_stdout = PR_AllocFileDesc((PROsfd)GetStdHandle(STD_OUTPUT_HANDLE), + methods); + _pr_stderr = PR_AllocFileDesc((PROsfd)GetStdHandle(STD_ERROR_HANDLE), + methods); +#ifdef WINNT + _pr_stdin->secret->md.sync_file_io = PR_TRUE; + _pr_stdout->secret->md.sync_file_io = PR_TRUE; + _pr_stderr->secret->md.sync_file_io = PR_TRUE; +#endif +#else + _pr_stdin = PR_AllocFileDesc(0, methods); + _pr_stdout = PR_AllocFileDesc(1, methods); + _pr_stderr = PR_AllocFileDesc(2, methods); +#endif + _PR_MD_INIT_FD_INHERITABLE(_pr_stdin, PR_TRUE); + _PR_MD_INIT_FD_INHERITABLE(_pr_stdout, PR_TRUE); + _PR_MD_INIT_FD_INHERITABLE(_pr_stderr, PR_TRUE); + + _PR_MD_INIT_IO(); +} + +void _PR_CleanupIO(void) +{ + PR_FreeFileDesc(_pr_stdin); + _pr_stdin = NULL; + PR_FreeFileDesc(_pr_stdout); + _pr_stdout = NULL; + PR_FreeFileDesc(_pr_stderr); + _pr_stderr = NULL; + + if (_pr_flock_cv) { + PR_DestroyCondVar(_pr_flock_cv); + _pr_flock_cv = NULL; + } + if (_pr_flock_lock) { + PR_DestroyLock(_pr_flock_lock); + _pr_flock_lock = NULL; + } + + _PR_CleanupFdCache(); +} + +PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) +{ + PRFileDesc *result = NULL; + PR_ASSERT((int) osfd >= PR_StandardInput && osfd <= PR_StandardError); + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + switch (osfd) + { + case PR_StandardInput: result = _pr_stdin; break; + case PR_StandardOutput: result = _pr_stdout; break; + case PR_StandardError: result = _pr_stderr; break; + default: + (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + return result; +} + +PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc( + PROsfd osfd, const PRIOMethods *methods) +{ + PRFileDesc *fd; + +#ifdef XP_UNIX + /* + * Assert that the file descriptor is small enough to fit in the + * fd_set passed to select + */ + PR_ASSERT(osfd < FD_SETSIZE); +#endif + fd = _PR_Getfd(); + if (fd) { + /* Initialize the members of PRFileDesc and PRFilePrivate */ + fd->methods = methods; + fd->secret->state = _PR_FILEDESC_OPEN; + fd->secret->md.osfd = osfd; +#if defined(_WIN64) + fd->secret->alreadyConnected = PR_FALSE; + fd->secret->overlappedActive = PR_FALSE; +#endif + _PR_MD_INIT_FILEDESC(fd); + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + + return fd; +} + +PR_IMPLEMENT(void) PR_FreeFileDesc(PRFileDesc *fd) +{ + PR_ASSERT(fd); + _PR_Putfd(fd); +} + +#if defined(_WIN64) && defined(WIN95) + +PRFileDescList *_fd_waiting_for_overlapped_done = NULL; +PRLock *_fd_waiting_for_overlapped_done_lock = NULL; + +void CheckOverlappedPendingSocketsAreDone() +{ + if (!_fd_waiting_for_overlapped_done_lock || + !_fd_waiting_for_overlapped_done) { + return; + } + + PR_Lock(_fd_waiting_for_overlapped_done_lock); + + PRFileDescList *cur = _fd_waiting_for_overlapped_done; + PRFileDescList *previous = NULL; + while (cur) { + PR_ASSERT(cur->fd->secret->overlappedActive); + PRFileDesc *fd = cur->fd; + DWORD rvSent; + if (GetOverlappedResult((HANDLE)fd->secret->md.osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) { + fd->secret->overlappedActive = PR_FALSE; + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("CheckOverlappedPendingSocketsAreDone GetOverlappedResult succeeded\n")); + } else { + DWORD err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("CheckOverlappedPendingSocketsAreDone GetOverlappedResult failed %d\n", err)); + if (err != ERROR_IO_INCOMPLETE) { + fd->secret->overlappedActive = PR_FALSE; + } + } + + if (!fd->secret->overlappedActive) { + + _PR_MD_CLOSE_SOCKET(fd->secret->md.osfd); + fd->secret->state = _PR_FILEDESC_CLOSED; +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBuffer) { + PR_ASSERT(fd->secret->peekBufSize > 0); + PR_DELETE(fd->secret->peekBuffer); + fd->secret->peekBufSize = 0; + fd->secret->peekBytes = 0; + } +#endif + + PR_FreeFileDesc(fd); + + if (previous) { + previous->next = cur->next; + } else { + _fd_waiting_for_overlapped_done = cur->next; + } + PRFileDescList *del = cur; + cur = cur->next; + PR_Free(del); + } else { + previous = cur; + cur = cur->next; + } + } + + PR_Unlock(_fd_waiting_for_overlapped_done_lock); +} +#endif + +/* +** Wait for some i/o to finish on one or more more poll descriptors. +*/ +PR_IMPLEMENT(PRInt32) PR_Poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ +#if defined(_WIN64) && defined(WIN95) + // For each iteration check if TFO overlapped IOs are down. + CheckOverlappedPendingSocketsAreDone(); +#endif + + return(_PR_MD_PR_POLL(pds, npds, timeout)); +} + +/* +** Set the inheritance attribute of a file descriptor. +*/ +PR_IMPLEMENT(PRStatus) PR_SetFDInheritable( + PRFileDesc *fd, + PRBool inheritable) +{ +#if defined(XP_UNIX) || defined(WIN32) || defined(XP_OS2) + /* + * Only a non-layered, NSPR file descriptor can be inherited + * by a child process. + */ + if (fd->identity != PR_NSPR_IO_LAYER) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + if (fd->secret->inheritable != inheritable) { + if (_PR_MD_SET_FD_INHERITABLE(fd, inheritable) == PR_FAILURE) { + return PR_FAILURE; + } + fd->secret->inheritable = inheritable; + } + return PR_SUCCESS; +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} + +/* +** This function only has a useful implementation in the debug build of +** the pthreads version. +*/ +PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) +{ + /* do nothing */ +} /* PT_FPrintStats */ diff --git a/nsprpub/pr/src/io/priometh.c b/nsprpub/pr/src/io/priometh.c new file mode 100644 index 0000000000..508719a809 --- /dev/null +++ b/nsprpub/pr/src/io/priometh.c @@ -0,0 +1,607 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "primpl.h" + +#include <string.h> + +/*****************************************************************************/ +/************************** Invalid I/O method object ************************/ +/*****************************************************************************/ +PRIOMethods _pr_faulty_methods = { + (PRDescType)0, + (PRCloseFN)_PR_InvalidStatus, + (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, + (PRPollFN)_PR_InvalidInt16, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +PRIntn _PR_InvalidInt(void) +{ + PR_NOT_REACHED("I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} /* _PR_InvalidInt */ + +PRInt16 _PR_InvalidInt16(void) +{ + PR_NOT_REACHED("I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} /* _PR_InvalidInt */ + +PRInt64 _PR_InvalidInt64(void) +{ + PRInt64 rv; + LL_I2L(rv, -1); + PR_NOT_REACHED("I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return rv; +} /* _PR_InvalidInt */ + +/* + * An invalid method that returns PRStatus + */ + +PRStatus _PR_InvalidStatus(void) +{ + PR_NOT_REACHED("I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} /* _PR_InvalidDesc */ + +/* + * An invalid method that returns a pointer + */ + +PRFileDesc *_PR_InvalidDesc(void) +{ + PR_NOT_REACHED("I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} /* _PR_InvalidDesc */ + +PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file) +{ + return file->methods->file_type; +} + +PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd) +{ + return (fd->methods->close)(fd); +} + +PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return((fd->methods->read)(fd,buf,amount)); +} + +PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return((fd->methods->write)(fd,buf,amount)); +} + +PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + return((fd->methods->seek)(fd, offset, whence)); +} + +PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + return((fd->methods->seek64)(fd, offset, whence)); +} + +PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd) +{ + return((fd->methods->available)(fd)); +} + +PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd) +{ + return((fd->methods->available64)(fd)); +} + +PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info) +{ + return((fd->methods->fileInfo)(fd, info)); +} + +PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + return((fd->methods->fileInfo64)(fd, info)); +} + +PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd) +{ + return((fd->methods->fsync)(fd)); +} + +PR_IMPLEMENT(PRStatus) PR_Connect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + return((fd->methods->connect)(fd,addr,timeout)); +} + +PR_IMPLEMENT(PRStatus) PR_ConnectContinue( + PRFileDesc *fd, PRInt16 out_flags) +{ + return((fd->methods->connectcontinue)(fd,out_flags)); +} + +PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr, + PRIntervalTime timeout) +{ + return((fd->methods->accept)(fd,addr,timeout)); +} + +PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr) +{ + return((fd->methods->bind)(fd,addr)); +} + +PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how) +{ + return((fd->methods->shutdown)(fd,how)); +} + +PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog) +{ + return((fd->methods->listen)(fd,backlog)); +} + +PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + return((fd->methods->recv)(fd,buf,amount,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + return((fd->methods->send)(fd,buf,amount,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout) +{ + if (iov_size > PR_MAX_IOVECTOR_SIZE) + { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return -1; + } + return((fd->methods->writev)(fd,iov,iov_size,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_SendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_TransmitFile( + PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_AcceptRead( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout)); +} + +PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr) +{ + return((fd->methods->getsockname)(fd,addr)); +} + +PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + return((fd->methods->getpeername)(fd,addr)); +} + +PR_IMPLEMENT(PRStatus) PR_GetSocketOption( + PRFileDesc *fd, PRSocketOptionData *data) +{ + return((fd->methods->getsocketoption)(fd, data)); +} + +PR_IMPLEMENT(PRStatus) PR_SetSocketOption( + PRFileDesc *fd, const PRSocketOptionData *data) +{ + return((fd->methods->setsocketoption)(fd, data)); +} + +PR_IMPLEMENT(PRInt32) PR_SendFile( + PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + return((sd->methods->sendfile)(sd,sfd,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + PRInt32 rv = -1; + PRNetAddr remote; + PRFileDesc *accepted = NULL; + + /* + ** The timeout does not apply to the accept portion of the + ** operation - it waits indefinitely. + */ + accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT); + if (NULL == accepted) { + return rv; + } + + rv = PR_Recv(accepted, buf, amount, 0, timeout); + if (rv >= 0) + { + /* copy the new info out where caller can see it */ +#define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */ + PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK; + *raddr = (PRNetAddr*)(aligned & ~AMASK); + memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote)); + *nd = accepted; + return rv; + } + + PR_Close(accepted); + return rv; +} + +/* + * PR_EmulateSendFile + * + * 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 + * + */ + +#if defined(XP_UNIX) || defined(WIN32) + +/* + * An implementation based on memory-mapped files + */ + +#define SENDFILE_MMAP_CHUNK (256 * 1024) + +PR_IMPLEMENT(PRInt32) PR_EmulateSendFile( + PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + PRInt32 rv, count = 0; + PRInt32 len, file_bytes, index = 0; + PRFileInfo info; + PRIOVec iov[3]; + PRFileMap *mapHandle = NULL; + void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */ + PRUint32 file_mmap_offset, alignment; + PRInt64 zero64; + PROffset64 file_mmap_offset64; + PRUint32 addr_offset, mmap_len; + + /* Get file size */ + if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) { + count = -1; + goto done; + } + if (sfd->file_nbytes && + (info.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 = info.size - sfd->file_offset; + } + + alignment = PR_GetMemMapAlignment(); + + /* number of initial bytes to skip in mmap'd segment */ + addr_offset = sfd->file_offset % alignment; + + /* find previous mmap alignment boundary */ + file_mmap_offset = sfd->file_offset - addr_offset; + + /* + * If the file is large, mmap and send the file in chunks so as + * to not consume too much virtual address space + */ + mmap_len = PR_MIN(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) { + LL_I2L(zero64, 0); + mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY); + if (!mapHandle) { + count = -1; + goto done; + } + LL_I2L(file_mmap_offset64, file_mmap_offset); + addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len); + if (!addr) { + 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) { + PR_MemUnmap(addr, mmap_len); + } + if (rv < 0) { + count = -1; + goto done; + } + + PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0)); + + file_bytes -= len; + count += rv; + if (!file_bytes) { /* header, file and trailer are sent */ + goto done; + } + + /* + * send remaining bytes of the file, if any + */ + len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK); + while (len > 0) { + /* + * Map in (part of) file + */ + file_mmap_offset = sfd->file_offset + count - sfd->hlen; + PR_ASSERT((file_mmap_offset % alignment) == 0); + + LL_I2L(file_mmap_offset64, file_mmap_offset); + addr = PR_MemMap(mapHandle, file_mmap_offset64, len); + if (!addr) { + count = -1; + goto done; + } + rv = PR_Send(sd, addr, len, 0, timeout); + PR_MemUnmap(addr, len); + if (rv < 0) { + count = -1; + goto done; + } + + PR_ASSERT(rv == len); + file_bytes -= rv; + count += rv; + len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK); + } + 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 (mapHandle) { + PR_CloseFileMap(mapHandle); + } + if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) { + PR_Close(sd); + } + return count; +} + +#else + +PR_IMPLEMENT(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; + char *buf; + +#define _SENDFILE_BUFSIZE (16 * 1024) + + buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE); + if (buf == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + + /* + * send header first + */ + 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; + buffer = (const void*) ((const char*)buffer + rv); + buflen -= rv; + } + } + + /* + * send file next + */ + 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) { + /* PR_Read() has invoked PR_SetError(). */ + rv = -1; + goto done; + } + } else { + readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE); + 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 = PR_MIN(sendbytes, _SENDFILE_BUFSIZE); + } + 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; +} + +#endif + +/* priometh.c */ diff --git a/nsprpub/pr/src/io/pripv6.c b/nsprpub/pr/src/io/pripv6.c new file mode 100644 index 0000000000..1c299652e6 --- /dev/null +++ b/nsprpub/pr/src/io/pripv6.c @@ -0,0 +1,372 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: pripv6.c +** Description: Support for various functions unique to IPv6 +*/ +#include "primpl.h" +#include <string.h> + +#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE) + +static PRIOMethods ipv6_to_v4_tcpMethods; +static PRIOMethods ipv6_to_v4_udpMethods; +static PRDescIdentity _pr_ipv6_to_ipv4_id; +extern PRBool IsValidNetAddr(const PRNetAddr *addr); +extern const PRIPv6Addr _pr_in6addr_any; +extern const PRIPv6Addr _pr_in6addr_loopback; + +/* + * convert an IPv4-mapped IPv6 addr to an IPv4 addr + */ +static void _PR_ConvertToIpv4NetAddr(const PRNetAddr *src_v6addr, + PRNetAddr *dst_v4addr) +{ + const PRUint8 *srcp; + + PR_ASSERT(PR_AF_INET6 == src_v6addr->ipv6.family); + + if (PR_IsNetAddrType(src_v6addr, PR_IpAddrV4Mapped)) { + srcp = src_v6addr->ipv6.ip.pr_s6_addr; + memcpy((char *) &dst_v4addr->inet.ip, srcp + 12, 4); + } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrAny)) { + dst_v4addr->inet.ip = htonl(INADDR_ANY); + } else if (PR_IsNetAddrType(src_v6addr, PR_IpAddrLoopback)) { + dst_v4addr->inet.ip = htonl(INADDR_LOOPBACK); + } + dst_v4addr->inet.family = PR_AF_INET; + dst_v4addr->inet.port = src_v6addr->ipv6.port; +} + +/* + * convert an IPv4 addr to an IPv4-mapped IPv6 addr + */ +static void _PR_ConvertToIpv6NetAddr(const PRNetAddr *src_v4addr, + PRNetAddr *dst_v6addr) +{ + PRUint8 *dstp; + + PR_ASSERT(PR_AF_INET == src_v4addr->inet.family); + dst_v6addr->ipv6.family = PR_AF_INET6; + dst_v6addr->ipv6.port = src_v4addr->inet.port; + + if (htonl(INADDR_ANY) == src_v4addr->inet.ip) { + dst_v6addr->ipv6.ip = _pr_in6addr_any; + } else { + dstp = dst_v6addr->ipv6.ip.pr_s6_addr; + memset(dstp, 0, 10); + memset(dstp + 10, 0xff, 2); + memcpy(dstp + 12,(char *) &src_v4addr->inet.ip, 4); + } +} + +static PRStatus PR_CALLBACK Ipv6ToIpv4SocketBind(PRFileDesc *fd, + const PRNetAddr *addr) +{ + PRNetAddr tmp_ipv4addr; + const PRNetAddr *tmp_addrp; + PRFileDesc *lo = fd->lower; + + if (PR_AF_INET6 != addr->raw.family) { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return PR_FAILURE; + } + if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) || + PR_IsNetAddrType(addr, PR_IpAddrAny)) { + _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr); + tmp_addrp = &tmp_ipv4addr; + } else { + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0); + return PR_FAILURE; + } + return((lo->methods->bind)(lo,tmp_addrp)); +} + +static PRStatus PR_CALLBACK Ipv6ToIpv4SocketConnect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRNetAddr tmp_ipv4addr; + const PRNetAddr *tmp_addrp; + + if (PR_AF_INET6 != addr->raw.family) { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return PR_FAILURE; + } + if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) || + PR_IsNetAddrType(addr, PR_IpAddrLoopback)) { + _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr); + tmp_addrp = &tmp_ipv4addr; + } else { + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0); + return PR_FAILURE; + } + return (fd->lower->methods->connect)(fd->lower, tmp_addrp, timeout); +} + +static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketSendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRNetAddr tmp_ipv4addr; + const PRNetAddr *tmp_addrp; + + if (PR_AF_INET6 != addr->raw.family) { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return PR_FAILURE; + } + if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped) || + PR_IsNetAddrType(addr, PR_IpAddrLoopback)) { + _PR_ConvertToIpv4NetAddr(addr, &tmp_ipv4addr); + tmp_addrp = &tmp_ipv4addr; + } else { + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, 0); + return PR_FAILURE; + } + return (fd->lower->methods->sendto)( + fd->lower, buf, amount, flags, tmp_addrp, timeout); +} + +static PRFileDesc* PR_CALLBACK Ipv6ToIpv4SocketAccept ( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRStatus rv; + PRFileDesc *newfd; + PRFileDesc *newstack; + PRNetAddr tmp_ipv4addr; + PRNetAddr *addrlower = NULL; + + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + newstack = PR_NEW(PRFileDesc); + if (NULL == newstack) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + *newstack = *fd; /* make a copy of the accepting layer */ + + if (addr) { + addrlower = &tmp_ipv4addr; + } + newfd = (fd->lower->methods->accept)(fd->lower, addrlower, timeout); + if (NULL == newfd) + { + PR_DELETE(newstack); + return NULL; + } + if (addr) { + _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, addr); + } + + rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack); + PR_ASSERT(PR_SUCCESS == rv); + return newfd; /* that's it */ +} + +static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketAcceptRead(PRFileDesc *sd, + PRFileDesc **nd, PRNetAddr **ipv6_raddr, void *buf, PRInt32 amount, + PRIntervalTime timeout) +{ + PRInt32 nbytes; + PRStatus rv; + PRNetAddr tmp_ipv4addr; + PRFileDesc *newstack; + + PR_ASSERT(sd != NULL); + PR_ASSERT(sd->lower != NULL); + + newstack = PR_NEW(PRFileDesc); + if (NULL == newstack) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + *newstack = *sd; /* make a copy of the accepting layer */ + + nbytes = sd->lower->methods->acceptread( + sd->lower, nd, ipv6_raddr, buf, amount, timeout); + if (-1 == nbytes) + { + PR_DELETE(newstack); + return nbytes; + } + tmp_ipv4addr = **ipv6_raddr; /* copy */ + _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, *ipv6_raddr); + + /* this PR_PushIOLayer call cannot fail */ + rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack); + PR_ASSERT(PR_SUCCESS == rv); + return nbytes; +} + +static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetName(PRFileDesc *fd, + PRNetAddr *ipv6addr) +{ + PRStatus result; + PRNetAddr tmp_ipv4addr; + + result = (fd->lower->methods->getsockname)(fd->lower, &tmp_ipv4addr); + if (PR_SUCCESS == result) { + _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr); + PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE); + } + return result; +} + +static PRStatus PR_CALLBACK Ipv6ToIpv4SocketGetPeerName(PRFileDesc *fd, + PRNetAddr *ipv6addr) +{ + PRStatus result; + PRNetAddr tmp_ipv4addr; + + result = (fd->lower->methods->getpeername)(fd->lower, &tmp_ipv4addr); + if (PR_SUCCESS == result) { + _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr); + PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE); + } + return result; +} + +static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketRecvFrom(PRFileDesc *fd, void *buf, + PRInt32 amount, PRIntn flags, PRNetAddr *ipv6addr, + PRIntervalTime timeout) +{ + PRNetAddr tmp_ipv4addr; + PRInt32 result; + + result = (fd->lower->methods->recvfrom)( + fd->lower, buf, amount, flags, &tmp_ipv4addr, timeout); + if (-1 != result) { + _PR_ConvertToIpv6NetAddr(&tmp_ipv4addr, ipv6addr); + PR_ASSERT(IsValidNetAddr(ipv6addr) == PR_TRUE); + } + return result; +} + +#if defined(_PR_INET6_PROBE) +static PRBool ipv6_is_present; +PR_EXTERN(PRBool) _pr_test_ipv6_socket(void); + +#if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) +extern PRStatus _pr_find_getipnodebyname(void); +#endif + +#if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO) +extern PRStatus _pr_find_getaddrinfo(void); +#endif + +static PRBool +_pr_probe_ipv6_presence(void) +{ +#if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + if (_pr_find_getipnodebyname() != PR_SUCCESS) { + return PR_FALSE; + } +#endif + +#if !defined(_PR_INET6) && defined(_PR_HAVE_GETADDRINFO) + if (_pr_find_getaddrinfo() != PR_SUCCESS) { + return PR_FALSE; + } +#endif + + return _pr_test_ipv6_socket(); +} +#endif /* _PR_INET6_PROBE */ + +static PRCallOnceType _pr_init_ipv6_once; + +static PRStatus PR_CALLBACK _pr_init_ipv6(void) +{ + const PRIOMethods *stubMethods; + +#if defined(_PR_INET6_PROBE) + ipv6_is_present = _pr_probe_ipv6_presence(); + if (ipv6_is_present) { + return PR_SUCCESS; + } +#endif + + _pr_ipv6_to_ipv4_id = PR_GetUniqueIdentity("Ipv6_to_Ipv4 layer"); + PR_ASSERT(PR_INVALID_IO_LAYER != _pr_ipv6_to_ipv4_id); + + stubMethods = PR_GetDefaultIOMethods(); + + ipv6_to_v4_tcpMethods = *stubMethods; /* first get the entire batch */ + /* then override the ones we care about */ + ipv6_to_v4_tcpMethods.connect = Ipv6ToIpv4SocketConnect; + ipv6_to_v4_tcpMethods.bind = Ipv6ToIpv4SocketBind; + ipv6_to_v4_tcpMethods.accept = Ipv6ToIpv4SocketAccept; + ipv6_to_v4_tcpMethods.acceptread = Ipv6ToIpv4SocketAcceptRead; + ipv6_to_v4_tcpMethods.getsockname = Ipv6ToIpv4SocketGetName; + ipv6_to_v4_tcpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName; + /* + ipv6_to_v4_tcpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption; + ipv6_to_v4_tcpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption; + */ + ipv6_to_v4_udpMethods = *stubMethods; /* first get the entire batch */ + /* then override the ones we care about */ + ipv6_to_v4_udpMethods.connect = Ipv6ToIpv4SocketConnect; + ipv6_to_v4_udpMethods.bind = Ipv6ToIpv4SocketBind; + ipv6_to_v4_udpMethods.sendto = Ipv6ToIpv4SocketSendTo; + ipv6_to_v4_udpMethods.recvfrom = Ipv6ToIpv4SocketRecvFrom; + ipv6_to_v4_udpMethods.getsockname = Ipv6ToIpv4SocketGetName; + ipv6_to_v4_udpMethods.getpeername = Ipv6ToIpv4SocketGetPeerName; + /* + ipv6_to_v4_udpMethods.getsocketoption = Ipv6ToIpv4GetSocketOption; + ipv6_to_v4_udpMethods.setsocketoption = Ipv6ToIpv4SetSocketOption; + */ + return PR_SUCCESS; +} + +#if defined(_PR_INET6_PROBE) +PRBool _pr_ipv6_is_present(void) +{ + if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS) { + return PR_FALSE; + } + return ipv6_is_present; +} +#endif + +PR_IMPLEMENT(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd) +{ + PRFileDesc *ipv6_fd = NULL; + + if (PR_CallOnce(&_pr_init_ipv6_once, _pr_init_ipv6) != PR_SUCCESS) { + return PR_FAILURE; + } + + /* + * For platforms with no support for IPv6 + * create layered socket for IPv4-mapped IPv6 addresses + */ + if (fd->methods->file_type == PR_DESC_SOCKET_TCP) + ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id, + &ipv6_to_v4_tcpMethods); + else + ipv6_fd = PR_CreateIOLayerStub(_pr_ipv6_to_ipv4_id, + &ipv6_to_v4_udpMethods); + if (NULL == ipv6_fd) { + goto errorExit; + } + ipv6_fd->secret = NULL; + + if (PR_PushIOLayer(fd, PR_TOP_IO_LAYER, ipv6_fd) == PR_FAILURE) { + goto errorExit; + } + + return PR_SUCCESS; +errorExit: + + if (ipv6_fd) { + ipv6_fd->dtor(ipv6_fd); + } + return PR_FAILURE; +} + +#endif /* !defined(_PR_INET6) || defined(_PR_INET6_PROBE) */ diff --git a/nsprpub/pr/src/io/prlayer.c b/nsprpub/pr/src/io/prlayer.c new file mode 100644 index 0000000000..1e63b7b723 --- /dev/null +++ b/nsprpub/pr/src/io/prlayer.c @@ -0,0 +1,785 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prlayer.c +** Description: Routines for handling pushable protocol modules on sockets. +*/ + +#include "primpl.h" +#include "prerror.h" +#include "prmem.h" +#include "prlock.h" +#include "prlog.h" +#include "prio.h" + +#include <string.h> /* for memset() */ +static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack); + +void PR_CALLBACK pl_FDDestructor(PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + if (NULL != fd->lower) { + fd->lower->higher = fd->higher; + } + if (NULL != fd->higher) { + fd->higher->lower = fd->lower; + } + PR_DELETE(fd); +} + +/* +** Default methods that just call down to the next fd. +*/ +static PRStatus PR_CALLBACK pl_TopClose (PRFileDesc *fd) +{ + PRFileDesc *top, *lower; + PRStatus rv; + + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + PR_ASSERT(fd->secret == NULL); + PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED); + + if (PR_IO_LAYER_HEAD == fd->identity) { + /* + * new style stack; close all the layers, before deleting the + * stack head + */ + rv = fd->lower->methods->close(fd->lower); + _PR_DestroyIOLayer(fd); + return rv; + } + if ((fd->higher) && (PR_IO_LAYER_HEAD == fd->higher->identity)) { + /* + * lower layers of new style stack + */ + lower = fd->lower; + /* + * pop and cleanup current layer + */ + top = PR_PopIOLayer(fd->higher, PR_TOP_IO_LAYER); + top->dtor(top); + /* + * then call lower layer + */ + return (lower->methods->close(lower)); + } else { + /* old style stack */ + top = PR_PopIOLayer(fd, PR_TOP_IO_LAYER); + top->dtor(top); + return (fd->methods->close)(fd); + } +} + +static PRInt32 PR_CALLBACK pl_DefRead (PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->read)(fd->lower, buf, amount); +} + +static PRInt32 PR_CALLBACK pl_DefWrite ( + PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->write)(fd->lower, buf, amount); +} + +static PRInt32 PR_CALLBACK pl_DefAvailable (PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->available)(fd->lower); +} + +static PRInt64 PR_CALLBACK pl_DefAvailable64 (PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->available64)(fd->lower); +} + +static PRStatus PR_CALLBACK pl_DefFsync (PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->fsync)(fd->lower); +} + +static PRInt32 PR_CALLBACK pl_DefSeek ( + PRFileDesc *fd, PRInt32 offset, PRSeekWhence how) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->seek)(fd->lower, offset, how); +} + +static PRInt64 PR_CALLBACK pl_DefSeek64 ( + PRFileDesc *fd, PRInt64 offset, PRSeekWhence how) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->seek64)(fd->lower, offset, how); +} + +static PRStatus PR_CALLBACK pl_DefFileInfo (PRFileDesc *fd, PRFileInfo *info) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->fileInfo)(fd->lower, info); +} + +static PRStatus PR_CALLBACK pl_DefFileInfo64 (PRFileDesc *fd, PRFileInfo64 *info) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->fileInfo64)(fd->lower, info); +} + +static PRInt32 PR_CALLBACK pl_DefWritev (PRFileDesc *fd, const PRIOVec *iov, + PRInt32 size, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->writev)(fd->lower, iov, size, timeout); +} + +static PRStatus PR_CALLBACK pl_DefConnect ( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->connect)(fd->lower, addr, timeout); +} + +static PRStatus PR_CALLBACK pl_DefConnectcontinue ( + PRFileDesc *fd, PRInt16 out_flags) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->connectcontinue)(fd->lower, out_flags); +} + +static PRFileDesc* PR_CALLBACK pl_TopAccept ( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRStatus rv; + PRFileDesc *newfd, *layer = fd; + PRFileDesc *newstack; + PRBool newstyle_stack = PR_FALSE; + + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + /* test for new style stack */ + while (NULL != layer->higher) { + layer = layer->higher; + } + newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE; + newstack = PR_NEW(PRFileDesc); + if (NULL == newstack) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + *newstack = *fd; /* make a copy of the accepting layer */ + + newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout); + if (NULL == newfd) + { + PR_DELETE(newstack); + return NULL; + } + + if (newstyle_stack) + { + newstack->lower = newfd; + newfd->higher = newstack; + return newstack; + } + /* this PR_PushIOLayer call cannot fail */ + rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack); + PR_ASSERT(PR_SUCCESS == rv); + return newfd; /* that's it */ +} + +static PRStatus PR_CALLBACK pl_DefBind (PRFileDesc *fd, const PRNetAddr *addr) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->bind)(fd->lower, addr); +} + +static PRStatus PR_CALLBACK pl_DefListen (PRFileDesc *fd, PRIntn backlog) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->listen)(fd->lower, backlog); +} + +static PRStatus PR_CALLBACK pl_DefShutdown (PRFileDesc *fd, PRIntn how) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->shutdown)(fd->lower, how); +} + +static PRInt32 PR_CALLBACK pl_DefRecv ( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->recv)( + fd->lower, buf, amount, flags, timeout); +} + +static PRInt32 PR_CALLBACK pl_DefSend ( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout); +} + +static PRInt32 PR_CALLBACK pl_DefRecvfrom ( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->recvfrom)( + fd->lower, buf, amount, flags, addr, timeout); +} + +static PRInt32 PR_CALLBACK pl_DefSendto ( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->sendto)( + fd->lower, buf, amount, flags, addr, timeout); +} + +static PRInt16 PR_CALLBACK pl_DefPoll ( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags); +} + +static PRInt32 PR_CALLBACK pl_DefAcceptread ( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, + PRInt32 amount, PRIntervalTime t) +{ + PRInt32 nbytes; + PRStatus rv; + PRFileDesc *newstack; + PRFileDesc *layer = sd; + PRBool newstyle_stack = PR_FALSE; + + PR_ASSERT(sd != NULL); + PR_ASSERT(sd->lower != NULL); + + /* test for new style stack */ + while (NULL != layer->higher) { + layer = layer->higher; + } + newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE; + newstack = PR_NEW(PRFileDesc); + if (NULL == newstack) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + *newstack = *sd; /* make a copy of the accepting layer */ + + nbytes = sd->lower->methods->acceptread( + sd->lower, nd, raddr, buf, amount, t); + if (-1 == nbytes) + { + PR_DELETE(newstack); + return nbytes; + } + if (newstyle_stack) { + newstack->lower = *nd; + (*nd)->higher = newstack; + *nd = newstack; + return nbytes; + } + /* this PR_PushIOLayer call cannot fail */ + rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack); + PR_ASSERT(PR_SUCCESS == rv); + return nbytes; +} + +static PRInt32 PR_CALLBACK pl_DefTransmitfile ( + PRFileDesc *sd, PRFileDesc *fd, const void *headers, PRInt32 hlen, + PRTransmitFileFlags flags, PRIntervalTime t) +{ + PR_ASSERT(sd != NULL); + PR_ASSERT(sd->lower != NULL); + + return sd->lower->methods->transmitfile( + sd->lower, fd, headers, hlen, flags, t); +} + +static PRStatus PR_CALLBACK pl_DefGetsockname (PRFileDesc *fd, PRNetAddr *addr) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getsockname)(fd->lower, addr); +} + +static PRStatus PR_CALLBACK pl_DefGetpeername (PRFileDesc *fd, PRNetAddr *addr) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getpeername)(fd->lower, addr); +} + +static PRStatus PR_CALLBACK pl_DefGetsocketoption ( + PRFileDesc *fd, PRSocketOptionData *data) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getsocketoption)(fd->lower, data); +} + +static PRStatus PR_CALLBACK pl_DefSetsocketoption ( + PRFileDesc *fd, const PRSocketOptionData *data) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->setsocketoption)(fd->lower, data); +} + +static PRInt32 PR_CALLBACK pl_DefSendfile ( + PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + PR_ASSERT(sd != NULL); + PR_ASSERT(sd->lower != NULL); + + return sd->lower->methods->sendfile( + sd->lower, sfd, flags, timeout); +} + +/* Methods for the top of the stack. Just call down to the next fd. */ +static PRIOMethods pl_methods = { + PR_DESC_LAYERED, + pl_TopClose, + pl_DefRead, + pl_DefWrite, + pl_DefAvailable, + pl_DefAvailable64, + pl_DefFsync, + pl_DefSeek, + pl_DefSeek64, + pl_DefFileInfo, + pl_DefFileInfo64, + pl_DefWritev, + pl_DefConnect, + pl_TopAccept, + pl_DefBind, + pl_DefListen, + pl_DefShutdown, + pl_DefRecv, + pl_DefSend, + pl_DefRecvfrom, + pl_DefSendto, + pl_DefPoll, + pl_DefAcceptread, + pl_DefTransmitfile, + pl_DefGetsockname, + pl_DefGetpeername, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + pl_DefGetsocketoption, + pl_DefSetsocketoption, + pl_DefSendfile, + pl_DefConnectcontinue, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +PR_IMPLEMENT(const PRIOMethods*) PR_GetDefaultIOMethods(void) +{ + return &pl_methods; +} /* PR_GetDefaultIOMethods */ + +PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub( + PRDescIdentity ident, const PRIOMethods *methods) +{ + PRFileDesc *fd = NULL; + PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident)); + if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + else + { + fd = PR_NEWZAP(PRFileDesc); + if (NULL == fd) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->methods = methods; + fd->dtor = pl_FDDestructor; + fd->identity = ident; + } + } + return fd; +} /* PR_CreateIOLayerStub */ + +/* + * PR_CreateIOLayer + * Create a new style stack, where the stack top is a dummy header. + * Unlike the old style stacks, the contents of the stack head + * are not modified when a layer is pushed onto or popped from a new + * style stack. + */ + +PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayer(PRFileDesc *top) +{ + PRFileDesc *fd = NULL; + + fd = PR_NEWZAP(PRFileDesc); + if (NULL == fd) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->methods = &pl_methods; + fd->dtor = pl_FDDestructor; + fd->identity = PR_IO_LAYER_HEAD; + fd->higher = NULL; + fd->lower = top; + top->higher = fd; + top->lower = NULL; + } + return fd; +} /* PR_CreateIOLayer */ + +/* + * _PR_DestroyIOLayer + * Delete the stack head of a new style stack. + */ + +static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack) +{ + if (NULL == stack) { + return PR_FAILURE; + } + + PR_DELETE(stack); + return PR_SUCCESS; +} /* _PR_DestroyIOLayer */ + +PR_IMPLEMENT(PRStatus) PR_PushIOLayer( + PRFileDesc *stack, PRDescIdentity id, PRFileDesc *fd) +{ + PRFileDesc *insert = PR_GetIdentitiesLayer(stack, id); + + PR_ASSERT(fd != NULL); + PR_ASSERT(stack != NULL); + PR_ASSERT(insert != NULL); + PR_ASSERT(PR_IO_LAYER_HEAD != id); + if ((NULL == stack) || (NULL == fd) || (NULL == insert)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + if (stack == insert) + { + /* going on top of the stack */ + /* old-style stack */ + PRFileDesc copy = *stack; + *stack = *fd; + *fd = copy; + fd->higher = stack; + if (fd->lower) + { + PR_ASSERT(fd->lower->higher == stack); + fd->lower->higher = fd; + } + stack->lower = fd; + stack->higher = NULL; + } else { + /* + * going somewhere in the middle of the stack for both old and new + * style stacks, or going on top of stack for new style stack + */ + fd->lower = insert; + fd->higher = insert->higher; + + insert->higher->lower = fd; + insert->higher = fd; + } + + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id) +{ + PRFileDesc *extract = PR_GetIdentitiesLayer(stack, id); + + PR_ASSERT(0 != id); + PR_ASSERT(NULL != stack); + PR_ASSERT(NULL != extract); + if ((NULL == stack) || (0 == id) || (NULL == extract)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + + if (extract == stack) { + /* popping top layer of the stack */ + /* old style stack */ + PRFileDesc copy = *stack; + extract = stack->lower; + *stack = *extract; + *extract = copy; + stack->higher = NULL; + if (stack->lower) { + PR_ASSERT(stack->lower->higher == extract); + stack->lower->higher = stack; + } + } else if ((PR_IO_LAYER_HEAD == stack->identity) && + (extract == stack->lower) && (extract->lower == NULL)) { + /* + * new style stack + * popping the only layer in the stack; delete the stack too + */ + stack->lower = NULL; + _PR_DestroyIOLayer(stack); + } else { + /* for both kinds of stacks */ + extract->lower->higher = extract->higher; + extract->higher->lower = extract->lower; + } + extract->higher = extract->lower = NULL; + return extract; +} /* PR_PopIOLayer */ + +#define ID_CACHE_INCREMENT 16 +typedef struct _PRIdentity_cache +{ + PRLock *ml; + char **name; + PRIntn length; + PRDescIdentity ident; +} _PRIdentity_cache; + +static _PRIdentity_cache identity_cache; + +PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name) +{ + PRDescIdentity identity, length; + char **names = NULL, *name = NULL, **old = NULL; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident); + + if (NULL != layer_name) + { + name = (char*)PR_Malloc(strlen(layer_name) + 1); + if (NULL == name) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_INVALID_IO_LAYER; + } + strcpy(name, layer_name); + } + + /* this initial code runs unsafe */ +retry: + PR_ASSERT(NULL == names); + /* + * In the initial round, both identity_cache.ident and + * identity_cache.length are 0, so (identity_cache.ident + 1) is greater + * than length. In later rounds, identity_cache.ident is always less + * than length, so (identity_cache.ident + 1) can be equal to but cannot + * be greater than length. + */ + length = identity_cache.length; + if ((identity_cache.ident + 1) >= length) + { + length += ID_CACHE_INCREMENT; + names = (char**)PR_CALLOC(length * sizeof(char*)); + if (NULL == names) + { + if (NULL != name) { + PR_DELETE(name); + } + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_INVALID_IO_LAYER; + } + } + + /* now we get serious about thread safety */ + PR_Lock(identity_cache.ml); + PR_ASSERT(identity_cache.length == 0 || + identity_cache.ident < identity_cache.length); + identity = identity_cache.ident + 1; + if (identity >= identity_cache.length) /* there's no room */ + { + /* we have to do something - hopefully it's already done */ + if ((NULL != names) && (identity < length)) + { + /* what we did is still okay */ + if (identity_cache.length != 0) { + memcpy( + names, identity_cache.name, + identity_cache.length * sizeof(char*)); + } + old = identity_cache.name; + identity_cache.name = names; + identity_cache.length = length; + names = NULL; + } + else + { + PR_Unlock(identity_cache.ml); + if (NULL != names) { + PR_DELETE(names); + } + goto retry; + } + } + if (NULL != name) /* there's a name to be stored */ + { + identity_cache.name[identity] = name; + } + identity_cache.ident = identity; + PR_ASSERT(identity_cache.ident < identity_cache.length); + PR_Unlock(identity_cache.ml); + + if (NULL != old) { + PR_DELETE(old); + } + if (NULL != names) { + PR_DELETE(names); + } + + return identity; +} /* PR_GetUniqueIdentity */ + +PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident) +{ + const char *rv = NULL; + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if ((PR_TOP_IO_LAYER != ident) && (ident >= 0)) { + PR_Lock(identity_cache.ml); + PR_ASSERT(ident <= identity_cache.ident); + rv = (ident > identity_cache.ident) ? NULL : identity_cache.name[ident]; + PR_Unlock(identity_cache.ml); + } + + return rv; +} /* PR_GetNameForIdentity */ + +PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd) +{ + PR_ASSERT(NULL != fd); + if (PR_IO_LAYER_HEAD == fd->identity) { + PR_ASSERT(NULL != fd->lower); + return fd->lower->identity; + } + return fd->identity; +} /* PR_GetLayersIdentity */ + +PR_IMPLEMENT(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id) +{ + PRFileDesc *layer = fd; + + if (PR_TOP_IO_LAYER == id) { + if (PR_IO_LAYER_HEAD == fd->identity) { + return fd->lower; + } + return fd; + } + + for (layer = fd; layer != NULL; layer = layer->lower) + { + if (id == layer->identity) { + return layer; + } + } + for (layer = fd; layer != NULL; layer = layer->higher) + { + if (id == layer->identity) { + return layer; + } + } + return NULL; +} /* PR_GetIdentitiesLayer */ + +void _PR_InitLayerCache(void) +{ + memset(&identity_cache, 0, sizeof(identity_cache)); + identity_cache.ml = PR_NewLock(); + PR_ASSERT(NULL != identity_cache.ml); +} /* _PR_InitLayerCache */ + +void _PR_CleanupLayerCache(void) +{ + if (identity_cache.ml) + { + PR_DestroyLock(identity_cache.ml); + identity_cache.ml = NULL; + } + + if (identity_cache.name) + { + PRDescIdentity ident; + + for (ident = 0; ident <= identity_cache.ident; ident++) { + PR_DELETE(identity_cache.name[ident]); + } + + PR_DELETE(identity_cache.name); + } +} /* _PR_CleanupLayerCache */ + +/* prlayer.c */ diff --git a/nsprpub/pr/src/io/prlog.c b/nsprpub/pr/src/io/prlog.c new file mode 100644 index 0000000000..52bd6abc5d --- /dev/null +++ b/nsprpub/pr/src/io/prlog.c @@ -0,0 +1,572 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include "prenv.h" +#include "prprf.h" +#include <string.h> +#ifdef ANDROID +#include <android/log.h> +#endif + +/* + * Lock used to lock the log. + * + * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may + * contain assertions. We have to avoid assertions in _PR_LOCK_LOG + * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG. + * This can lead to infinite recursion. + */ +static PRLock *_pr_logLock; +#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) +#define _PR_LOCK_LOG() PR_Lock(_pr_logLock); +#define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock); +#elif defined(_PR_GLOBAL_THREADS_ONLY) +#define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock) +#define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); } +#else + +#define _PR_LOCK_LOG() \ +{ \ + PRIntn _is; \ + PRThread *_me = _PR_MD_CURRENT_THREAD(); \ + if (!_PR_IS_NATIVE_THREAD(_me)) \ + _PR_INTSOFF(_is); \ + _PR_LOCK_LOCK(_pr_logLock) + +#define _PR_UNLOCK_LOG() \ + _PR_LOCK_UNLOCK(_pr_logLock); \ + PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \ + if (!_PR_IS_NATIVE_THREAD(_me)) \ + _PR_INTSON(_is); \ +} + +#endif + +#if defined(XP_PC) +#define strcasecmp stricmp +#endif + +/* + * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE, + * because every asynchronous file io operation leads to a fiber context + * switch. So we define _PUT_LOG as fputs (from stdio.h). A side + * benefit is that fputs handles the LF->CRLF translation. This + * code can also be used on other platforms with file stream io. + */ +#if defined(WIN32) || defined(XP_OS2) +#define _PR_USE_STDIO_FOR_LOGGING +#endif + +/* +** Coerce Win32 log output to use OutputDebugString() when +** NSPR_LOG_FILE is set to "WinDebug". +*/ +#if defined(XP_PC) +#define WIN32_DEBUG_FILE (FILE*)-2 +#endif + +#ifdef WINCE +static void OutputDebugStringA(const char* msg) { + int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0); + WCHAR *wMsg = (WCHAR *)PR_Malloc(len * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len); + OutputDebugStringW(wMsg); + PR_Free(wMsg); +} +#endif + +/* Macros used to reduce #ifdef pollution */ + +#if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC) +#define _PUT_LOG(fd, buf, nb) \ + PR_BEGIN_MACRO \ + if (logFile == WIN32_DEBUG_FILE) { \ + char savebyte = buf[nb]; \ + buf[nb] = '\0'; \ + OutputDebugStringA(buf); \ + buf[nb] = savebyte; \ + } else { \ + fwrite(buf, 1, nb, fd); \ + fflush(fd); \ + } \ + PR_END_MACRO +#elif defined(_PR_USE_STDIO_FOR_LOGGING) +#define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);} +#elif defined(ANDROID) +#define _PUT_LOG(fd, buf, nb) \ + PR_BEGIN_MACRO \ + if (fd == _pr_stderr) { \ + char savebyte = buf[nb]; \ + buf[nb] = '\0'; \ + __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \ + buf[nb] = savebyte; \ + } else { \ + PR_Write(fd, buf, nb); \ + } \ + PR_END_MACRO +#elif defined(_PR_PTHREADS) +#define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb) +#else +#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb) +#endif + +/************************************************************************/ + +static PRLogModuleInfo *logModules; + +static char *logBuf = NULL; +static char *logp; +static char *logEndp; +#ifdef _PR_USE_STDIO_FOR_LOGGING +static FILE *logFile = NULL; +#else +static PRFileDesc *logFile = 0; +#endif +static PRBool outputTimeStamp = PR_FALSE; +static PRBool appendToLog = PR_FALSE; + +#define LINE_BUF_SIZE 512 +#define DEFAULT_BUF_SIZE 16384 + +#ifdef _PR_NEED_STRCASECMP + +/* + * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms + * such as NCR and Unixware. Linking with both libc and libucb + * may cause some problem, so I just provide our own implementation + * of strcasecmp here. + */ + +static const unsigned char uc[] = +{ + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '{', '|', '}', '~', '\177' +}; + +PRIntn strcasecmp(const char *a, const char *b) +{ + const unsigned char *ua = (const unsigned char *)a; + const unsigned char *ub = (const unsigned char *)b; + + if( ((const char *)0 == a) || (const char *)0 == b ) { + return (PRIntn)(a-b); + } + + while( (uc[*ua] == uc[*ub]) && ('\0' != *a) ) + { + a++; + ua++; + ub++; + } + + return (PRIntn)(uc[*ua] - uc[*ub]); +} + +#endif /* _PR_NEED_STRCASECMP */ + +void _PR_InitLog(void) +{ + char *ev; + + _pr_logLock = PR_NewLock(); + + ev = PR_GetEnv("NSPR_LOG_MODULES"); + if (ev && ev[0]) { + char module[64]; /* Security-Critical: If you change this + * size, you must also change the sscanf + * format string to be size-1. + */ + PRBool isSync = PR_FALSE; + PRIntn evlen = strlen(ev), pos = 0; + PRInt32 bufSize = DEFAULT_BUF_SIZE; + while (pos < evlen) { + PRIntn level = 1, count = 0, delta = 0; + count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n", + module, &delta, &level, &delta); + pos += delta; + if (count == 0) { + break; + } + + /* + ** If count == 2, then we got module and level. If count + ** == 1, then level defaults to 1 (module enabled). + */ + if (strcasecmp(module, "sync") == 0) { + isSync = PR_TRUE; + } else if (strcasecmp(module, "bufsize") == 0) { + if (level >= LINE_BUF_SIZE) { + bufSize = level; + } + } else if (strcasecmp(module, "timestamp") == 0) { + outputTimeStamp = PR_TRUE; + } else if (strcasecmp(module, "append") == 0) { + appendToLog = PR_TRUE; + } else { + PRLogModuleInfo *lm = logModules; + PRBool skip_modcheck = + (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE; + + while (lm != NULL) { + if (skip_modcheck) { + lm -> level = (PRLogModuleLevel)level; + } + else if (strcasecmp(module, lm->name) == 0) { + lm->level = (PRLogModuleLevel)level; + break; + } + lm = lm->next; + } + } + /*found:*/ + count = sscanf(&ev[pos], " , %n", &delta); + pos += delta; + if (count == EOF) { + break; + } + } + PR_SetLogBuffering(isSync ? 0 : bufSize); + + ev = PR_GetEnvSecure("NSPR_LOG_FILE"); + if (ev && ev[0]) { + if (!PR_SetLogFile(ev)) { +#ifdef XP_PC + char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev); + if (str) { + OutputDebugStringA(str); + PR_smprintf_free(str); + } +#else + fprintf(stderr, "Unable to create nspr log file '%s'\n", ev); +#endif + } + } else { +#ifdef _PR_USE_STDIO_FOR_LOGGING + logFile = stderr; +#else + logFile = _pr_stderr; +#endif + } + } +} + +void _PR_LogCleanup(void) +{ + PRLogModuleInfo *lm = logModules; + + PR_LogFlush(); + +#ifdef _PR_USE_STDIO_FOR_LOGGING + if (logFile + && logFile != stdout + && logFile != stderr +#ifdef XP_PC + && logFile != WIN32_DEBUG_FILE +#endif + ) { + fclose(logFile); + } +#else + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { + PR_Close(logFile); + } +#endif + logFile = NULL; + + if (logBuf) { + PR_DELETE(logBuf); + } + + while (lm != NULL) { + PRLogModuleInfo *next = lm->next; + free((/*const*/ char *)lm->name); + PR_Free(lm); + lm = next; + } + logModules = NULL; + + if (_pr_logLock) { + PR_DestroyLock(_pr_logLock); + _pr_logLock = NULL; + } +} + +static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm ) +{ + char *ev; + + ev = PR_GetEnv("NSPR_LOG_MODULES"); + if (ev && ev[0]) { + char module[64]; /* Security-Critical: If you change this + * size, you must also change the sscanf + * format string to be size-1. + */ + PRIntn evlen = strlen(ev), pos = 0; + while (pos < evlen) { + PRIntn level = 1, count = 0, delta = 0; + + count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n", + module, &delta, &level, &delta); + pos += delta; + if (count == 0) { + break; + } + + /* + ** If count == 2, then we got module and level. If count + ** == 1, then level defaults to 1 (module enabled). + */ + if (lm != NULL) + { + if ((strcasecmp(module, "all") == 0) + || (strcasecmp(module, lm->name) == 0)) + { + lm->level = (PRLogModuleLevel)level; + } + } + count = sscanf(&ev[pos], " , %n", &delta); + pos += delta; + if (count == EOF) { + break; + } + } + } +} /* end _PR_SetLogModuleLevel() */ + +PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name) +{ + PRLogModuleInfo *lm; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + lm = PR_NEWZAP(PRLogModuleInfo); + if (lm) { + lm->name = strdup(name); + lm->level = PR_LOG_NONE; + lm->next = logModules; + logModules = lm; + _PR_SetLogModuleLevel(lm); + } + return lm; +} + +PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file) +{ +#ifdef _PR_USE_STDIO_FOR_LOGGING + FILE *newLogFile; + +#ifdef XP_PC + if ( strcmp( file, "WinDebug") == 0) + { + newLogFile = WIN32_DEBUG_FILE; + } + else +#endif + { + const char *mode = appendToLog ? "a" : "w"; + newLogFile = fopen(file, mode); + if (!newLogFile) { + return PR_FALSE; + } + +#ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */ + /* We do buffering ourselves. */ + setvbuf(newLogFile, NULL, _IONBF, 0); +#endif + } + if (logFile + && logFile != stdout + && logFile != stderr +#ifdef XP_PC + && logFile != WIN32_DEBUG_FILE +#endif + ) { + fclose(logFile); + } + logFile = newLogFile; + return PR_TRUE; +#else + PRFileDesc *newLogFile; + PRIntn flags = PR_WRONLY|PR_CREATE_FILE; + if (appendToLog) { + flags |= PR_APPEND; + } else { + flags |= PR_TRUNCATE; + } + + newLogFile = PR_Open(file, flags, 0666); + if (newLogFile) { + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { + PR_Close(logFile); + } + logFile = newLogFile; + } + return (PRBool) (newLogFile != 0); +#endif /* _PR_USE_STDIO_FOR_LOGGING */ +} + +PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) +{ + PR_LogFlush(); + + if (logBuf) { + PR_DELETE(logBuf); + } + + if (buffer_size >= LINE_BUF_SIZE) { + logp = logBuf = (char*) PR_MALLOC(buffer_size); + logEndp = logp + buffer_size; + } +} + +PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...) +{ + va_list ap; + char line[LINE_BUF_SIZE]; + char *line_long = NULL; + PRUint32 nb_tid = 0, nb; + PRThread *me; + PRExplodedTime now; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (!logFile) { + return; + } + + if (outputTimeStamp) { + PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now); + nb_tid = PR_snprintf(line, sizeof(line)-1, + "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ", + now.tm_year, now.tm_month + 1, now.tm_mday, + now.tm_hour, now.tm_min, now.tm_sec, + now.tm_usec); + } + + me = PR_GetCurrentThread(); + nb_tid += PR_snprintf(line+nb_tid, sizeof(line)-nb_tid-1, "%ld[%p]: ", +#if defined(_PR_BTHREADS) + me, me); +#else + me ? me->id : 0L, me); +#endif + + va_start(ap, fmt); + nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap); + va_end(ap); + + /* + * Check if we might have run out of buffer space (in case we have a + * long line), and malloc a buffer just this once. + */ + if (nb == sizeof(line)-2) { + va_start(ap, fmt); + line_long = PR_vsmprintf(fmt, ap); + va_end(ap); + /* If this failed, we'll fall back to writing the truncated line. */ + } + + if (line_long) { + nb = strlen(line_long); + _PR_LOCK_LOG(); + if (logBuf != 0) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + /* + * Write out the thread id (with an optional timestamp) and the + * malloc'ed buffer. + */ + _PUT_LOG(logFile, line, nb_tid); + _PUT_LOG(logFile, line_long, nb); + /* Ensure there is a trailing newline. */ + if (!nb || (line_long[nb-1] != '\n')) { + char eol[2]; + eol[0] = '\n'; + eol[1] = '\0'; + _PUT_LOG(logFile, eol, 1); + } + _PR_UNLOCK_LOG(); + PR_smprintf_free(line_long); + } else { + /* Ensure there is a trailing newline. */ + if (nb && (line[nb-1] != '\n')) { + line[nb++] = '\n'; + line[nb] = '\0'; + } + _PR_LOCK_LOG(); + if (logBuf == 0) { + _PUT_LOG(logFile, line, nb); + } else { + /* If nb can't fit into logBuf, write out logBuf first. */ + if (logp + nb > logEndp) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + /* nb is guaranteed to fit into logBuf. */ + memcpy(logp, line, nb); + logp += nb; + } + _PR_UNLOCK_LOG(); + } + PR_LogFlush(); +} + +PR_IMPLEMENT(void) PR_LogFlush(void) +{ + if (logBuf && logFile) { + _PR_LOCK_LOG(); + if (logp > logBuf) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + _PR_UNLOCK_LOG(); + } +} + +PR_IMPLEMENT(void) PR_Abort(void) +{ + PR_LogPrint("Aborting"); +#ifdef ANDROID + __android_log_write(ANDROID_LOG_ERROR, "PRLog", "Aborting"); +#endif + abort(); +} + +PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln) +{ + PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln); + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); + fflush(stderr); +#ifdef WIN32 + DebugBreak(); +#elif defined(XP_OS2) + asm("int $3"); +#elif defined(ANDROID) + __android_log_assert(NULL, "PRLog", "Assertion failure: %s, at %s:%d\n", + s, file, ln); +#endif + abort(); +} diff --git a/nsprpub/pr/src/io/prmapopt.c b/nsprpub/pr/src/io/prmapopt.c new file mode 100644 index 0000000000..375a73d2a9 --- /dev/null +++ b/nsprpub/pr/src/io/prmapopt.c @@ -0,0 +1,530 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file defines _PR_MapOptionName(). The purpose of putting + * _PR_MapOptionName() in a separate file is to work around a Winsock + * header file problem on Windows NT. + * + * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order + * to use Service Pack 3 extensions), windows.h includes winsock2.h + * (instead of winsock.h), which doesn't define many socket options + * defined in winsock.h. + * + * We need the socket options defined in winsock.h. So this file + * includes winsock.h, with _WIN32_WINNT undefined. + */ + +#if defined(WINNT) || defined(__MINGW32__) +#include <winsock.h> +#endif + +/* MinGW doesn't define these in its winsock.h. */ +#ifdef __MINGW32__ +#ifndef IP_TTL +#define IP_TTL 7 +#endif +#ifndef IP_TOS +#define IP_TOS 8 +#endif +#endif + +#include "primpl.h" + +#if defined(LINUX) || defined(ANDROID) +#include <netinet/in.h> +#endif + +#ifdef DARWIN +#include <netinet/in.h> +#include <netinet/ip.h> +#endif + +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ +#endif + +#ifndef _PR_PTHREADS + +PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) +{ + PRStatus rv; + PRInt32 length; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call + */ + if (PR_SockOpt_Nonblocking == data->option) + { + data->value.non_blocking = fd->secret->nonblocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + length = sizeof(linger); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char *) &linger, &length); + if (PR_SUCCESS == rv) + { + PR_ASSERT(sizeof(linger) == length); + data->value.linger.polarity = + (linger.l_onoff) ? PR_TRUE : PR_FALSE; + data->value.linger.linger = + PR_SecondsToInterval(linger.l_linger); + } + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + case PR_SockOpt_Broadcast: + case PR_SockOpt_Reuseport: + { +#ifdef WIN32 /* Winsock */ + BOOL value; +#else + PRIntn value; +#endif + length = sizeof(value); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&value, &length); + if (PR_SUCCESS == rv) { + data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; + } + break; + } + case PR_SockOpt_McastLoopback: + { +#ifdef WIN32 /* Winsock */ + BOOL bool; +#else + PRUint8 bool; +#endif + length = sizeof(bool); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&bool, &length); + if (PR_SUCCESS == rv) { + data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE; + } + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value; + length = sizeof(value); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&value, &length); + if (PR_SUCCESS == rv) { + data->value.recv_buffer_size = value; + } + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + /* These options should really be an int (or PRIntn). */ + length = sizeof(PRUintn); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&data->value.ip_ttl, &length); + break; + } + case PR_SockOpt_McastTimeToLive: + { +#ifdef WIN32 /* Winsock */ + int ttl; +#else + PRUint8 ttl; +#endif + length = sizeof(ttl); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&ttl, &length); + if (PR_SUCCESS == rv) { + data->value.mcast_ttl = ttl; + } + break; + } +#ifdef IP_ADD_MEMBERSHIP + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + length = sizeof(mreq); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&mreq, &length); + if (PR_SUCCESS == rv) + { + data->value.add_member.mcaddr.inet.ip = + mreq.imr_multiaddr.s_addr; + data->value.add_member.ifaddr.inet.ip = + mreq.imr_interface.s_addr; + } + break; + } +#endif /* IP_ADD_MEMBERSHIP */ + case PR_SockOpt_McastInterface: + { + /* This option is a struct in_addr. */ + length = sizeof(data->value.mcast_if.inet.ip); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, + (char*)&data->value.mcast_if.inet.ip, &length); + break; + } + case PR_SockOpt_DontFrag: + { +#if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + rv = PR_FAILURE; +#else +#ifdef WIN32 /* Winsock */ + DWORD value; +#else + PRIntn value; +#endif + length = sizeof(value); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&value, &length); +#if defined(WIN32) || defined(DARWIN) + data->value.dont_fragment = value; +#else + data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0; +#endif +#endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + } + return rv; +} /* _PR_SocketGetSocketOption */ + +PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data) +{ + PRStatus rv; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt call. + */ + if (PR_SockOpt_Nonblocking == data->option) + { +#ifdef WINNT + PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE) + || (fd->secret->nonblocking == data->value.non_blocking)); + if (fd->secret->md.io_model_committed + && (fd->secret->nonblocking != data->value.non_blocking)) + { + /* + * On NT, once we have associated a socket with the io + * completion port, we can't disassociate it. So we + * can't change the nonblocking option of the socket + * afterwards. + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } +#endif + fd->secret->nonblocking = data->value.non_blocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + linger.l_onoff = data->value.linger.polarity; + linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger); + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&linger, sizeof(linger)); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + case PR_SockOpt_Broadcast: + case PR_SockOpt_Reuseport: + { +#ifdef WIN32 /* Winsock */ + BOOL value; +#else + PRIntn value; +#endif + value = (data->value.reuse_addr) ? 1 : 0; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&value, sizeof(value)); + break; + } + case PR_SockOpt_McastLoopback: + { +#ifdef WIN32 /* Winsock */ + BOOL bool; +#else + PRUint8 bool; +#endif + bool = data->value.mcast_loopback ? 1 : 0; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&bool, sizeof(bool)); + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value = data->value.recv_buffer_size; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&value, sizeof(value)); + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + /* These options should really be an int (or PRIntn). */ + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&data->value.ip_ttl, sizeof(PRUintn)); + break; + } + case PR_SockOpt_McastTimeToLive: + { +#ifdef WIN32 /* Winsock */ + int ttl; +#else + PRUint8 ttl; +#endif + ttl = data->value.mcast_ttl; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&ttl, sizeof(ttl)); + break; + } +#ifdef IP_ADD_MEMBERSHIP + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = + data->value.add_member.mcaddr.inet.ip; + mreq.imr_interface.s_addr = + data->value.add_member.ifaddr.inet.ip; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&mreq, sizeof(mreq)); + break; + } +#endif /* IP_ADD_MEMBERSHIP */ + case PR_SockOpt_McastInterface: + { + /* This option is a struct in_addr. */ + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&data->value.mcast_if.inet.ip, + sizeof(data->value.mcast_if.inet.ip)); + break; + } + case PR_SockOpt_DontFrag: + { +#if !defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + rv = PR_FAILURE; +#else +#if defined(WIN32) /* Winsock */ + DWORD value; + value = (data->value.dont_fragment) ? 1 : 0; +#elif defined(LINUX) || defined(ANDROID) + PRIntn value; + value = (data->value.dont_fragment) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; +#elif defined(DARWIN) + PRIntn value; + value = data->value.dont_fragment; +#endif + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&value, sizeof(value)); +#endif /* !(!defined(WIN32) && !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + } + return rv; +} /* _PR_SocketSetSocketOption */ + +#endif /* ! _PR_PTHREADS */ + +/* + ********************************************************************* + ********************************************************************* + ** + ** Make sure that the following is at the end of this file, + ** because we will be playing with macro redefines. + ** + ********************************************************************* + ********************************************************************* + */ + +/* + * Not every platform has all the socket options we want to + * support. Some older operating systems such as SunOS 4.1.3 + * don't have the IP multicast socket options. Win32 doesn't + * have TCP_MAXSEG. + * + * To deal with this problem, we define the missing socket + * options as _PR_NO_SUCH_SOCKOPT. _PR_MapOptionName() fails with + * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not + * available on the platform is requested. + */ + +/* + * Sanity check. SO_LINGER and TCP_NODELAY should be available + * on all platforms. Just to make sure we have included the + * appropriate header files. Then any undefined socket options + * are really missing. + */ + +#if !defined(SO_LINGER) +#error "SO_LINGER is not defined" +#endif + +#if !defined(TCP_NODELAY) +#error "TCP_NODELAY is not defined" +#endif + +/* + * Make sure the value of _PR_NO_SUCH_SOCKOPT is not + * a valid socket option. + */ +#define _PR_NO_SUCH_SOCKOPT -1 + +#ifndef SO_KEEPALIVE +#define SO_KEEPALIVE _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef SO_SNDBUF +#define SO_SNDBUF _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef SO_RCVBUF +#define SO_RCVBUF _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_MULTICAST_IF /* set/get IP multicast interface */ +#define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive */ +#define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback */ +#define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_ADD_MEMBERSHIP /* add an IP group membership */ +#define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership */ +#define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_TTL /* set/get IP Time To Live */ +#define IP_TTL _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_TOS /* set/get IP Type Of Service */ +#define IP_TOS _PR_NO_SUCH_SOCKOPT +#endif + +/* set/get IP do not fragment */ +#if defined(WIN32) +#ifndef IP_DONTFRAGMENT +#define IP_DONTFRAGMENT _PR_NO_SUCH_SOCKOPT +#endif + +#elif defined(LINUX) || defined(ANDROID) +#ifndef IP_MTU_DISCOVER +#define IP_MTU_DISCOVER _PR_NO_SUCH_SOCKOPT +#endif + +#elif defined(DARWIN) +#ifndef IP_DONTFRAG +#define IP_DONTFRAG _PR_NO_SUCH_SOCKOPT +#endif +#endif + +#ifndef TCP_NODELAY /* don't delay to coalesce data */ +#define TCP_NODELAY _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef TCP_MAXSEG /* maxumum segment size for tcp */ +#define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef SO_BROADCAST /* enable broadcast on UDP sockets */ +#define SO_BROADCAST _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef SO_REUSEPORT /* allow local address & port reuse */ +#define SO_REUSEPORT _PR_NO_SUCH_SOCKOPT +#endif + +PRStatus _PR_MapOptionName( + PRSockOption optname, PRInt32 *level, PRInt32 *name) +{ + static PRInt32 socketOptions[PR_SockOpt_Last] = + { + 0, SO_LINGER, SO_REUSEADDR, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF, + IP_TTL, IP_TOS, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, + IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP, + TCP_NODELAY, TCP_MAXSEG, SO_BROADCAST, SO_REUSEPORT, +#if defined(WIN32) + IP_DONTFRAGMENT, +#elif defined(LINUX) || defined(ANDROID) + IP_MTU_DISCOVER, +#elif defined(DARWIN) + IP_DONTFRAG, +#else + _PR_NO_SUCH_SOCKOPT, +#endif + }; + static PRInt32 socketLevels[PR_SockOpt_Last] = + { + 0, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, + IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, + IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, + IPPROTO_TCP, IPPROTO_TCP, SOL_SOCKET, SOL_SOCKET, IPPROTO_IP + }; + + if ((optname < PR_SockOpt_Linger) + || (optname >= PR_SockOpt_Last)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) + { + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + return PR_FAILURE; + } + *name = socketOptions[optname]; + *level = socketLevels[optname]; + return PR_SUCCESS; +} /* _PR_MapOptionName */ diff --git a/nsprpub/pr/src/io/prmmap.c b/nsprpub/pr/src/io/prmmap.c new file mode 100644 index 0000000000..b0adf88676 --- /dev/null +++ b/nsprpub/pr/src/io/prmmap.c @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + ********************************************************************* + * + * Memory-mapped files + * + ********************************************************************* + */ + +#include "primpl.h" + +PR_IMPLEMENT(PRFileMap *) PR_CreateFileMap( + PRFileDesc *fd, + PRInt64 size, + PRFileMapProtect prot) +{ + PRFileMap *fmap; + + PR_ASSERT(prot == PR_PROT_READONLY || prot == PR_PROT_READWRITE + || prot == PR_PROT_WRITECOPY); + fmap = PR_NEWZAP(PRFileMap); + if (NULL == fmap) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + fmap->fd = fd; + fmap->prot = prot; + if (_PR_MD_CREATE_FILE_MAP(fmap, size) == PR_SUCCESS) { + return fmap; + } + PR_DELETE(fmap); + return NULL; +} + +PR_IMPLEMENT(PRInt32) PR_GetMemMapAlignment(void) +{ + return _PR_MD_GET_MEM_MAP_ALIGNMENT(); +} + +PR_IMPLEMENT(void *) PR_MemMap( + PRFileMap *fmap, + PROffset64 offset, + PRUint32 len) +{ + return _PR_MD_MEM_MAP(fmap, offset, len); +} + +PR_IMPLEMENT(PRStatus) PR_MemUnmap(void *addr, PRUint32 len) +{ + return _PR_MD_MEM_UNMAP(addr, len); +} + +PR_IMPLEMENT(PRStatus) PR_CloseFileMap(PRFileMap *fmap) +{ + return _PR_MD_CLOSE_FILE_MAP(fmap); +} + +PR_IMPLEMENT(PRStatus) PR_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len) +{ + return _PR_MD_SYNC_MEM_MAP(fd, addr, len); +} diff --git a/nsprpub/pr/src/io/prmwait.c b/nsprpub/pr/src/io/prmwait.c new file mode 100644 index 0000000000..62e35ab6fb --- /dev/null +++ b/nsprpub/pr/src/io/prmwait.c @@ -0,0 +1,1534 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include "pprmwait.h" + +#define _MW_REHASH_MAX 11 + +static PRLock *mw_lock = NULL; +static _PRGlobalState *mw_state = NULL; + +static PRIntervalTime max_polling_interval; + +#ifdef WINNT + +typedef struct TimerEvent { + PRIntervalTime absolute; + void (*func)(void *); + void *arg; + LONG ref_count; + PRCList links; +} TimerEvent; + +#define TIMER_EVENT_PTR(_qp) \ + ((TimerEvent *) ((char *) (_qp) - offsetof(TimerEvent, links))) + +struct { + PRLock *ml; + PRCondVar *new_timer; + PRCondVar *cancel_timer; + PRThread *manager_thread; + PRCList timer_queue; +} tm_vars; + +static PRStatus TimerInit(void); +static void TimerManager(void *arg); +static TimerEvent *CreateTimer(PRIntervalTime timeout, + void (*func)(void *), void *arg); +static PRBool CancelTimer(TimerEvent *timer); + +static void TimerManager(void *arg) +{ + PRIntervalTime now; + PRIntervalTime timeout; + PRCList *head; + TimerEvent *timer; + + PR_Lock(tm_vars.ml); + while (1) + { + if (PR_CLIST_IS_EMPTY(&tm_vars.timer_queue)) + { + PR_WaitCondVar(tm_vars.new_timer, PR_INTERVAL_NO_TIMEOUT); + } + else + { + now = PR_IntervalNow(); + head = PR_LIST_HEAD(&tm_vars.timer_queue); + timer = TIMER_EVENT_PTR(head); + if ((PRInt32) (now - timer->absolute) >= 0) + { + PR_REMOVE_LINK(head); + /* + * make its prev and next point to itself so that + * it's obvious that it's not on the timer_queue. + */ + PR_INIT_CLIST(head); + PR_ASSERT(2 == timer->ref_count); + PR_Unlock(tm_vars.ml); + timer->func(timer->arg); + PR_Lock(tm_vars.ml); + timer->ref_count -= 1; + if (0 == timer->ref_count) + { + PR_NotifyAllCondVar(tm_vars.cancel_timer); + } + } + else + { + timeout = (PRIntervalTime)(timer->absolute - now); + PR_WaitCondVar(tm_vars.new_timer, timeout); + } + } + } + PR_Unlock(tm_vars.ml); +} + +static TimerEvent *CreateTimer( + PRIntervalTime timeout, + void (*func)(void *), + void *arg) +{ + TimerEvent *timer; + PRCList *links, *tail; + TimerEvent *elem; + + timer = PR_NEW(TimerEvent); + if (NULL == timer) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return timer; + } + timer->absolute = PR_IntervalNow() + timeout; + timer->func = func; + timer->arg = arg; + timer->ref_count = 2; + PR_Lock(tm_vars.ml); + tail = links = PR_LIST_TAIL(&tm_vars.timer_queue); + while (links->prev != tail) + { + elem = TIMER_EVENT_PTR(links); + if ((PRInt32)(timer->absolute - elem->absolute) >= 0) + { + break; + } + links = links->prev; + } + PR_INSERT_AFTER(&timer->links, links); + PR_NotifyCondVar(tm_vars.new_timer); + PR_Unlock(tm_vars.ml); + return timer; +} + +static PRBool CancelTimer(TimerEvent *timer) +{ + PRBool canceled = PR_FALSE; + + PR_Lock(tm_vars.ml); + timer->ref_count -= 1; + if (timer->links.prev == &timer->links) + { + while (timer->ref_count == 1) + { + PR_WaitCondVar(tm_vars.cancel_timer, PR_INTERVAL_NO_TIMEOUT); + } + } + else + { + PR_REMOVE_LINK(&timer->links); + canceled = PR_TRUE; + } + PR_Unlock(tm_vars.ml); + PR_DELETE(timer); + return canceled; +} + +static PRStatus TimerInit(void) +{ + tm_vars.ml = PR_NewLock(); + if (NULL == tm_vars.ml) + { + goto failed; + } + tm_vars.new_timer = PR_NewCondVar(tm_vars.ml); + if (NULL == tm_vars.new_timer) + { + goto failed; + } + tm_vars.cancel_timer = PR_NewCondVar(tm_vars.ml); + if (NULL == tm_vars.cancel_timer) + { + 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); + if (NULL == tm_vars.manager_thread) + { + goto failed; + } + return PR_SUCCESS; + +failed: + if (NULL != tm_vars.cancel_timer) + { + PR_DestroyCondVar(tm_vars.cancel_timer); + } + if (NULL != tm_vars.new_timer) + { + PR_DestroyCondVar(tm_vars.new_timer); + } + if (NULL != tm_vars.ml) + { + PR_DestroyLock(tm_vars.ml); + } + return PR_FAILURE; +} + +#endif /* WINNT */ + +/******************************************************************/ +/******************************************************************/ +/************************ The private portion *********************/ +/******************************************************************/ +/******************************************************************/ +void _PR_InitMW(void) +{ +#ifdef WINNT + /* + * We use NT 4's InterlockedCompareExchange() to operate + * on PRMWStatus variables. + */ + PR_ASSERT(sizeof(LONG) == sizeof(PRMWStatus)); + TimerInit(); +#endif + mw_lock = PR_NewLock(); + PR_ASSERT(NULL != mw_lock); + mw_state = PR_NEWZAP(_PRGlobalState); + PR_ASSERT(NULL != mw_state); + PR_INIT_CLIST(&mw_state->group_list); + max_polling_interval = PR_MillisecondsToInterval(MAX_POLLING_INTERVAL); +} /* _PR_InitMW */ + +void _PR_CleanupMW(void) +{ + PR_DestroyLock(mw_lock); + mw_lock = NULL; + if (mw_state->group) { + PR_DestroyWaitGroup(mw_state->group); + /* mw_state->group is set to NULL as a side effect. */ + } + PR_DELETE(mw_state); +} /* _PR_CleanupMW */ + +static PRWaitGroup *MW_Init2(void) +{ + PRWaitGroup *group = mw_state->group; /* it's the null group */ + if (NULL == group) /* there is this special case */ + { + group = PR_CreateWaitGroup(_PR_DEFAULT_HASH_LENGTH); + if (NULL == group) { + goto failed_alloc; + } + PR_Lock(mw_lock); + if (NULL == mw_state->group) + { + mw_state->group = group; + group = NULL; + } + PR_Unlock(mw_lock); + if (group != NULL) { + (void)PR_DestroyWaitGroup(group); + } + group = mw_state->group; /* somebody beat us to it */ + } +failed_alloc: + return group; /* whatever */ +} /* MW_Init2 */ + +static _PR_HashStory MW_AddHashInternal(PRRecvWait *desc, _PRWaiterHash *hash) +{ + /* + ** The entries are put in the table using the fd (PRFileDesc*) of + ** the receive descriptor as the key. This allows us to locate + ** the appropriate entry aqain when the poll operation finishes. + ** + ** The pointer to the file descriptor object is first divided by + ** the natural alignment of a pointer in the belief that object + ** will have at least that many zeros in the low order bits. + ** This may not be a good assuption. + ** + ** We try to put the entry in by rehashing _MW_REHASH_MAX times. After + ** that we declare defeat and force the table to be reconstructed. + ** Since some fds might be added more than once, won't that cause + ** collisions even in an empty table? + */ + PRIntn rehash = _MW_REHASH_MAX; + PRRecvWait **waiter; + PRUintn hidx = _MW_HASH(desc->fd, hash->length); + PRUintn hoffset = 0; + + while (rehash-- > 0) + { + waiter = &hash->recv_wait; + if (NULL == waiter[hidx]) + { + waiter[hidx] = desc; + hash->count += 1; +#if 0 + printf("Adding 0x%x->0x%x ", desc, desc->fd); + printf( + "table[%u:%u:*%u]: 0x%x->0x%x\n", + hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd); +#endif + return _prmw_success; + } + if (desc == waiter[hidx]) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); /* desc already in table */ + return _prmw_error; + } +#if 0 + printf("Failing 0x%x->0x%x ", desc, desc->fd); + printf( + "table[*%u:%u:%u]: 0x%x->0x%x\n", + hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd); +#endif + if (0 == hoffset) + { + hoffset = _MW_HASH2(desc->fd, hash->length); + PR_ASSERT(0 != hoffset); + } + hidx = (hidx + hoffset) % (hash->length); + } + return _prmw_rehash; +} /* MW_AddHashInternal */ + +static _PR_HashStory MW_ExpandHashInternal(PRWaitGroup *group) +{ + PRRecvWait **desc; + PRUint32 pidx, length; + _PRWaiterHash *newHash, *oldHash = group->waiter; + PRBool retry; + _PR_HashStory hrv; + + static const PRInt32 prime_number[] = { + _PR_DEFAULT_HASH_LENGTH, 179, 521, 907, 1427, + 2711, 3917, 5021, 8219, 11549, 18911, 26711, 33749, 44771 + }; + PRUintn primes = (sizeof(prime_number) / sizeof(PRInt32)); + + /* look up the next size we'd like to use for the hash table */ + for (pidx = 0; pidx < primes; ++pidx) + { + if (prime_number[pidx] == oldHash->length) + { + break; + } + } + /* table size must be one of the prime numbers */ + PR_ASSERT(pidx < primes); + + /* if pidx == primes - 1, we can't expand the table any more */ + while (pidx < primes - 1) + { + /* next size */ + ++pidx; + length = prime_number[pidx]; + + /* allocate the new hash table and fill it in with the old */ + newHash = (_PRWaiterHash*)PR_CALLOC( + sizeof(_PRWaiterHash) + (length * sizeof(PRRecvWait*))); + if (NULL == newHash) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return _prmw_error; + } + + newHash->length = length; + retry = PR_FALSE; + for (desc = &oldHash->recv_wait; + newHash->count < oldHash->count; ++desc) + { + PR_ASSERT(desc < &oldHash->recv_wait + oldHash->length); + if (NULL != *desc) + { + hrv = MW_AddHashInternal(*desc, newHash); + PR_ASSERT(_prmw_error != hrv); + if (_prmw_success != hrv) + { + PR_DELETE(newHash); + retry = PR_TRUE; + break; + } + } + } + if (retry) { + continue; + } + + PR_DELETE(group->waiter); + group->waiter = newHash; + group->p_timestamp += 1; + return _prmw_success; + } + + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return _prmw_error; /* we're hosed */ +} /* MW_ExpandHashInternal */ + +#ifndef WINNT +static void _MW_DoneInternal( + PRWaitGroup *group, PRRecvWait **waiter, PRMWStatus outcome) +{ + /* + ** Add this receive wait object to the list of finished I/O + ** operations for this particular group. If there are other + ** threads waiting on the group, notify one. If not, arrange + ** for this thread to return. + */ + +#if 0 + printf("Removing 0x%x->0x%x\n", *waiter, (*waiter)->fd); +#endif + (*waiter)->outcome = outcome; + PR_APPEND_LINK(&((*waiter)->internal), &group->io_ready); + PR_NotifyCondVar(group->io_complete); + PR_ASSERT(0 != group->waiter->count); + group->waiter->count -= 1; + *waiter = NULL; +} /* _MW_DoneInternal */ +#endif /* WINNT */ + +static PRRecvWait **_MW_LookupInternal(PRWaitGroup *group, PRFileDesc *fd) +{ + /* + ** Find the receive wait object corresponding to the file descriptor. + ** Only search the wait group specified. + */ + PRRecvWait **desc; + PRIntn rehash = _MW_REHASH_MAX; + _PRWaiterHash *hash = group->waiter; + PRUintn hidx = _MW_HASH(fd, hash->length); + PRUintn hoffset = 0; + + while (rehash-- > 0) + { + desc = (&hash->recv_wait) + hidx; + if ((*desc != NULL) && ((*desc)->fd == fd)) { + return desc; + } + if (0 == hoffset) + { + hoffset = _MW_HASH2(fd, hash->length); + PR_ASSERT(0 != hoffset); + } + hidx = (hidx + hoffset) % (hash->length); + } + return NULL; +} /* _MW_LookupInternal */ + +#ifndef WINNT +static PRStatus _MW_PollInternal(PRWaitGroup *group) +{ + PRRecvWait **waiter; + PRStatus rv = PR_FAILURE; + PRInt32 count, count_ready; + PRIntervalTime polling_interval; + + group->poller = PR_GetCurrentThread(); + + while (PR_TRUE) + { + PRIntervalTime now, since_last_poll; + PRPollDesc *poll_list; + + while (0 == group->waiter->count) + { + PRStatus st; + st = PR_WaitCondVar(group->new_business, PR_INTERVAL_NO_TIMEOUT); + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto aborted; + } + if (_MW_ABORTED(st)) { + goto aborted; + } + } + + /* + ** There's something to do. See if our existing polling list + ** is large enough for what we have to do? + */ + + while (group->polling_count < group->waiter->count) + { + PRUint32 old_count = group->waiter->count; + PRUint32 new_count = PR_ROUNDUP(old_count, _PR_POLL_COUNT_FUDGE); + PRSize new_size = sizeof(PRPollDesc) * new_count; + PRPollDesc *old_polling_list = group->polling_list; + + PR_Unlock(group->ml); + poll_list = (PRPollDesc*)PR_CALLOC(new_size); + if (NULL == poll_list) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + PR_Lock(group->ml); + goto failed_alloc; + } + if (NULL != old_polling_list) { + PR_DELETE(old_polling_list); + } + PR_Lock(group->ml); + if (_prmw_running != group->state) + { + PR_DELETE(poll_list); + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto aborted; + } + group->polling_list = poll_list; + group->polling_count = new_count; + } + + now = PR_IntervalNow(); + polling_interval = max_polling_interval; + since_last_poll = now - group->last_poll; + + waiter = &group->waiter->recv_wait; + poll_list = group->polling_list; + for (count = 0; count < group->waiter->count; ++waiter) + { + PR_ASSERT(waiter < &group->waiter->recv_wait + + group->waiter->length); + if (NULL != *waiter) /* a live one! */ + { + if ((PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout) + && (since_last_poll >= (*waiter)->timeout)) { + _MW_DoneInternal(group, waiter, PR_MW_TIMEOUT); + } + else + { + if (PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout) + { + (*waiter)->timeout -= since_last_poll; + if ((*waiter)->timeout < polling_interval) { + polling_interval = (*waiter)->timeout; + } + } + PR_ASSERT(poll_list < group->polling_list + + group->polling_count); + poll_list->fd = (*waiter)->fd; + poll_list->in_flags = PR_POLL_READ; + poll_list->out_flags = 0; +#if 0 + printf( + "Polling 0x%x[%d]: [fd: 0x%x, tmo: %u]\n", + poll_list, count, poll_list->fd, (*waiter)->timeout); +#endif + poll_list += 1; + count += 1; + } + } + } + + PR_ASSERT(count == group->waiter->count); + + /* + ** If there are no more threads waiting for completion, + ** we need to return. + */ + if ((!PR_CLIST_IS_EMPTY(&group->io_ready)) + && (1 == group->waiting_threads)) { + break; + } + + if (0 == count) { + continue; /* wait for new business */ + } + + group->last_poll = now; + + PR_Unlock(group->ml); + + count_ready = PR_Poll(group->polling_list, count, polling_interval); + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto aborted; + } + if (-1 == count_ready) + { + goto failed_poll; /* that's a shame */ + } + else if (0 < count_ready) + { + for (poll_list = group->polling_list; count > 0; + poll_list++, count--) + { + PR_ASSERT( + poll_list < group->polling_list + group->polling_count); + if (poll_list->out_flags != 0) + { + waiter = _MW_LookupInternal(group, poll_list->fd); + /* + ** If 'waiter' is NULL, that means the wait receive + ** descriptor has been canceled. + */ + if (NULL != waiter) { + _MW_DoneInternal(group, waiter, PR_MW_SUCCESS); + } + } + } + } + /* + ** If there are no more threads waiting for completion, + ** we need to return. + ** This thread was "borrowed" to do the polling, but it really + ** belongs to the client. + */ + if ((!PR_CLIST_IS_EMPTY(&group->io_ready)) + && (1 == group->waiting_threads)) { + break; + } + } + + rv = PR_SUCCESS; + +aborted: +failed_poll: +failed_alloc: + group->poller = NULL; /* we were that, not we ain't */ + if ((_prmw_running == group->state) && (group->waiting_threads > 1)) + { + /* Wake up one thread to become the new poller. */ + PR_NotifyCondVar(group->io_complete); + } + return rv; /* we return with the lock held */ +} /* _MW_PollInternal */ +#endif /* !WINNT */ + +static PRMWGroupState MW_TestForShutdownInternal(PRWaitGroup *group) +{ + PRMWGroupState rv = group->state; + /* + ** Looking at the group's fields is safe because + ** once the group's state is no longer running, it + ** cannot revert and there is a safe check on entry + ** to make sure no more threads are made to wait. + */ + if ((_prmw_stopping == rv) + && (0 == group->waiting_threads)) + { + rv = group->state = _prmw_stopped; + PR_NotifyCondVar(group->mw_manage); + } + return rv; +} /* MW_TestForShutdownInternal */ + +#ifndef WINNT +static void _MW_InitialRecv(PRCList *io_ready) +{ + PRRecvWait *desc = (PRRecvWait*)io_ready; + if ((NULL == desc->buffer.start) + || (0 == desc->buffer.length)) { + desc->bytesRecv = 0; + } + else + { + desc->bytesRecv = (desc->fd->methods->recv)( + desc->fd, desc->buffer.start, + desc->buffer.length, 0, desc->timeout); + if (desc->bytesRecv < 0) { /* SetError should already be there */ + desc->outcome = PR_MW_FAILURE; + } + } +} /* _MW_InitialRecv */ +#endif + +#ifdef WINNT +static void NT_TimeProc(void *arg) +{ + _MDOverlapped *overlapped = (_MDOverlapped *)arg; + PRRecvWait *desc = overlapped->data.mw.desc; + PRFileDesc *bottom; + + if (InterlockedCompareExchange((LONG *)&desc->outcome, + (LONG)PR_MW_TIMEOUT, (LONG)PR_MW_PENDING) != (LONG)PR_MW_PENDING) + { + /* This wait recv descriptor has already completed. */ + return; + } + + /* close the osfd to abort the outstanding async io request */ + /* $$$$ + ** Little late to be checking if NSPR's on the bottom of stack, + ** but if we don't check, we can't assert that the private data + ** is what we think it is. + ** $$$$ + */ + bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + if (NULL != bottom) /* now what!?!?! */ + { + bottom->secret->state = _PR_FILEDESC_CLOSED; + if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) + { + fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError()); + PR_NOT_REACHED("What shall I do?"); + } + } + return; +} /* NT_TimeProc */ + +static PRStatus NT_HashRemove(PRWaitGroup *group, PRFileDesc *fd) +{ + PRRecvWait **waiter; + + _PR_MD_LOCK(&group->mdlock); + waiter = _MW_LookupInternal(group, fd); + if (NULL != waiter) + { + group->waiter->count -= 1; + *waiter = NULL; + } + _PR_MD_UNLOCK(&group->mdlock); + return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE; +} + +PRStatus NT_HashRemoveInternal(PRWaitGroup *group, PRFileDesc *fd) +{ + PRRecvWait **waiter; + + waiter = _MW_LookupInternal(group, fd); + if (NULL != waiter) + { + group->waiter->count -= 1; + *waiter = NULL; + } + return (NULL != waiter) ? PR_SUCCESS : PR_FAILURE; +} +#endif /* WINNT */ + +/******************************************************************/ +/******************************************************************/ +/********************** The public API portion ********************/ +/******************************************************************/ +/******************************************************************/ +PR_IMPLEMENT(PRStatus) PR_AddWaitFileDesc( + PRWaitGroup *group, PRRecvWait *desc) +{ + _PR_HashStory hrv; + PRStatus rv = PR_FAILURE; +#ifdef WINNT + _MDOverlapped *overlapped; + HANDLE hFile; + BOOL bResult; + DWORD dwError; + PRFileDesc *bottom; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + if ((NULL == group) && (NULL == (group = MW_Init2()))) + { + return rv; + } + + PR_ASSERT(NULL != desc->fd); + + desc->outcome = PR_MW_PENDING; /* nice, well known value */ + desc->bytesRecv = 0; /* likewise, though this value is ambiguious */ + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + /* Not allowed to add after cancelling the group */ + desc->outcome = PR_MW_INTERRUPT; + PR_SetError(PR_INVALID_STATE_ERROR, 0); + PR_Unlock(group->ml); + return rv; + } + +#ifdef WINNT + _PR_MD_LOCK(&group->mdlock); +#endif + + /* + ** If the waiter count is zero at this point, there's no telling + ** how long we've been idle. Therefore, initialize the beginning + ** of the timing interval. As long as the list doesn't go empty, + ** it will maintain itself. + */ + if (0 == group->waiter->count) { + group->last_poll = PR_IntervalNow(); + } + + do + { + hrv = MW_AddHashInternal(desc, group->waiter); + if (_prmw_rehash != hrv) { + break; + } + hrv = MW_ExpandHashInternal(group); /* gruesome */ + if (_prmw_success != hrv) { + break; + } + } while (PR_TRUE); + +#ifdef WINNT + _PR_MD_UNLOCK(&group->mdlock); +#endif + + PR_NotifyCondVar(group->new_business); /* tell the world */ + rv = (_prmw_success == hrv) ? PR_SUCCESS : PR_FAILURE; + PR_Unlock(group->ml); + +#ifdef WINNT + overlapped = PR_NEWZAP(_MDOverlapped); + if (NULL == overlapped) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + NT_HashRemove(group, desc->fd); + return rv; + } + overlapped->ioModel = _MD_MultiWaitIO; + overlapped->data.mw.desc = desc; + overlapped->data.mw.group = group; + if (desc->timeout != PR_INTERVAL_NO_TIMEOUT) + { + overlapped->data.mw.timer = CreateTimer( + desc->timeout, + NT_TimeProc, + overlapped); + if (0 == overlapped->data.mw.timer) + { + NT_HashRemove(group, desc->fd); + PR_DELETE(overlapped); + /* + * XXX It appears that a maximum of 16 timer events can + * be outstanding. GetLastError() returns 0 when I try it. + */ + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, GetLastError()); + return PR_FAILURE; + } + } + + /* Reach to the bottom layer to get the OS fd */ + bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + if (NULL == bottom) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + hFile = (HANDLE)bottom->secret->md.osfd; + if (!bottom->secret->md.io_model_committed) + { + PRInt32 st; + st = _md_Associate(hFile); + PR_ASSERT(0 != st); + bottom->secret->md.io_model_committed = PR_TRUE; + } + bResult = ReadFile(hFile, + desc->buffer.start, + (DWORD)desc->buffer.length, + NULL, + &overlapped->overlapped); + if (FALSE == bResult && (dwError = GetLastError()) != ERROR_IO_PENDING) + { + if (desc->timeout != PR_INTERVAL_NO_TIMEOUT) + { + if (InterlockedCompareExchange((LONG *)&desc->outcome, + (LONG)PR_MW_FAILURE, (LONG)PR_MW_PENDING) + == (LONG)PR_MW_PENDING) + { + CancelTimer(overlapped->data.mw.timer); + } + NT_HashRemove(group, desc->fd); + PR_DELETE(overlapped); + } + _PR_MD_MAP_READ_ERROR(dwError); + rv = PR_FAILURE; + } +#endif + + return rv; +} /* PR_AddWaitFileDesc */ + +PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group) +{ + PRCList *io_ready = NULL; +#ifdef WINNT + PRThread *me = _PR_MD_CURRENT_THREAD(); + _MDOverlapped *overlapped; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + if ((NULL == group) && (NULL == (group = MW_Init2()))) { + goto failed_init; + } + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto invalid_state; + } + + group->waiting_threads += 1; /* the polling thread is counted */ + +#ifdef WINNT + _PR_MD_LOCK(&group->mdlock); + while (PR_CLIST_IS_EMPTY(&group->io_ready)) + { + _PR_THREAD_LOCK(me); + me->state = _PR_IO_WAIT; + PR_APPEND_LINK(&me->waitQLinks, &group->wait_list); + if (!_PR_IS_NATIVE_THREAD(me)) + { + _PR_SLEEPQ_LOCK(me->cpu); + _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT); + _PR_SLEEPQ_UNLOCK(me->cpu); + } + _PR_THREAD_UNLOCK(me); + _PR_MD_UNLOCK(&group->mdlock); + PR_Unlock(group->ml); + _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); + me->state = _PR_RUNNING; + PR_Lock(group->ml); + _PR_MD_LOCK(&group->mdlock); + if (_PR_PENDING_INTERRUPT(me)) { + PR_REMOVE_LINK(&me->waitQLinks); + _PR_MD_UNLOCK(&group->mdlock); + me->flags &= ~_PR_INTERRUPT; + me->io_suspended = PR_FALSE; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + goto aborted; + } + } + io_ready = PR_LIST_HEAD(&group->io_ready); + PR_ASSERT(io_ready != NULL); + PR_REMOVE_LINK(io_ready); + _PR_MD_UNLOCK(&group->mdlock); + overlapped = (_MDOverlapped *) + ((char *)io_ready - offsetof(_MDOverlapped, data)); + io_ready = &overlapped->data.mw.desc->internal; +#else + do + { + /* + ** If the I/O ready list isn't empty, have this thread + ** return with the first receive wait object that's available. + */ + if (PR_CLIST_IS_EMPTY(&group->io_ready)) + { + /* + ** Is there a polling thread yet? If not, grab this thread + ** and use it. + */ + if (NULL == group->poller) + { + /* + ** This thread will stay do polling until it becomes the only one + ** left to service a completion. Then it will return and there will + ** be none left to actually poll or to run completions. + ** + ** The polling function should only return w/ failure or + ** with some I/O ready. + */ + if (PR_FAILURE == _MW_PollInternal(group)) { + goto failed_poll; + } + } + else + { + /* + ** There are four reasons a thread can be awakened from + ** a wait on the io_complete condition variable. + ** 1. Some I/O has completed, i.e., the io_ready list + ** is nonempty. + ** 2. The wait group is canceled. + ** 3. The thread is interrupted. + ** 4. The current polling thread has to leave and needs + ** a replacement. + ** The logic to find a new polling thread is made more + ** complicated by all the other possible events. + ** I tried my best to write the logic clearly, but + ** it is still full of if's with continue and goto. + */ + PRStatus st; + do + { + st = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT); + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto aborted; + } + if (_MW_ABORTED(st) || (NULL == group->poller)) { + break; + } + } while (PR_CLIST_IS_EMPTY(&group->io_ready)); + + /* + ** The thread is interrupted and has to leave. It might + ** have also been awakened to process ready i/o or be the + ** new poller. To be safe, if either condition is true, + ** we awaken another thread to take its place. + */ + if (_MW_ABORTED(st)) + { + if ((NULL == group->poller + || !PR_CLIST_IS_EMPTY(&group->io_ready)) + && group->waiting_threads > 1) { + PR_NotifyCondVar(group->io_complete); + } + goto aborted; + } + + /* + ** A new poller is needed, but can I be the new poller? + ** If there is no i/o ready, sure. But if there is any + ** i/o ready, it has a higher priority. I want to + ** process the ready i/o first and wake up another + ** thread to be the new poller. + */ + if (NULL == group->poller) + { + if (PR_CLIST_IS_EMPTY(&group->io_ready)) { + continue; + } + if (group->waiting_threads > 1) { + PR_NotifyCondVar(group->io_complete); + } + } + } + PR_ASSERT(!PR_CLIST_IS_EMPTY(&group->io_ready)); + } + io_ready = PR_LIST_HEAD(&group->io_ready); + PR_NotifyCondVar(group->io_taken); + PR_ASSERT(io_ready != NULL); + PR_REMOVE_LINK(io_ready); + } while (NULL == io_ready); + +failed_poll: + +#endif + +aborted: + + group->waiting_threads -= 1; +invalid_state: + (void)MW_TestForShutdownInternal(group); + PR_Unlock(group->ml); + +failed_init: + if (NULL != io_ready) + { + /* If the operation failed, record the reason why */ + switch (((PRRecvWait*)io_ready)->outcome) + { + case PR_MW_PENDING: + PR_ASSERT(0); + break; + case PR_MW_SUCCESS: +#ifndef WINNT + _MW_InitialRecv(io_ready); +#endif + break; +#ifdef WINNT + case PR_MW_FAILURE: + _PR_MD_MAP_READ_ERROR(overlapped->data.mw.error); + break; +#endif + case PR_MW_TIMEOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_MW_INTERRUPT: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + break; + default: break; + } +#ifdef WINNT + if (NULL != overlapped->data.mw.timer) + { + PR_ASSERT(PR_INTERVAL_NO_TIMEOUT + != overlapped->data.mw.desc->timeout); + CancelTimer(overlapped->data.mw.timer); + } + else + { + PR_ASSERT(PR_INTERVAL_NO_TIMEOUT + == overlapped->data.mw.desc->timeout); + } + PR_DELETE(overlapped); +#endif + } + return (PRRecvWait*)io_ready; +} /* PR_WaitRecvReady */ + +PR_IMPLEMENT(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc) +{ +#if !defined(WINNT) + PRRecvWait **recv_wait; +#endif + PRStatus rv = PR_SUCCESS; + if (NULL == group) { + group = mw_state->group; + } + PR_ASSERT(NULL != group); + if (NULL == group) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + rv = PR_FAILURE; + goto unlock; + } + +#ifdef WINNT + if (InterlockedCompareExchange((LONG *)&desc->outcome, + (LONG)PR_MW_INTERRUPT, (LONG)PR_MW_PENDING) == (LONG)PR_MW_PENDING) + { + PRFileDesc *bottom = PR_GetIdentitiesLayer(desc->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + if (NULL == bottom) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + goto unlock; + } + bottom->secret->state = _PR_FILEDESC_CLOSED; +#if 0 + fprintf(stderr, "cancel wait recv: closing socket\n"); +#endif + if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) + { + fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError()); + exit(1); + } + } +#else + if (NULL != (recv_wait = _MW_LookupInternal(group, desc->fd))) + { + /* it was in the wait table */ + _MW_DoneInternal(group, recv_wait, PR_MW_INTERRUPT); + goto unlock; + } + if (!PR_CLIST_IS_EMPTY(&group->io_ready)) + { + /* is it already complete? */ + PRCList *head = PR_LIST_HEAD(&group->io_ready); + do + { + PRRecvWait *done = (PRRecvWait*)head; + if (done == desc) { + goto unlock; + } + head = PR_NEXT_LINK(head); + } while (head != &group->io_ready); + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + +#endif +unlock: + PR_Unlock(group->ml); + return rv; +} /* PR_CancelWaitFileDesc */ + +PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group) +{ + PRRecvWait **desc; + PRRecvWait *recv_wait = NULL; +#ifdef WINNT + _MDOverlapped *overlapped; + PRRecvWait **end; + PRThread *me = _PR_MD_CURRENT_THREAD(); +#endif + + if (NULL == group) { + group = mw_state->group; + } + PR_ASSERT(NULL != group); + if (NULL == group) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + + PR_Lock(group->ml); + if (_prmw_stopped != group->state) + { + if (_prmw_running == group->state) { + group->state = _prmw_stopping; /* so nothing new comes in */ + } + if (0 == group->waiting_threads) { /* is there anybody else? */ + group->state = _prmw_stopped; /* we can stop right now */ + } + else + { + PR_NotifyAllCondVar(group->new_business); + PR_NotifyAllCondVar(group->io_complete); + } + while (_prmw_stopped != group->state) { + (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT); + } + } + +#ifdef WINNT + _PR_MD_LOCK(&group->mdlock); +#endif + /* make all the existing descriptors look done/interrupted */ +#ifdef WINNT + end = &group->waiter->recv_wait + group->waiter->length; + for (desc = &group->waiter->recv_wait; desc < end; ++desc) + { + if (NULL != *desc) + { + if (InterlockedCompareExchange((LONG *)&(*desc)->outcome, + (LONG)PR_MW_INTERRUPT, (LONG)PR_MW_PENDING) + == (LONG)PR_MW_PENDING) + { + PRFileDesc *bottom = PR_GetIdentitiesLayer( + (*desc)->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + if (NULL == bottom) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + goto invalid_arg; + } + bottom->secret->state = _PR_FILEDESC_CLOSED; +#if 0 + fprintf(stderr, "cancel wait group: closing socket\n"); +#endif + if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) + { + fprintf(stderr, "closesocket failed: %d\n", + WSAGetLastError()); + exit(1); + } + } + } + } + while (group->waiter->count > 0) + { + _PR_THREAD_LOCK(me); + me->state = _PR_IO_WAIT; + PR_APPEND_LINK(&me->waitQLinks, &group->wait_list); + if (!_PR_IS_NATIVE_THREAD(me)) + { + _PR_SLEEPQ_LOCK(me->cpu); + _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT); + _PR_SLEEPQ_UNLOCK(me->cpu); + } + _PR_THREAD_UNLOCK(me); + _PR_MD_UNLOCK(&group->mdlock); + PR_Unlock(group->ml); + _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); + me->state = _PR_RUNNING; + PR_Lock(group->ml); + _PR_MD_LOCK(&group->mdlock); + } +#else + for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc) + { + PR_ASSERT(desc < &group->waiter->recv_wait + group->waiter->length); + if (NULL != *desc) { + _MW_DoneInternal(group, desc, PR_MW_INTERRUPT); + } + } +#endif + + /* take first element of finished list and return it or NULL */ + if (PR_CLIST_IS_EMPTY(&group->io_ready)) { + PR_SetError(PR_GROUP_EMPTY_ERROR, 0); + } + else + { + PRCList *head = PR_LIST_HEAD(&group->io_ready); + PR_REMOVE_AND_INIT_LINK(head); +#ifdef WINNT + overlapped = (_MDOverlapped *) + ((char *)head - offsetof(_MDOverlapped, data)); + head = &overlapped->data.mw.desc->internal; + if (NULL != overlapped->data.mw.timer) + { + PR_ASSERT(PR_INTERVAL_NO_TIMEOUT + != overlapped->data.mw.desc->timeout); + CancelTimer(overlapped->data.mw.timer); + } + else + { + PR_ASSERT(PR_INTERVAL_NO_TIMEOUT + == overlapped->data.mw.desc->timeout); + } + PR_DELETE(overlapped); +#endif + recv_wait = (PRRecvWait*)head; + } +#ifdef WINNT +invalid_arg: + _PR_MD_UNLOCK(&group->mdlock); +#endif + PR_Unlock(group->ml); + + return recv_wait; +} /* PR_CancelWaitGroup */ + +PR_IMPLEMENT(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size /* ignored */) +{ + PRWaitGroup *wg; + + if (NULL == (wg = PR_NEWZAP(PRWaitGroup))) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto failed; + } + /* the wait group itself */ + wg->ml = PR_NewLock(); + if (NULL == wg->ml) { + goto failed_lock; + } + wg->io_taken = PR_NewCondVar(wg->ml); + if (NULL == wg->io_taken) { + goto failed_cvar0; + } + wg->io_complete = PR_NewCondVar(wg->ml); + if (NULL == wg->io_complete) { + goto failed_cvar1; + } + wg->new_business = PR_NewCondVar(wg->ml); + if (NULL == wg->new_business) { + goto failed_cvar2; + } + wg->mw_manage = PR_NewCondVar(wg->ml); + if (NULL == wg->mw_manage) { + goto failed_cvar3; + } + + PR_INIT_CLIST(&wg->group_link); + PR_INIT_CLIST(&wg->io_ready); + + /* the waiters sequence */ + wg->waiter = (_PRWaiterHash*)PR_CALLOC( + sizeof(_PRWaiterHash) + + (_PR_DEFAULT_HASH_LENGTH * sizeof(PRRecvWait*))); + if (NULL == wg->waiter) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto failed_waiter; + } + wg->waiter->count = 0; + wg->waiter->length = _PR_DEFAULT_HASH_LENGTH; + +#ifdef WINNT + _PR_MD_NEW_LOCK(&wg->mdlock); + PR_INIT_CLIST(&wg->wait_list); +#endif /* WINNT */ + + PR_Lock(mw_lock); + PR_APPEND_LINK(&wg->group_link, &mw_state->group_list); + PR_Unlock(mw_lock); + return wg; + +failed_waiter: + PR_DestroyCondVar(wg->mw_manage); +failed_cvar3: + PR_DestroyCondVar(wg->new_business); +failed_cvar2: + PR_DestroyCondVar(wg->io_complete); +failed_cvar1: + PR_DestroyCondVar(wg->io_taken); +failed_cvar0: + PR_DestroyLock(wg->ml); +failed_lock: + PR_DELETE(wg); + wg = NULL; + +failed: + return wg; +} /* MW_CreateWaitGroup */ + +PR_IMPLEMENT(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group) +{ + PRStatus rv = PR_SUCCESS; + if (NULL == group) { + group = mw_state->group; + } + PR_ASSERT(NULL != group); + if (NULL != group) + { + PR_Lock(group->ml); + if ((group->waiting_threads == 0) + && (group->waiter->count == 0) + && PR_CLIST_IS_EMPTY(&group->io_ready)) + { + group->state = _prmw_stopped; + } + else + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + rv = PR_FAILURE; + } + PR_Unlock(group->ml); + if (PR_FAILURE == rv) { + return rv; + } + + PR_Lock(mw_lock); + PR_REMOVE_LINK(&group->group_link); + PR_Unlock(mw_lock); + +#ifdef WINNT + /* + * XXX make sure wait_list is empty and waiter is empty. + * These must be checked while holding mdlock. + */ + _PR_MD_FREE_LOCK(&group->mdlock); +#endif + + PR_DELETE(group->waiter); + PR_DELETE(group->polling_list); + PR_DestroyCondVar(group->mw_manage); + PR_DestroyCondVar(group->new_business); + PR_DestroyCondVar(group->io_complete); + PR_DestroyCondVar(group->io_taken); + PR_DestroyLock(group->ml); + if (group == mw_state->group) { + mw_state->group = NULL; + } + PR_DELETE(group); + } + else + { + /* The default wait group is not created yet. */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + } + return rv; +} /* PR_DestroyWaitGroup */ + +/********************************************************************** +*********************************************************************** +******************** Wait group enumerations ************************** +*********************************************************************** +**********************************************************************/ + +PR_IMPLEMENT(PRMWaitEnumerator*) PR_CreateMWaitEnumerator(PRWaitGroup *group) +{ + PRMWaitEnumerator *enumerator = PR_NEWZAP(PRMWaitEnumerator); + if (NULL == enumerator) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + enumerator->group = group; + enumerator->seal = _PR_ENUM_SEALED; + } + return enumerator; +} /* PR_CreateMWaitEnumerator */ + +PR_IMPLEMENT(PRStatus) PR_DestroyMWaitEnumerator(PRMWaitEnumerator* enumerator) +{ + PR_ASSERT(NULL != enumerator); + PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal); + if ((NULL == enumerator) || (_PR_ENUM_SEALED != enumerator->seal)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + enumerator->seal = _PR_ENUM_UNSEALED; + PR_Free(enumerator); + return PR_SUCCESS; +} /* PR_DestroyMWaitEnumerator */ + +PR_IMPLEMENT(PRRecvWait*) PR_EnumerateWaitGroup( + PRMWaitEnumerator *enumerator, const PRRecvWait *previous) +{ + PRRecvWait *result = NULL; + + /* entry point sanity checking */ + PR_ASSERT(NULL != enumerator); + PR_ASSERT(_PR_ENUM_SEALED == enumerator->seal); + if ((NULL == enumerator) + || (_PR_ENUM_SEALED != enumerator->seal)) { + goto bad_argument; + } + + /* beginning of enumeration */ + if (NULL == previous) + { + if (NULL == enumerator->group) + { + enumerator->group = mw_state->group; + if (NULL == enumerator->group) + { + PR_SetError(PR_GROUP_EMPTY_ERROR, 0); + return NULL; + } + } + enumerator->waiter = &enumerator->group->waiter->recv_wait; + enumerator->p_timestamp = enumerator->group->p_timestamp; + enumerator->thread = PR_GetCurrentThread(); + enumerator->index = 0; + } + /* continuing an enumeration */ + else + { + PRThread *me = PR_GetCurrentThread(); + PR_ASSERT(me == enumerator->thread); + if (me != enumerator->thread) { + goto bad_argument; + } + + /* need to restart the enumeration */ + if (enumerator->p_timestamp != enumerator->group->p_timestamp) { + return PR_EnumerateWaitGroup(enumerator, NULL); + } + } + + /* actually progress the enumeration */ +#if defined(WINNT) + _PR_MD_LOCK(&enumerator->group->mdlock); +#else + PR_Lock(enumerator->group->ml); +#endif + while (enumerator->index++ < enumerator->group->waiter->length) + { + if (NULL != (result = *(enumerator->waiter)++)) { + break; + } + } +#if defined(WINNT) + _PR_MD_UNLOCK(&enumerator->group->mdlock); +#else + PR_Unlock(enumerator->group->ml); +#endif + + return result; /* what we live for */ + +bad_argument: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; /* probably ambiguous */ +} /* PR_EnumerateWaitGroup */ + +/* prmwait.c */ diff --git a/nsprpub/pr/src/io/prpolevt.c b/nsprpub/pr/src/io/prpolevt.c new file mode 100644 index 0000000000..1b664ba24e --- /dev/null +++ b/nsprpub/pr/src/io/prpolevt.c @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + ********************************************************************* + * + * 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 "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); + +/* + * 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, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +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) +#define USE_TCP_SOCKETPAIR +#endif + +PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void) +{ + PRFileDesc *event; + PRFileDesc *fd[2]; /* fd[0] is the read end; fd[1] is the write end */ +#ifdef USE_TCP_SOCKETPAIR + PRSocketOptionData socket_opt; + PRStatus rv; +#endif + + 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; + } + +#ifndef USE_TCP_SOCKETPAIR + if (PR_CreatePipe(&fd[0], &fd[1]) == PR_FAILURE) { + fd[0] = fd[1] = NULL; + goto errorExit; + } +#else + if (PR_NewTCPSocketPair(fd) == PR_FAILURE) { + fd[0] = fd[1] = NULL; + goto errorExit; + } + /* + * set the TCP_NODELAY option to reduce notification latency + */ + socket_opt.option = PR_SockOpt_NoDelay; + socket_opt.value.no_delay = PR_TRUE; + rv = PR_SetSocketOption(fd[1], &socket_opt); + PR_ASSERT(PR_SUCCESS == rv); +#endif + + event->secret->writeEnd = fd[1]; + if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, event) == PR_FAILURE) { + goto errorExit; + } + + return fd[0]; + +errorExit: + if (fd[0]) { + PR_Close(fd[0]); + PR_Close(fd[1]); + } + if (event) { + PR_DELETE(event->secret); + event->dtor(event); + } + return NULL; +} + +static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd) +{ + 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) +{ + if (PR_Write(event->secret->writeEnd, &magicChar, 1) != 1) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event) +{ + char buf[1024]; + PRInt32 nBytes; +#ifdef DEBUG + PRIntn i; +#endif + + nBytes = PR_Read(event->lower, buf, sizeof(buf)); + if (nBytes == -1) { + return PR_FAILURE; + } + +#ifdef DEBUG + /* + * Make sure people do not write to the pollable event fd + * directly. + */ + for (i = 0; i < nBytes; i++) { + PR_ASSERT(buf[i] == magicChar); + } +#endif + + return PR_SUCCESS; +} diff --git a/nsprpub/pr/src/io/prprf.c b/nsprpub/pr/src/io/prprf.c new file mode 100644 index 0000000000..9b6072e268 --- /dev/null +++ b/nsprpub/pr/src/io/prprf.c @@ -0,0 +1,1316 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Portable safe sprintf code. +** +** Author: Kipp E.B. Hickman +*/ +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include "primpl.h" +#include "prprf.h" +#include "prlong.h" +#include "prlog.h" +#include "prmem.h" + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + +/* +** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it) +*/ + +/* +** XXX This needs to be internationalized! +*/ + +typedef struct SprintfStateStr SprintfState; + +struct SprintfStateStr { + int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len); + + char *base; + char *cur; + PRUint32 maxlen; /* Must not exceed PR_INT32_MAX. */ + + int (*func)(void *arg, const char *sp, PRUint32 len); + void *arg; +}; + +/* +** Numbered Argument +*/ +struct NumArg { + int type; /* type of the numbered argument */ + union { /* the numbered argument */ + int i; + unsigned int ui; + PRInt32 i32; + PRUint32 ui32; + PRInt64 ll; + PRUint64 ull; + double d; + const char *s; + int *ip; +#ifdef WIN32 + const WCHAR *ws; +#endif + } u; +}; + +#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgument array */ + +/* +** For numeric types, the signed versions must have even values, +** and their corresponding unsigned versions must have the subsequent +** odd value. +*/ +#define TYPE_INT16 0 +#define TYPE_UINT16 1 +#define TYPE_INTN 2 +#define TYPE_UINTN 3 +#define TYPE_INT32 4 +#define TYPE_UINT32 5 +#define TYPE_INT64 6 +#define TYPE_UINT64 7 +#define TYPE_STRING 8 +#define TYPE_DOUBLE 9 +#define TYPE_INTSTR 10 +#ifdef WIN32 +#define TYPE_WSTRING 11 +#endif +#define TYPE_UNKNOWN 20 + +#define FLAG_LEFT 0x1 +#define FLAG_SIGNED 0x2 +#define FLAG_SPACED 0x4 +#define FLAG_ZEROS 0x8 +#define FLAG_NEG 0x10 + +/* +** Fill into the buffer using the data in src +*/ +static int fill2(SprintfState *ss, const char *src, int srclen, int width, + int flags) +{ + char space = ' '; + int rv; + + width -= srclen; + if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ + if (flags & FLAG_ZEROS) { + space = '0'; + } + while (--width >= 0) { + rv = (*ss->stuff)(ss, &space, 1); + if (rv < 0) { + return rv; + } + } + } + + /* Copy out the source data */ + rv = (*ss->stuff)(ss, src, srclen); + if (rv < 0) { + return rv; + } + + if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ + while (--width >= 0) { + rv = (*ss->stuff)(ss, &space, 1); + if (rv < 0) { + return rv; + } + } + } + return 0; +} + +/* +** Fill a number. The order is: optional-sign zero-filling conversion-digits +*/ +static int fill_n(SprintfState *ss, const char *src, int srclen, int width, + int prec, int type, int flags) +{ + int zerowidth = 0; + int precwidth = 0; + int signwidth = 0; + int leftspaces = 0; + int rightspaces = 0; + int cvtwidth; + int rv; + char sign; + + if ((type & 1) == 0) { + if (flags & FLAG_NEG) { + sign = '-'; + signwidth = 1; + } else if (flags & FLAG_SIGNED) { + sign = '+'; + signwidth = 1; + } else if (flags & FLAG_SPACED) { + sign = ' '; + signwidth = 1; + } + } + cvtwidth = signwidth + srclen; + + if (prec > 0) { + if (prec > srclen) { + precwidth = prec - srclen; /* Need zero filling */ + cvtwidth += precwidth; + } + } + + if ((flags & FLAG_ZEROS) && (prec < 0)) { + if (width > cvtwidth) { + zerowidth = width - cvtwidth; /* Zero filling */ + cvtwidth += zerowidth; + } + } + + if (flags & FLAG_LEFT) { + if (width > cvtwidth) { + /* Space filling on the right (i.e. left adjusting) */ + rightspaces = width - cvtwidth; + } + } else { + if (width > cvtwidth) { + /* Space filling on the left (i.e. right adjusting) */ + leftspaces = width - cvtwidth; + } + } + while (--leftspaces >= 0) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + if (signwidth) { + rv = (*ss->stuff)(ss, &sign, 1); + if (rv < 0) { + return rv; + } + } + while (--precwidth >= 0) { + rv = (*ss->stuff)(ss, "0", 1); + if (rv < 0) { + return rv; + } + } + while (--zerowidth >= 0) { + rv = (*ss->stuff)(ss, "0", 1); + if (rv < 0) { + return rv; + } + } + rv = (*ss->stuff)(ss, src, srclen); + if (rv < 0) { + return rv; + } + while (--rightspaces >= 0) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + return 0; +} + +/* +** Convert a long into its printable form +*/ +static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, + int type, int flags, const char *hexp) +{ + char cvtbuf[100]; + char *cvt; + int digits; + + /* according to the man page this needs to happen */ + if ((prec == 0) && (num == 0)) { + return 0; + } + + /* + ** Converting decimal is a little tricky. In the unsigned case we + ** need to stop when we hit 10 digits. In the signed case, we can + ** stop when the number is zero. + */ + cvt = cvtbuf + sizeof(cvtbuf); + digits = 0; + while (num) { + int digit = (((unsigned long)num) % radix) & 0xF; + *--cvt = hexp[digit]; + digits++; + num = (long)(((unsigned long)num) / radix); + } + if (digits == 0) { + *--cvt = '0'; + digits++; + } + + /* + ** Now that we have the number converted without its sign, deal with + ** the sign and zero padding. + */ + return fill_n(ss, cvt, digits, width, prec, type, flags); +} + +/* +** Convert a 64-bit integer into its printable form +*/ +static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix, + int type, int flags, const char *hexp) +{ + char cvtbuf[100]; + char *cvt; + int digits; + PRInt64 rad; + + /* according to the man page this needs to happen */ + if ((prec == 0) && (LL_IS_ZERO(num))) { + return 0; + } + + /* + ** Converting decimal is a little tricky. In the unsigned case we + ** need to stop when we hit 10 digits. In the signed case, we can + ** stop when the number is zero. + */ + LL_I2L(rad, radix); + cvt = cvtbuf + sizeof(cvtbuf); + digits = 0; + while (!LL_IS_ZERO(num)) { + PRInt32 digit; + PRInt64 quot, rem; + LL_UDIVMOD(", &rem, num, rad); + LL_L2I(digit, rem); + *--cvt = hexp[digit & 0xf]; + digits++; + num = quot; + } + if (digits == 0) { + *--cvt = '0'; + digits++; + } + + /* + ** Now that we have the number converted without its sign, deal with + ** the sign and zero padding. + */ + return fill_n(ss, cvt, digits, width, prec, type, flags); +} + +/* +** Convert a double precision floating point number into its printable +** form. +** +** XXX stop using snprintf to convert floating point +*/ +static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) +{ + char fin[20]; + char fout[300]; + int amount = fmt1 - fmt0; + + if (amount <= 0 || amount >= sizeof(fin)) { + /* Totally bogus % command to snprintf. Just ignore it */ + return 0; + } + memcpy(fin, fmt0, amount); + fin[amount] = 0; + + /* Convert floating point using the native snprintf code */ +#ifdef DEBUG + { + const char *p = fin; + while (*p) { + PR_ASSERT(*p != 'L'); + p++; + } + } +#endif + memset(fout, 0, sizeof(fout)); + snprintf(fout, sizeof(fout), fin, d); + /* Explicitly null-terminate fout because on Windows snprintf doesn't + * append a null-terminator if the buffer is too small. */ + fout[sizeof(fout) - 1] = '\0'; + + return (*ss->stuff)(ss, fout, strlen(fout)); +} + +/* +** Convert a string into its printable form. "width" is the output +** width. "prec" is the maximum number of characters of "s" to output, +** where -1 means until NUL. +*/ +static int cvt_s(SprintfState *ss, const char *str, int width, int prec, + int flags) +{ + int slen; + + if (prec == 0) { + return 0; + } + + /* Limit string length by precision value */ + if (!str) { + str = "(null)"; + } + if (prec > 0) { + /* this is: slen = strnlen(str, prec); */ + register const char *s; + + for(s = str; prec && *s; s++, prec-- ) + ; + slen = s - str; + } else { + slen = strlen(str); + } + + /* and away we go */ + return fill2(ss, str, slen, width, flags); +} + +/* +** BuildArgArray stands for Numbered Argument list Sprintf +** for example, +** fmt = "%4$i, %2$d, %3s, %1d"; +** the number must start from 1, and no gap among them +*/ + +static struct NumArg* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArg* nasArray ) +{ + int number = 0, cn = 0, i; + const char* p; + char c; + struct NumArg* nas; + + + /* + ** first pass: + ** determine how many legal % I have got, then allocate space + */ + + p = fmt; + *rv = 0; + i = 0; + while( ( c = *p++ ) != 0 ) { + if( c != '%' ) { + continue; + } + if( ( c = *p++ ) == '%' ) { /* skip %% case */ + continue; + } + + while( c != 0 ) { + if( c > '9' || c < '0' ) { + if( c == '$' ) { /* numbered argument case */ + if( i > 0 ) { + *rv = -1; + return NULL; + } + number++; + } else { /* non-numbered argument case */ + if( number > 0 ) { + *rv = -1; + return NULL; + } + i = 1; + } + break; + } + + c = *p++; + } + } + + if( number == 0 ) { + return NULL; + } + + + if( number > NAS_DEFAULT_NUM ) { + nas = (struct NumArg*)PR_MALLOC( number * sizeof( struct NumArg ) ); + if( !nas ) { + *rv = -1; + return NULL; + } + } else { + nas = nasArray; + } + + for( i = 0; i < number; i++ ) { + nas[i].type = TYPE_UNKNOWN; + } + + + /* + ** second pass: + ** set nas[].type + */ + + p = fmt; + while( ( c = *p++ ) != 0 ) { + if( c != '%' ) { + continue; + } + c = *p++; + if( c == '%' ) { + continue; + } + + cn = 0; + while( c && c != '$' ) { /* should improve error check later */ + cn = cn*10 + c - '0'; + c = *p++; + } + + if( !c || cn < 1 || cn > number ) { + *rv = -1; + break; + } + + /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ + cn--; + if( nas[cn].type != TYPE_UNKNOWN ) { + continue; + } + + c = *p++; + + /* width */ + if (c == '*') { + /* not supported feature, for the argument is not numbered */ + *rv = -1; + break; + } + + while ((c >= '0') && (c <= '9')) { + c = *p++; + } + + /* precision */ + if (c == '.') { + c = *p++; + if (c == '*') { + /* not supported feature, for the argument is not numbered */ + *rv = -1; + break; + } + + while ((c >= '0') && (c <= '9')) { + c = *p++; + } + } + + /* size */ + nas[cn].type = TYPE_INTN; + if (c == 'h') { + nas[cn].type = TYPE_INT16; + c = *p++; + } else if (c == 'L') { + /* XXX not quite sure here */ + nas[cn].type = TYPE_INT64; + c = *p++; + } else if (c == 'l') { + nas[cn].type = TYPE_INT32; + c = *p++; + if (c == 'l') { + nas[cn].type = TYPE_INT64; + c = *p++; + } + } else if (c == 'z') { + if (sizeof(size_t) == sizeof(PRInt32)) { + nas[ cn ].type = TYPE_INT32; + } else if (sizeof(size_t) == sizeof(PRInt64)) { + nas[ cn ].type = TYPE_INT64; + } else { + nas[ cn ].type = TYPE_UNKNOWN; + } + c = *p++; + } + + /* format */ + switch (c) { + case 'd': + case 'c': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + break; + + case 'e': + case 'f': + case 'g': + nas[ cn ].type = TYPE_DOUBLE; + break; + + case 'p': + /* XXX should use cpp */ + if (sizeof(void *) == sizeof(PRInt32)) { + nas[ cn ].type = TYPE_UINT32; + } else if (sizeof(void *) == sizeof(PRInt64)) { + nas[ cn ].type = TYPE_UINT64; + } else if (sizeof(void *) == sizeof(PRIntn)) { + nas[ cn ].type = TYPE_UINTN; + } else { + nas[ cn ].type = TYPE_UNKNOWN; + } + break; + + case 'S': +#ifdef WIN32 + nas[ cn ].type = TYPE_WSTRING; + break; +#endif + case 'C': + case 'E': + case 'G': + /* XXX not supported I suppose */ + PR_ASSERT(0); + nas[ cn ].type = TYPE_UNKNOWN; + break; + + case 's': + nas[ cn ].type = TYPE_STRING; + break; + + case 'n': + nas[ cn ].type = TYPE_INTSTR; + break; + + default: + PR_ASSERT(0); + nas[ cn ].type = TYPE_UNKNOWN; + break; + } + + /* get a legal para. */ + if( nas[ cn ].type == TYPE_UNKNOWN ) { + *rv = -1; + break; + } + } + + + /* + ** third pass + ** fill the nas[cn].ap + */ + + if( *rv < 0 ) { + if( nas != nasArray ) { + PR_DELETE( nas ); + } + return NULL; + } + + cn = 0; + while( cn < number ) { + if( nas[cn].type == TYPE_UNKNOWN ) { + cn++; + continue; + } + + switch( nas[cn].type ) { + case TYPE_INT16: + case TYPE_UINT16: + case TYPE_INTN: + nas[cn].u.i = va_arg( ap, int ); + break; + + case TYPE_UINTN: + nas[cn].u.ui = va_arg( ap, unsigned int ); + break; + + case TYPE_INT32: + nas[cn].u.i32 = va_arg( ap, PRInt32 ); + break; + + case TYPE_UINT32: + nas[cn].u.ui32 = va_arg( ap, PRUint32 ); + break; + + case TYPE_INT64: + nas[cn].u.ll = va_arg( ap, PRInt64 ); + break; + + case TYPE_UINT64: + nas[cn].u.ull = va_arg( ap, PRUint64 ); + break; + + case TYPE_STRING: + nas[cn].u.s = va_arg( ap, char* ); + break; + +#ifdef WIN32 + case TYPE_WSTRING: + nas[cn].u.ws = va_arg( ap, WCHAR* ); + break; +#endif + + case TYPE_INTSTR: + nas[cn].u.ip = va_arg( ap, int* ); + break; + + case TYPE_DOUBLE: + nas[cn].u.d = va_arg( ap, double ); + break; + + default: + if( nas != nasArray ) { + PR_DELETE( nas ); + } + *rv = -1; + return NULL; + } + + cn++; + } + + + return nas; +} + +/* +** The workhorse sprintf code. +*/ +static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) +{ + char c; + int flags, width, prec, radix, type; + union { + char ch; + int i; + long l; + PRInt64 ll; + double d; + const char *s; + int *ip; +#ifdef WIN32 + const WCHAR *ws; +#endif + } u; + const char *fmt0; + static char *hex = "0123456789abcdef"; + static char *HEX = "0123456789ABCDEF"; + char *hexp; + int rv, i; + struct NumArg* nas = NULL; + struct NumArg* nap = NULL; + struct NumArg nasArray[ NAS_DEFAULT_NUM ]; + char pattern[20]; + const char* dolPt = NULL; /* in "%4$.2f", dolPt will point to . */ +#ifdef WIN32 + char *pBuf = NULL; +#endif + + /* + ** build an argument array, IF the fmt is numbered argument + ** list style, to contain the Numbered Argument list pointers + */ + + nas = BuildArgArray( fmt, ap, &rv, nasArray ); + if( rv < 0 ) { + /* the fmt contains error Numbered Argument format, jliu@netscape.com */ + PR_ASSERT(0); + return rv; + } + + while ((c = *fmt++) != 0) { + if (c != '%') { + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + continue; + } + fmt0 = fmt - 1; + + /* + ** Gobble up the % format string. Hopefully we have handled all + ** of the strange cases! + */ + flags = 0; + c = *fmt++; + if (c == '%') { + /* quoting a % with %% */ + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + continue; + } + + if( nas != NULL ) { + /* the fmt contains the Numbered Arguments feature */ + i = 0; + while( c && c != '$' ) { /* should improve error check later */ + i = ( i * 10 ) + ( c - '0' ); + c = *fmt++; + } + + if( nas[i-1].type == TYPE_UNKNOWN ) { + if( nas && ( nas != nasArray ) ) { + PR_DELETE( nas ); + } + return -1; + } + + nap = &nas[i-1]; + dolPt = fmt; + c = *fmt++; + } + + /* + * Examine optional flags. Note that we do not implement the + * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is + * somewhat ambiguous and not ideal, which is perhaps why + * the various sprintf() implementations are inconsistent + * on this feature. + */ + while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { + if (c == '-') { + flags |= FLAG_LEFT; + } + if (c == '+') { + flags |= FLAG_SIGNED; + } + if (c == ' ') { + flags |= FLAG_SPACED; + } + if (c == '0') { + flags |= FLAG_ZEROS; + } + c = *fmt++; + } + if (flags & FLAG_SIGNED) { + flags &= ~FLAG_SPACED; + } + if (flags & FLAG_LEFT) { + flags &= ~FLAG_ZEROS; + } + + /* width */ + if (c == '*') { + c = *fmt++; + width = va_arg(ap, int); + } else { + width = 0; + while ((c >= '0') && (c <= '9')) { + width = (width * 10) + (c - '0'); + c = *fmt++; + } + } + + /* precision */ + prec = -1; + if (c == '.') { + c = *fmt++; + if (c == '*') { + c = *fmt++; + prec = va_arg(ap, int); + } else { + prec = 0; + while ((c >= '0') && (c <= '9')) { + prec = (prec * 10) + (c - '0'); + c = *fmt++; + } + } + } + + /* size */ + type = TYPE_INTN; + if (c == 'h') { + type = TYPE_INT16; + c = *fmt++; + } else if (c == 'L') { + /* XXX not quite sure here */ + type = TYPE_INT64; + c = *fmt++; + } else if (c == 'l') { + type = TYPE_INT32; + c = *fmt++; + if (c == 'l') { + type = TYPE_INT64; + c = *fmt++; + } + } else if (c == 'z') { + if (sizeof(size_t) == sizeof(PRInt32)) { + type = TYPE_INT32; + } else if (sizeof(size_t) == sizeof(PRInt64)) { + type = TYPE_INT64; + } + c = *fmt++; + } + + /* format */ + hexp = hex; + switch (c) { + case 'd': case 'i': /* decimal/integer */ + radix = 10; + goto fetch_and_convert; + + case 'o': /* octal */ + radix = 8; + type |= 1; + goto fetch_and_convert; + + case 'u': /* unsigned decimal */ + radix = 10; + type |= 1; + goto fetch_and_convert; + + case 'x': /* unsigned hex */ + radix = 16; + type |= 1; + goto fetch_and_convert; + + case 'X': /* unsigned HEX */ + radix = 16; + hexp = HEX; + type |= 1; + goto fetch_and_convert; + +fetch_and_convert: + switch (type) { + case TYPE_INT16: + u.l = nas ? nap->u.i : va_arg(ap, int); + if (u.l < 0) { + u.l = -u.l; + flags |= FLAG_NEG; + } + goto do_long; + case TYPE_UINT16: + u.l = (nas ? nap->u.i : va_arg(ap, int)) & 0xffff; + goto do_long; + case TYPE_INTN: + u.l = nas ? nap->u.i : va_arg(ap, int); + if (u.l < 0) { + u.l = -u.l; + flags |= FLAG_NEG; + } + goto do_long; + case TYPE_UINTN: + u.l = (long)(nas ? nap->u.ui : va_arg(ap, unsigned int)); + goto do_long; + + case TYPE_INT32: + u.l = nas ? nap->u.i32 : va_arg(ap, PRInt32); + if (u.l < 0) { + u.l = -u.l; + flags |= FLAG_NEG; + } + goto do_long; + case TYPE_UINT32: + u.l = (long)(nas ? nap->u.ui32 : va_arg(ap, PRUint32)); +do_long: + rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); + if (rv < 0) { + return rv; + } + break; + + case TYPE_INT64: + u.ll = nas ? nap->u.ll : va_arg(ap, PRInt64); + if (!LL_GE_ZERO(u.ll)) { + LL_NEG(u.ll, u.ll); + flags |= FLAG_NEG; + } + goto do_longlong; + case TYPE_UINT64: + u.ll = nas ? nap->u.ull : va_arg(ap, PRUint64); +do_longlong: + rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); + if (rv < 0) { + return rv; + } + break; + } + break; + + case 'e': + case 'E': + case 'f': + case 'g': + u.d = nas ? nap->u.d : va_arg(ap, double); + if( nas != NULL ) { + i = fmt - dolPt; + if( i < sizeof( pattern ) ) { + pattern[0] = '%'; + memcpy( &pattern[1], dolPt, i ); + rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); + } + } else { + rv = cvt_f(ss, u.d, fmt0, fmt); + } + + if (rv < 0) { + return rv; + } + break; + + case 'c': + u.ch = nas ? nap->u.i : va_arg(ap, int); + if ((flags & FLAG_LEFT) == 0) { + while (width-- > 1) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + } + rv = (*ss->stuff)(ss, &u.ch, 1); + if (rv < 0) { + return rv; + } + if (flags & FLAG_LEFT) { + while (width-- > 1) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + } + break; + + case 'p': + if (sizeof(void *) == sizeof(PRInt32)) { + type = TYPE_UINT32; + } else if (sizeof(void *) == sizeof(PRInt64)) { + type = TYPE_UINT64; + } else if (sizeof(void *) == sizeof(int)) { + type = TYPE_UINTN; + } else { + PR_ASSERT(0); + break; + } + radix = 16; + goto fetch_and_convert; + +#ifndef WIN32 + case 'S': + /* XXX not supported I suppose */ + PR_ASSERT(0); + break; +#endif + +#if 0 + case 'C': + case 'E': + case 'G': + /* XXX not supported I suppose */ + PR_ASSERT(0); + break; +#endif + +#ifdef WIN32 + case 'S': + u.ws = nas ? nap->u.ws : va_arg(ap, const WCHAR*); + + /* Get the required size in rv */ + rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL); + if (rv == 0) { + rv = 1; + } + pBuf = PR_MALLOC(rv); + WideCharToMultiByte(CP_ACP, 0, u.ws, -1, pBuf, (int)rv, NULL, NULL); + pBuf[rv-1] = '\0'; + + rv = cvt_s(ss, pBuf, width, prec, flags); + + /* We don't need the allocated buffer anymore */ + PR_Free(pBuf); + if (rv < 0) { + return rv; + } + break; + +#endif + + case 's': + u.s = nas ? nap->u.s : va_arg(ap, const char*); + rv = cvt_s(ss, u.s, width, prec, flags); + if (rv < 0) { + return rv; + } + break; + + case 'n': + u.ip = nas ? nap->u.ip : va_arg(ap, int*); + if (u.ip) { + *u.ip = ss->cur - ss->base; + } + break; + + default: + /* Not a % token after all... skip it */ +#if 0 + PR_ASSERT(0); +#endif + rv = (*ss->stuff)(ss, "%", 1); + if (rv < 0) { + return rv; + } + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + } + } + + /* Stuff trailing NUL */ + rv = (*ss->stuff)(ss, "\0", 1); + + if( nas && ( nas != nasArray ) ) { + PR_DELETE( nas ); + } + + return rv; +} + +/************************************************************************/ + +static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len) +{ + int rv; + + /* + ** We will add len to ss->maxlen at the end of the function. First check + ** if ss->maxlen + len would overflow or be greater than PR_INT32_MAX. + */ + if (PR_UINT32_MAX - ss->maxlen < len || ss->maxlen + len > PR_INT32_MAX) { + return -1; + } + rv = (*ss->func)(ss->arg, sp, len); + if (rv < 0) { + return rv; + } + ss->maxlen += len; + return 0; +} + +PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg, + const char *fmt, ...) +{ + va_list ap; + PRUint32 rv; + + va_start(ap, fmt); + rv = PR_vsxprintf(func, arg, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg, + const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = FuncStuff; + ss.func = func; + ss.arg = arg; + ss.maxlen = 0; + rv = dosprintf(&ss, fmt, ap); + return (rv < 0) ? (PRUint32)-1 : ss.maxlen; +} + +/* +** Stuff routine that automatically grows the malloc'd output buffer +** before it overflows. +*/ +static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len) +{ + ptrdiff_t off; + char *newbase; + PRUint32 newlen; + + off = ss->cur - ss->base; + if (PR_UINT32_MAX - len < off) { + /* off + len would be too big. */ + return -1; + } + if (off + len >= ss->maxlen) { + /* Grow the buffer */ + PRUint32 increment = (len > 32) ? len : 32; + if (PR_UINT32_MAX - ss->maxlen < increment) { + /* ss->maxlen + increment would overflow. */ + return -1; + } + newlen = ss->maxlen + increment; + if (newlen > PR_INT32_MAX) { + return -1; + } + if (ss->base) { + newbase = (char*) PR_REALLOC(ss->base, newlen); + } else { + newbase = (char*) PR_MALLOC(newlen); + } + if (!newbase) { + /* Ran out of memory */ + return -1; + } + ss->base = newbase; + ss->maxlen = newlen; + ss->cur = ss->base + off; + } + + /* Copy data */ + while (len) { + --len; + *ss->cur++ = *sp++; + } + PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen); + return 0; +} + +/* +** sprintf into a malloc'd buffer +*/ +PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = PR_vsmprintf(fmt, ap); + va_end(ap); + return rv; +} + +/* +** Free memory allocated, for the caller, by PR_smprintf +*/ +PR_IMPLEMENT(void) PR_smprintf_free(char *mem) +{ + PR_DELETE(mem); +} + +PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = GrowStuff; + ss.base = 0; + ss.cur = 0; + ss.maxlen = 0; + rv = dosprintf(&ss, fmt, ap); + if (rv < 0) { + if (ss.base) { + PR_DELETE(ss.base); + } + return 0; + } + return ss.base; +} + +/* +** Stuff routine that discards overflow data +*/ +static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len) +{ + PRUint32 limit = ss->maxlen - (ss->cur - ss->base); + + if (len > limit) { + len = limit; + } + while (len) { + --len; + *ss->cur++ = *sp++; + } + return 0; +} + +/* +** sprintf into a fixed size buffer. Make sure there is a NUL at the end +** when finished. +*/ +PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...) +{ + va_list ap; + PRUint32 rv; + + va_start(ap, fmt); + rv = PR_vsnprintf(out, outlen, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt, + va_list ap) +{ + SprintfState ss; + PRUint32 n; + + PR_ASSERT(outlen != 0 && outlen <= PR_INT32_MAX); + if (outlen == 0 || outlen > PR_INT32_MAX) { + return 0; + } + + ss.stuff = LimitStuff; + ss.base = out; + ss.cur = out; + ss.maxlen = outlen; + (void) dosprintf(&ss, fmt, ap); + + /* If we added chars, and we didn't append a null, do it now. */ + if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') ) { + *(ss.cur - 1) = '\0'; + } + + n = ss.cur - ss.base; + return n ? n - 1 : n; +} + +PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = PR_vsprintf_append(last, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = GrowStuff; + if (last) { + size_t lastlen = strlen(last); + if (lastlen > PR_INT32_MAX) { + return 0; + } + ss.base = last; + ss.cur = last + lastlen; + ss.maxlen = lastlen; + } else { + ss.base = 0; + ss.cur = 0; + ss.maxlen = 0; + } + rv = dosprintf(&ss, fmt, ap); + if (rv < 0) { + if (ss.base) { + PR_DELETE(ss.base); + } + return 0; + } + return ss.base; +} + diff --git a/nsprpub/pr/src/io/prscanf.c b/nsprpub/pr/src/io/prscanf.c new file mode 100644 index 0000000000..9923ea27c7 --- /dev/null +++ b/nsprpub/pr/src/io/prscanf.c @@ -0,0 +1,630 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Scan functions for NSPR types + * + * Author: Wan-Teh Chang + * + * Acknowledgment: The implementation is inspired by the source code + * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992. + */ + +#include <limits.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "prprf.h" +#include "prdtoa.h" +#include "prlog.h" +#include "prerror.h" + +/* + * A function that reads a character from 'stream'. + * Returns the character read, or EOF if end of stream is reached. + */ +typedef int (*_PRGetCharFN)(void *stream); + +/* + * A function that pushes the character 'ch' back to 'stream'. + */ +typedef void (*_PRUngetCharFN)(void *stream, int ch); + +/* + * The size specifier for the integer and floating point number + * conversions in format control strings. + */ +typedef enum { + _PR_size_none, /* No size specifier is given */ + _PR_size_h, /* The 'h' specifier, suggesting "short" */ + _PR_size_l, /* The 'l' specifier, suggesting "long" */ + _PR_size_L, /* The 'L' specifier, meaning a 'long double' */ + _PR_size_ll /* The 'll' specifier, suggesting "long long" */ +} _PRSizeSpec; + +/* + * The collection of data that is passed between the scan function + * and its subordinate functions. The fields of this structure + * serve as the input or output arguments for these functions. + */ +typedef struct { + _PRGetCharFN get; /* get a character from input stream */ + _PRUngetCharFN unget; /* unget (push back) a character */ + void *stream; /* argument for get and unget */ + va_list ap; /* the variable argument list */ + int nChar; /* number of characters read from 'stream' */ + + PRBool assign; /* assign, or suppress assignment? */ + int width; /* field width */ + _PRSizeSpec sizeSpec; /* 'h', 'l', 'L', or 'll' */ + + PRBool converted; /* is the value actually converted? */ +} ScanfState; + +#define GET(state) ((state)->nChar++, (state)->get((state)->stream)) +#define UNGET(state, ch) \ + ((state)->nChar--, (state)->unget((state)->stream, ch)) + +/* + * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH, + * are always used together. + * + * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return + * value to 'ch' only if we have not exceeded the field width of + * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of + * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true. + */ + +#define GET_IF_WITHIN_WIDTH(state, ch) \ + if (--(state)->width >= 0) { \ + (ch) = GET(state); \ + } +#define WITHIN_WIDTH(state) ((state)->width >= 0) + +/* + * _pr_strtoull: + * Convert a string to an unsigned 64-bit integer. The string + * 'str' is assumed to be a representation of the integer in + * base 'base'. + * + * Warning: + * - Only handle base 8, 10, and 16. + * - No overflow checking. + */ + +static PRUint64 +_pr_strtoull(const char *str, char **endptr, int base) +{ + static const int BASE_MAX = 16; + static const char digits[] = "0123456789abcdef"; + char *digitPtr; + PRUint64 x; /* return value */ + PRInt64 base64; + const char *cPtr; + PRBool negative; + const char *digitStart; + + PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16); + if (base < 0 || base == 1 || base > BASE_MAX) { + if (endptr) { + *endptr = (char *) str; + return LL_ZERO; + } + } + + cPtr = str; + while (isspace(*cPtr)) { + ++cPtr; + } + + negative = PR_FALSE; + if (*cPtr == '-') { + negative = PR_TRUE; + cPtr++; + } else if (*cPtr == '+') { + cPtr++; + } + + if (base == 16) { + if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) { + cPtr += 2; + } + } else if (base == 0) { + if (*cPtr != '0') { + base = 10; + } else if (cPtr[1] == 'x' || cPtr[1] == 'X') { + base = 16; + cPtr += 2; + } else { + base = 8; + } + } + PR_ASSERT(base != 0); + LL_I2L(base64, base); + digitStart = cPtr; + + /* Skip leading zeros */ + while (*cPtr == '0') { + cPtr++; + } + + LL_I2L(x, 0); + while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) { + PRUint64 d; + + LL_I2L(d, (digitPtr - digits)); + LL_MUL(x, x, base64); + LL_ADD(x, x, d); + cPtr++; + } + + if (cPtr == digitStart) { + if (endptr) { + *endptr = (char *) str; + } + return LL_ZERO; + } + + if (negative) { +#ifdef HAVE_LONG_LONG + /* The cast to a signed type is to avoid a compiler warning */ + x = -(PRInt64)x; +#else + LL_NEG(x, x); +#endif + } + + if (endptr) { + *endptr = (char *) cPtr; + } + return x; +} + +/* + * The maximum field width (in number of characters) that is enough + * (may be more than necessary) to represent a 64-bit integer or + * floating point number. + */ +#define FMAX 31 +#define DECIMAL_POINT '.' + +static PRStatus +GetInt(ScanfState *state, int code) +{ + char buf[FMAX + 1], *p; + int ch = 0; + static const char digits[] = "0123456789abcdefABCDEF"; + PRBool seenDigit = PR_FALSE; + int base; + int dlen; + + switch (code) { + case 'd': case 'u': + base = 10; + break; + case 'i': + base = 0; + break; + case 'x': case 'X': case 'p': + base = 16; + break; + case 'o': + base = 8; + break; + default: + return PR_FAILURE; + } + if (state->width == 0 || state->width > FMAX) { + state->width = FMAX; + } + p = buf; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + if (WITHIN_WIDTH(state) && ch == '0') { + seenDigit = PR_TRUE; + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) + && (ch == 'x' || ch == 'X') + && (base == 0 || base == 16)) { + base = 16; + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } else if (base == 0) { + base = 8; + } + } + if (base == 0 || base == 10) { + dlen = 10; + } else if (base == 8) { + dlen = 8; + } else { + PR_ASSERT(base == 16); + dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */ + } + while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + seenDigit = PR_TRUE; + } + if (WITHIN_WIDTH(state)) { + UNGET(state, ch); + } + if (!seenDigit) { + return PR_FAILURE; + } + *p = '\0'; + if (state->assign) { + if (code == 'd' || code == 'i') { + if (state->sizeSpec == _PR_size_ll) { + PRInt64 llval = _pr_strtoull(buf, NULL, base); + *va_arg(state->ap, PRInt64 *) = llval; + } else { + long lval = strtol(buf, NULL, base); + + if (state->sizeSpec == _PR_size_none) { + *va_arg(state->ap, PRIntn *) = lval; + } else if (state->sizeSpec == _PR_size_h) { + *va_arg(state->ap, PRInt16 *) = (PRInt16)lval; + } else if (state->sizeSpec == _PR_size_l) { + *va_arg(state->ap, PRInt32 *) = lval; + } else { + return PR_FAILURE; + } + } + } else { + if (state->sizeSpec == _PR_size_ll) { + PRUint64 llval = _pr_strtoull(buf, NULL, base); + *va_arg(state->ap, PRUint64 *) = llval; + } else { + unsigned long lval = strtoul(buf, NULL, base); + + if (state->sizeSpec == _PR_size_none) { + *va_arg(state->ap, PRUintn *) = lval; + } else if (state->sizeSpec == _PR_size_h) { + *va_arg(state->ap, PRUint16 *) = (PRUint16)lval; + } else if (state->sizeSpec == _PR_size_l) { + *va_arg(state->ap, PRUint32 *) = lval; + } else { + return PR_FAILURE; + } + } + } + state->converted = PR_TRUE; + } + return PR_SUCCESS; +} + +static PRStatus +GetFloat(ScanfState *state) +{ + char buf[FMAX + 1], *p; + int ch = 0; + PRBool seenDigit = PR_FALSE; + + if (state->width == 0 || state->width > FMAX) { + state->width = FMAX; + } + p = buf; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + while (WITHIN_WIDTH(state) && isdigit(ch)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + seenDigit = PR_TRUE; + } + if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + while (WITHIN_WIDTH(state) && isdigit(ch)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + seenDigit = PR_TRUE; + } + } + + /* + * This is not robust. For example, "1.2e+" would confuse + * the code below to read 'e' and '+', only to realize that + * it should have stopped at "1.2". But we can't push back + * more than one character, so there is nothing I can do. + */ + + /* Parse exponent */ + if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + while (WITHIN_WIDTH(state) && isdigit(ch)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + } + if (WITHIN_WIDTH(state)) { + UNGET(state, ch); + } + if (!seenDigit) { + return PR_FAILURE; + } + *p = '\0'; + if (state->assign) { + PRFloat64 dval = PR_strtod(buf, NULL); + + state->converted = PR_TRUE; + if (state->sizeSpec == _PR_size_l) { + *va_arg(state->ap, PRFloat64 *) = dval; + } else if (state->sizeSpec == _PR_size_L) { + *va_arg(state->ap, long double *) = dval; + } else { + *va_arg(state->ap, float *) = (float) dval; + } + } + return PR_SUCCESS; +} + +/* + * Convert, and return the end of the conversion spec. + * Return NULL on error. + */ + +static const char * +Convert(ScanfState *state, const char *fmt) +{ + const char *cPtr; + int ch; + char *cArg = NULL; + + state->converted = PR_FALSE; + cPtr = fmt; + if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') { + do { + ch = GET(state); + } while (isspace(ch)); + UNGET(state, ch); + } + switch (*cPtr) { + case 'c': + if (state->assign) { + cArg = va_arg(state->ap, char *); + } + if (state->width == 0) { + state->width = 1; + } + for (; state->width > 0; state->width--) { + ch = GET(state); + if (ch == EOF) { + return NULL; + } + if (state->assign) { + *cArg++ = ch; + } + } + if (state->assign) { + state->converted = PR_TRUE; + } + break; + case 'p': + case 'd': case 'i': case 'o': + case 'u': case 'x': case 'X': + if (GetInt(state, *cPtr) == PR_FAILURE) { + return NULL; + } + break; + case 'e': case 'E': case 'f': + case 'g': case 'G': + if (GetFloat(state) == PR_FAILURE) { + return NULL; + } + break; + case 'n': + /* do not consume any input */ + if (state->assign) { + switch (state->sizeSpec) { + case _PR_size_none: + *va_arg(state->ap, PRIntn *) = state->nChar; + break; + case _PR_size_h: + *va_arg(state->ap, PRInt16 *) = state->nChar; + break; + case _PR_size_l: + *va_arg(state->ap, PRInt32 *) = state->nChar; + break; + case _PR_size_ll: + LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar); + break; + default: + PR_ASSERT(0); + } + } + break; + case 's': + if (state->width == 0) { + state->width = INT_MAX; + } + if (state->assign) { + cArg = va_arg(state->ap, char *); + } + for (; state->width > 0; state->width--) { + ch = GET(state); + if ((ch == EOF) || isspace(ch)) { + UNGET(state, ch); + break; + } + if (state->assign) { + *cArg++ = ch; + } + } + if (state->assign) { + *cArg = '\0'; + state->converted = PR_TRUE; + } + break; + case '%': + ch = GET(state); + if (ch != '%') { + UNGET(state, ch); + return NULL; + } + break; + case '[': + { + PRBool complement = PR_FALSE; + const char *closeBracket; + size_t n; + + if (*++cPtr == '^') { + complement = PR_TRUE; + cPtr++; + } + closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']'); + if (closeBracket == NULL) { + return NULL; + } + n = closeBracket - cPtr; + if (state->width == 0) { + state->width = INT_MAX; + } + if (state->assign) { + cArg = va_arg(state->ap, char *); + } + for (; state->width > 0; state->width--) { + ch = GET(state); + if ((ch == EOF) + || (!complement && !memchr(cPtr, ch, n)) + || (complement && memchr(cPtr, ch, n))) { + UNGET(state, ch); + break; + } + if (state->assign) { + *cArg++ = ch; + } + } + if (state->assign) { + *cArg = '\0'; + state->converted = PR_TRUE; + } + cPtr = closeBracket; + } + break; + default: + return NULL; + } + return cPtr; +} + +static PRInt32 +DoScanf(ScanfState *state, const char *fmt) +{ + PRInt32 nConverted = 0; + const char *cPtr; + int ch; + + state->nChar = 0; + cPtr = fmt; + while (1) { + if (isspace(*cPtr)) { + /* white space: skip */ + do { + cPtr++; + } while (isspace(*cPtr)); + do { + ch = GET(state); + } while (isspace(ch)); + UNGET(state, ch); + } else if (*cPtr == '%') { + /* format spec: convert */ + cPtr++; + state->assign = PR_TRUE; + if (*cPtr == '*') { + cPtr++; + state->assign = PR_FALSE; + } + for (state->width = 0; isdigit(*cPtr); cPtr++) { + state->width = state->width * 10 + *cPtr - '0'; + } + state->sizeSpec = _PR_size_none; + if (*cPtr == 'h') { + cPtr++; + state->sizeSpec = _PR_size_h; + } else if (*cPtr == 'l') { + cPtr++; + if (*cPtr == 'l') { + cPtr++; + state->sizeSpec = _PR_size_ll; + } else { + state->sizeSpec = _PR_size_l; + } + } else if (*cPtr == 'L') { + cPtr++; + state->sizeSpec = _PR_size_L; + } + cPtr = Convert(state, cPtr); + if (cPtr == NULL) { + return (nConverted > 0 ? nConverted : EOF); + } + if (state->converted) { + nConverted++; + } + cPtr++; + } else { + /* others: must match */ + if (*cPtr == '\0') { + return nConverted; + } + ch = GET(state); + if (ch != *cPtr) { + UNGET(state, ch); + return nConverted; + } + cPtr++; + } + } +} + +static int +StringGetChar(void *stream) +{ + char *cPtr = *((char **) stream); + + if (*cPtr == '\0') { + return EOF; + } + *((char **) stream) = cPtr + 1; + return (unsigned char) *cPtr; +} + +static void +StringUngetChar(void *stream, int ch) +{ + char *cPtr = *((char **) stream); + + if (ch != EOF) { + *((char **) stream) = cPtr - 1; + } +} + +PR_IMPLEMENT(PRInt32) +PR_sscanf(const char *buf, const char *fmt, ...) +{ + PRInt32 rv; + ScanfState state; + + state.get = &StringGetChar; + state.unget = &StringUngetChar; + state.stream = (void *) &buf; + va_start(state.ap, fmt); + rv = DoScanf(&state, fmt); + va_end(state.ap); + return rv; +} diff --git a/nsprpub/pr/src/io/prsocket.c b/nsprpub/pr/src/io/prsocket.c new file mode 100644 index 0000000000..4ca2bad8f2 --- /dev/null +++ b/nsprpub/pr/src/io/prsocket.c @@ -0,0 +1,2024 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> + +#if defined(_WIN64) +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif +#endif + +/************************************************************************/ + +/* These two functions are only used in assertions. */ +#if defined(DEBUG) + +PRBool IsValidNetAddr(const PRNetAddr *addr) +{ + if ((addr != NULL) +#if defined(XP_UNIX) || defined(XP_OS2) + && (addr->raw.family != PR_AF_LOCAL) +#endif + && (addr->raw.family != PR_AF_INET6) + && (addr->raw.family != PR_AF_INET)) { + return PR_FALSE; + } + return PR_TRUE; +} + +static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len) +{ + /* + * The definition of the length of a Unix domain socket address + * is not uniform, so we don't check it. + */ + if ((addr != NULL) +#if defined(XP_UNIX) || defined(XP_OS2) + && (addr->raw.family != AF_UNIX) +#endif + && (PR_NETADDR_SIZE(addr) != addr_len)) { +#if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1 + /* + * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2 + * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id + * field and is 28 bytes. It is possible for socket functions + * to return an addr_len greater than sizeof(struct sockaddr_in6). + * We need to allow that. (Bugzilla bug #77264) + */ + if ((PR_AF_INET6 == addr->raw.family) + && (sizeof(addr->ipv6) == addr_len)) { + return PR_TRUE; + } +#endif + /* + * The accept(), getsockname(), etc. calls on some platforms + * do not set the actual socket address length on return. + * In this case, we verifiy addr_len is still the value we + * passed in (i.e., sizeof(PRNetAddr)). + */ +#if defined(QNX) + if (sizeof(PRNetAddr) == addr_len) { + return PR_TRUE; + } +#endif + return PR_FALSE; + } + return PR_TRUE; +} + +#endif /* DEBUG */ + +static PRInt32 PR_CALLBACK SocketWritev(PRFileDesc *fd, const PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + int w = 0; + const PRIOVec *tmp_iov; +#define LOCAL_MAXIOV 8 + PRIOVec local_iov[LOCAL_MAXIOV]; + PRIOVec *iov_copy = NULL; + int tmp_out; + int index, iov_cnt; + int count=0, sz = 0; /* 'count' is the return value. */ + + 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; + } + + /* + * Assume the first writev will succeed. Copy iov's only on + * failure. + */ + tmp_iov = iov; + for (index = 0; index < iov_size; index++) { + sz += iov[index].iov_len; + } + + iov_cnt = iov_size; + + while (sz > 0) { + + w = _PR_MD_WRITEV(fd, tmp_iov, iov_cnt, timeout); + if (w < 0) { + count = -1; + break; + } + count += w; + if (fd->secret->nonblocking) { + break; + } + sz -= w; + + if (sz > 0) { + /* find the next unwritten vector */ + for ( index = 0, tmp_out = count; + tmp_out >= iov[index].iov_len; + tmp_out -= iov[index].iov_len, index++) {;} /* nothing to execute */ + + if (tmp_iov == iov) { + /* + * The first writev failed so we + * must copy iov's around. + * Avoid calloc/free if there + * are few enough iov's. + */ + if (iov_size - index <= LOCAL_MAXIOV) { + iov_copy = local_iov; + } + else if ((iov_copy = (PRIOVec *) PR_CALLOC((iov_size - index) * + sizeof *iov_copy)) == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + tmp_iov = iov_copy; + } + + PR_ASSERT(tmp_iov == iov_copy); + + /* fill in the first partial read */ + iov_copy[0].iov_base = &(((char *)iov[index].iov_base)[tmp_out]); + iov_copy[0].iov_len = iov[index].iov_len - tmp_out; + index++; + + /* copy the remaining vectors */ + for (iov_cnt=1; index<iov_size; iov_cnt++, index++) { + iov_copy[iov_cnt].iov_base = iov[index].iov_base; + iov_copy[iov_cnt].iov_len = iov[index].iov_len; + } + } + } + + if (iov_copy != local_iov) { + PR_DELETE(iov_copy); + } + return count; +} + +/************************************************************************/ + +PR_IMPLEMENT(PRFileDesc *) PR_ImportTCPSocket(PROsfd osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (fd != NULL) { + _PR_MD_MAKE_NONBLOCK(fd); + _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); +#ifdef _PR_NEED_SECRET_AF + /* this means we can only import IPv4 sockets here. + * but this is what the function in ptio.c does. + * We need a way to import IPv6 sockets, too. + */ + fd->secret->af = AF_INET; +#endif + } else { + _PR_MD_CLOSE_SOCKET(osfd); + } + return(fd); +} + +PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PROsfd osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); + if (fd != NULL) { + _PR_MD_MAKE_NONBLOCK(fd); + _PR_MD_INIT_FD_INHERITABLE(fd, PR_TRUE); + } else { + _PR_MD_CLOSE_SOCKET(osfd); + } + return(fd); +} + + +static const PRIOMethods* PR_GetSocketPollFdMethods(void); + +PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PROsfd osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + fd = _PR_Getfd(); + + if (fd == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->secret->md.osfd = osfd; + fd->secret->inheritable = _PR_TRI_FALSE; + fd->secret->state = _PR_FILEDESC_OPEN; + fd->methods = PR_GetSocketPollFdMethods(); + } + + return fd; +} /* PR_CreateSocketPollFD */ + +PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd) +{ + if (NULL == fd) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + _PR_Putfd(fd); + return PR_SUCCESS; +} /* PR_DestroySocketPollFd */ + +static PRStatus PR_CALLBACK SocketConnect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 rv; /* Return value of _PR_MD_CONNECT */ + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return PR_FAILURE; + } +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + + rv = _PR_MD_CONNECT(fd, addrp, PR_NETADDR_SIZE(addr), timeout); + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("connect -> %d", rv)); + if (rv == 0) { + return PR_SUCCESS; + } + else { + return PR_FAILURE; + } +} + +static PRStatus PR_CALLBACK SocketConnectContinue( + PRFileDesc *fd, PRInt16 out_flags) +{ + PROsfd osfd; + int err; + + if (out_flags & PR_POLL_NVAL) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) { + PR_ASSERT(out_flags == 0); + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + + osfd = fd->secret->md.osfd; + +#if defined(XP_UNIX) + + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + +#elif defined(WIN32) || defined(WIN16) + + if (out_flags & PR_POLL_EXCEPT) { + int len = sizeof(err); + if (getsockopt(osfd, (int)SOL_SOCKET, SO_ERROR, (char *) &err, &len) + == SOCKET_ERROR) { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } else { +#if defined(_WIN64) + if (fd->secret->overlappedActive) { + PRInt32 rvSent; + if (GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE) == FALSE) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue GetOverlappedResult failed %d\n", err)); + if (err != ERROR_IO_INCOMPLETE) { + _PR_MD_MAP_CONNECT_ERROR(err); + fd->secret->overlappedActive = PR_FALSE; + } + } + } + if (err == 0) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } +#else + PR_SetError(PR_UNKNOWN_ERROR, 0); +#endif + } + return PR_FAILURE; + } + + PR_ASSERT(out_flags & PR_POLL_WRITE); + +#if defined(_WIN64) + if (fd->secret->alreadyConnected) { + fd->secret->alreadyConnected = PR_FALSE; + } + /* TCP Fast Open on Windows must use ConnectEx, which uses overlapped + * input/output. + * To get result we need to use GetOverlappedResult. */ + if (fd->secret->overlappedActive) { + PR_ASSERT(fd->secret->nonblocking); + PRInt32 rvSent; + if (GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) { + fd->secret->overlappedActive = PR_FALSE; + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue GetOverlappedResult succeeded\n")); + /* When ConnectEx is used, all previously set socket options and + * property are not enabled and to enable them + * SO_UPDATE_CONNECT_CONTEXT option need to be set. */ + if (setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err)); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + } else { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketConnectContinue GetOverlappedResult failed %d\n", err)); + if (err != ERROR_IO_INCOMPLETE) { + _PR_MD_MAP_CONNECT_ERROR(err); + fd->secret->overlappedActive = PR_FALSE; + return PR_FAILURE; + } else { + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + } + } +#endif + + return PR_SUCCESS; + +#elif defined(XP_OS2) + + err = _MD_os2_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + + +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} + +PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) +{ + /* Find the NSPR layer and invoke its connectcontinue method */ + PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + + if (NULL == bottom) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + return SocketConnectContinue(bottom, pd->out_flags); +} + +static PRFileDesc* PR_CALLBACK SocketAccept(PRFileDesc *fd, PRNetAddr *addr, + PRIntervalTime timeout) +{ + PROsfd osfd; + PRFileDesc *fd2; + PRUint32 al; + PRThread *me = _PR_MD_CURRENT_THREAD(); +#ifdef WINNT + PRNetAddr addrCopy; +#endif + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return 0; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return 0; + } + +#ifdef WINNT + if (addr == NULL) { + addr = &addrCopy; + } +#endif + al = sizeof(PRNetAddr); + osfd = _PR_MD_ACCEPT(fd, addr, &al, timeout); + if (osfd == -1) { + return 0; + } + + fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (!fd2) { + _PR_MD_CLOSE_SOCKET(osfd); + return NULL; + } + + fd2->secret->nonblocking = fd->secret->nonblocking; + fd2->secret->inheritable = fd->secret->inheritable; +#ifdef WINNT + if (!fd2->secret->nonblocking && fd2->secret->inheritable != _PR_TRI_TRUE) { + /* + * The new socket has been associated with an I/O + * completion port. There is no going back. + */ + fd2->secret->md.io_model_committed = PR_TRUE; + } + PR_ASSERT(al == PR_NETADDR_SIZE(addr)); + fd2->secret->md.accepted_socket = PR_TRUE; + memcpy(&fd2->secret->md.peer_addr, addr, al); +#endif + + /* + * On some platforms, the new socket created by accept() + * inherits the nonblocking (or overlapped io) attribute + * of the listening socket. As an optimization, these + * platforms can skip the following _PR_MD_MAKE_NONBLOCK + * call. + */ +#if !defined(SOLARIS) && !defined(WINNT) + _PR_MD_MAKE_NONBLOCK(fd2); +#endif + +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, al) == PR_TRUE); + + return fd2; +} + +#ifdef WINNT +PR_IMPLEMENT(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr, + PRIntervalTime timeout) +{ + PROsfd osfd; + PRFileDesc *fd2; + PRIntn al; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr addrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return 0; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return 0; + } + + if (addr == NULL) { + addr = &addrCopy; + } + al = PR_NETADDR_SIZE(addr); + osfd = _PR_MD_FAST_ACCEPT(fd, addr, &al, timeout, PR_TRUE, NULL, NULL); + if (osfd == -1) { + return 0; + } + + fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (!fd2) { + _PR_MD_CLOSE_SOCKET(osfd); + } else { + fd2->secret->nonblocking = fd->secret->nonblocking; + fd2->secret->md.io_model_committed = PR_TRUE; + PR_ASSERT(al == PR_NETADDR_SIZE(addr)); + fd2->secret->md.accepted_socket = PR_TRUE; + memcpy(&fd2->secret->md.peer_addr, addr, al); +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif +#ifdef _PR_NEED_SECRET_AF + fd2->secret->af = fd->secret->af; +#endif + } + return fd2; +} +#endif /* WINNT */ + + +static PRStatus PR_CALLBACK SocketBind(PRFileDesc *fd, const PRNetAddr *addr) +{ + PRInt32 result; + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + +#ifdef XP_UNIX + if (addr->raw.family == AF_UNIX) { + /* Disallow relative pathnames */ + if (addr->local.path[0] != '/') { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + } +#endif /* XP_UNIX */ + +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + result = _PR_MD_BIND(fd, addrp, PR_NETADDR_SIZE(addr)); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketListen(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 result; + + result = _PR_MD_LISTEN(fd, backlog); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketShutdown(PRFileDesc *fd, PRIntn how) +{ + PRInt32 result; + + result = _PR_MD_SHUTDOWN(fd, how); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketRecv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if ((flags != 0) && (flags != PR_MSG_PEEK)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_LOG(_pr_io_lm, PR_LOG_MAX, + ("recv: fd=%p osfd=%" PR_PRIdOSFD " buf=%p amount=%d flags=%d", + fd, fd->secret->md.osfd, buf, amount, flags)); + +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBytes != 0) { + rv = (amount < fd->secret->peekBytes) ? + amount : fd->secret->peekBytes; + memcpy(buf, fd->secret->peekBuffer, rv); + if (flags == 0) { + /* consume the bytes in the peek buffer */ + fd->secret->peekBytes -= rv; + if (fd->secret->peekBytes != 0) { + memmove(fd->secret->peekBuffer, + fd->secret->peekBuffer + rv, + fd->secret->peekBytes); + } + } + return rv; + } + + /* allocate peek buffer, if necessary */ + if ((PR_MSG_PEEK == flags) && _PR_FD_NEED_EMULATE_MSG_PEEK(fd)) { + PR_ASSERT(0 == fd->secret->peekBytes); + /* impose a max size on the peek buffer */ + if (amount > _PR_PEEK_BUFFER_MAX) { + amount = _PR_PEEK_BUFFER_MAX; + } + if (fd->secret->peekBufSize < amount) { + if (fd->secret->peekBuffer) { + PR_Free(fd->secret->peekBuffer); + } + fd->secret->peekBufSize = amount; + fd->secret->peekBuffer = PR_Malloc(amount); + if (NULL == fd->secret->peekBuffer) { + fd->secret->peekBufSize = 0; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + } + } +#endif + + rv = _PR_MD_RECV(fd, buf, amount, flags, timeout); + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv -> %d, error = %d, os error = %d", + rv, PR_GetError(), PR_GetOSError())); + +#ifdef _PR_HAVE_PEEK_BUFFER + if ((PR_MSG_PEEK == flags) && _PR_FD_NEED_EMULATE_MSG_PEEK(fd)) { + if (rv > 0) { + memcpy(fd->secret->peekBuffer, buf, rv); + fd->secret->peekBytes = rv; + } + } +#endif + + return rv; +} + +static PRInt32 PR_CALLBACK SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return SocketRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} + +static PRInt32 PR_CALLBACK SocketSend(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 temp, count; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + count = 0; + while (amount > 0) { + PR_LOG(_pr_io_lm, PR_LOG_MAX, + ("send: fd=%p osfd=%" PR_PRIdOSFD " buf=%p amount=%d", + fd, fd->secret->md.osfd, buf, amount)); + temp = _PR_MD_SEND(fd, buf, amount, flags, timeout); + if (temp < 0) { + count = -1; + break; + } + + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + + amount -= temp; + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("send -> %d", count)); + return count; +} + +static PRInt32 PR_CALLBACK SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return SocketSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} + +static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd) +{ + if (!fd || !fd->secret + || (fd->secret->state != _PR_FILEDESC_OPEN + && fd->secret->state != _PR_FILEDESC_CLOSED)) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + + if (fd->secret->state == _PR_FILEDESC_OPEN) { +#if defined(_WIN64) && defined(WIN95) + /* TCP Fast Open on Windows must use ConnectEx, which uses overlapped + * input/output. Before closing such a socket we must cancelIO. + */ + if (fd->secret->overlappedActive) { + PR_ASSERT(fd->secret->nonblocking); + if (CancelIo((HANDLE) fd->secret->md.osfd) == TRUE) { + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketClose - CancelIo succeeded\n")); + } else { + DWORD err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketClose - CancelIo failed err=%x\n", err)); + } + + DWORD rvSent; + if (GetOverlappedResult((HANDLE)fd->secret->md.osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE) { + fd->secret->overlappedActive = PR_FALSE; + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketClose GetOverlappedResult succeeded\n")); + } else { + DWORD err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("SocketClose GetOverlappedResult failed %d\n", err)); + if (err != ERROR_IO_INCOMPLETE) { + _PR_MD_MAP_CONNECT_ERROR(err); + fd->secret->overlappedActive = PR_FALSE; + } + } + } + + if (fd->secret->overlappedActive && + _fd_waiting_for_overlapped_done_lock) { + // Put osfd into the list to be checked later. + PRFileDescList *forWaiting = PR_NEW(PRFileDescList); + if (!forWaiting) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_FAILURE; + } + forWaiting->fd = fd; + + PR_Lock(_fd_waiting_for_overlapped_done_lock); + forWaiting->next = _fd_waiting_for_overlapped_done; + _fd_waiting_for_overlapped_done = forWaiting; + PR_Unlock(_fd_waiting_for_overlapped_done_lock); + + return PR_SUCCESS; + } +#endif + + if (_PR_MD_CLOSE_SOCKET(fd->secret->md.osfd) < 0) { + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + } + +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBuffer) { + PR_ASSERT(fd->secret->peekBufSize > 0); + PR_DELETE(fd->secret->peekBuffer); + fd->secret->peekBufSize = 0; + fd->secret->peekBytes = 0; + } +#endif + + PR_FreeFileDesc(fd); + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketAvailable(PRFileDesc *fd) +{ + PRInt32 rv; +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBytes != 0) { + return fd->secret->peekBytes; + } +#endif + rv = _PR_MD_SOCKETAVAILABLE(fd); + return rv; +} + +static PRInt64 PR_CALLBACK SocketAvailable64(PRFileDesc *fd) +{ + PRInt64 rv; +#ifdef _PR_HAVE_PEEK_BUFFER + if (fd->secret->peekBytes != 0) { + LL_I2L(rv, fd->secret->peekBytes); + return rv; + } +#endif + LL_I2L(rv, _PR_MD_SOCKETAVAILABLE(fd)); + return rv; +} + +static PRStatus PR_CALLBACK SocketSync(PRFileDesc *fd) +{ + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketSendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 temp, count; + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + + count = 0; + do { + temp = _PR_MD_SENDTO(fd, buf, amount, flags, + addrp, PR_NETADDR_SIZE(addr), timeout); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } while (amount > 0); + return count; +} + +#if defined(_WIN64) && defined(WIN95) +static PRInt32 PR_CALLBACK SocketTCPSendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 temp, count; + const PRNetAddr *addrp = addr; +#if defined(_PR_INET6) + PRNetAddr addrCopy; +#endif + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; + } +#endif + + count = 0; + while (amount > 0) { + temp = _PR_MD_TCPSENDTO(fd, buf, amount, flags, + addrp, PR_NETADDR_SIZE(addr), timeout); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } + return count; +} +#endif + +static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 rv; + PRUint32 al; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + al = sizeof(PRNetAddr); + rv = _PR_MD_RECVFROM(fd, buf, amount, flags, addr, &al, timeout); +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) { + addr->raw.family = PR_AF_INET6; + } +#endif + return rv; +} + +static PRInt32 PR_CALLBACK SocketAcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, PRInt32 amount, + PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + *nd = NULL; + +#if defined(WINNT) + { + PROsfd newSock; + PRNetAddr *raddrCopy; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); +#ifdef _PR_INET6 + if (AF_INET6 == *raddr->raw.family) { + *raddr->raw.family = PR_AF_INET6; + } +#endif + } + } + } +#else + rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); +#endif + return rv; +} + +#ifdef WINNT +PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, PRInt32 amount, + PRIntervalTime timeout) +{ + PRInt32 rv; + PROsfd newSock; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr *raddrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, + timeout, PR_TRUE, NULL, NULL); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); +#ifdef _PR_INET6 + if (AF_INET6 == *raddr->raw.family) { + *raddr->raw.family = PR_AF_INET6; + } +#endif +#ifdef _PR_NEED_SECRET_AF + (*nd)->secret->af = sd->secret->af; +#endif + } + } + return rv; +} + +PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback( + PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, PRInt32 amount, + PRIntervalTime timeout, + _PR_AcceptTimeoutCallback callback, + void *callbackArg) +{ + PRInt32 rv; + PROsfd newSock; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr *raddrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, + timeout, PR_TRUE, callback, callbackArg); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); +#ifdef _PR_INET6 + if (AF_INET6 == *raddr->raw.family) { + *raddr->raw.family = PR_AF_INET6; + } +#endif +#ifdef _PR_NEED_SECRET_AF + (*nd)->secret->af = sd->secret->af; +#endif + } + } + return rv; +} +#endif /* WINNT */ + +#ifdef WINNT +PR_IMPLEMENT(void) +PR_NTFast_UpdateAcceptContext(PRFileDesc *socket, PRFileDesc *acceptSocket) +{ + _PR_MD_UPDATE_ACCEPT_CONTEXT( + socket->secret->md.osfd, acceptSocket->secret->md.osfd); +} +#endif /* WINNT */ + +static PRInt32 PR_CALLBACK SocketSendFile( + 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 + rv = PR_EmulateSendFile(sd, sfd, flags, timeout); +#endif /* WINNT */ + + return rv; +} + +static PRInt32 PR_CALLBACK SocketTransmitFile(PRFileDesc *sd, PRFileDesc *fd, + const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, + PRIntervalTime timeout) +{ + PRSendFileData sfd; + + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; + + return(SocketSendFile(sd, &sfd, flags, timeout)); +} + +static PRStatus PR_CALLBACK SocketGetName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRInt32 result; + PRUint32 addrlen; + + addrlen = sizeof(PRNetAddr); + result = _PR_MD_GETSOCKNAME(fd, addr, &addrlen); + if (result < 0) { + return PR_FAILURE; + } +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE); + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketGetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRInt32 result; + PRUint32 addrlen; + + addrlen = sizeof(PRNetAddr); + result = _PR_MD_GETPEERNAME(fd, addr, &addrlen); + if (result < 0) { + return PR_FAILURE; + } +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE); + return PR_SUCCESS; +} + +static PRInt16 PR_CALLBACK SocketPoll( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + *out_flags = 0; + +#if defined(_WIN64) + if (in_flags & PR_POLL_WRITE) { + if (fd->secret->alreadyConnected) { + *out_flags = PR_POLL_WRITE; + return PR_POLL_WRITE; + } + } +#endif + return in_flags; +} /* SocketPoll */ + +static PRIOMethods tcpMethods = { + PR_DESC_SOCKET_TCP, + SocketClose, + SocketRead, + SocketWrite, + SocketAvailable, + SocketAvailable64, + SocketSync, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + SocketWritev, + SocketConnect, + SocketAccept, + SocketBind, + SocketListen, + SocketShutdown, + SocketRecv, + SocketSend, + (PRRecvfromFN)_PR_InvalidInt, +#if defined(_WIN64) && defined(WIN95) + SocketTCPSendTo, /* This is for fast open. We imitate Linux interface. */ +#else + (PRSendtoFN)_PR_InvalidInt, +#endif + SocketPoll, + SocketAcceptRead, + SocketTransmitFile, + SocketGetName, + SocketGetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + _PR_SocketGetSocketOption, + _PR_SocketSetSocketOption, + SocketSendFile, + SocketConnectContinue, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods udpMethods = { + PR_DESC_SOCKET_UDP, + SocketClose, + SocketRead, + SocketWrite, + SocketAvailable, + SocketAvailable64, + SocketSync, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + SocketWritev, + SocketConnect, + (PRAcceptFN)_PR_InvalidDesc, + SocketBind, + SocketListen, + SocketShutdown, + SocketRecv, + SocketSend, + SocketRecvFrom, + SocketSendTo, + SocketPoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + SocketGetName, + SocketGetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + _PR_SocketGetSocketOption, + _PR_SocketSetSocketOption, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + + +static PRIOMethods socketpollfdMethods = { + (PRDescType) 0, + (PRCloseFN)_PR_InvalidStatus, + (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, + SocketPoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods() +{ + return &tcpMethods; +} + +PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods() +{ + return &udpMethods; +} + +static const PRIOMethods* PR_GetSocketPollFdMethods() +{ + return &socketpollfdMethods; +} /* PR_GetSocketPollFdMethods */ + +#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE) +PR_EXTERN(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd); + +#if defined(_PR_INET6_PROBE) + +extern PRBool _pr_ipv6_is_present(void); + +PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket() +{ + PROsfd osfd; + + osfd = _PR_MD_SOCKET(AF_INET6, SOCK_STREAM, 0); + if (osfd != -1) { + _PR_MD_CLOSE_SOCKET(osfd); + return PR_TRUE; + } + return PR_FALSE; +} +#endif /* _PR_INET6_PROBE */ + +#endif + +PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PROsfd osfd; + PRFileDesc *fd; + PRInt32 tmp_domain = domain; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + if (PR_AF_INET != domain + && PR_AF_INET6 != domain +#if defined(XP_UNIX) || defined(XP_OS2) + && PR_AF_LOCAL != domain +#endif + ) { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return NULL; + } + +#if defined(_PR_INET6_PROBE) + if (PR_AF_INET6 == domain) { + domain = _pr_ipv6_is_present() ? AF_INET6 : AF_INET; + } +#elif defined(_PR_INET6) + if (PR_AF_INET6 == domain) { + domain = AF_INET6; + } +#else + if (PR_AF_INET6 == domain) { + domain = AF_INET; + } +#endif /* _PR_INET6 */ + osfd = _PR_MD_SOCKET(domain, type, proto); + if (osfd == -1) { + return 0; + } + if (type == SOCK_STREAM) { + fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + } + else { + fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); + } + /* + * Make the sockets non-blocking + */ + if (fd != NULL) { + _PR_MD_MAKE_NONBLOCK(fd); + _PR_MD_INIT_FD_INHERITABLE(fd, PR_FALSE); +#ifdef _PR_NEED_SECRET_AF + fd->secret->af = domain; +#endif +#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) + /* + * For platforms with no support for IPv6 + * create layered socket for IPv4-mapped IPv6 addresses + */ + if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) { + if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) { + PR_Close(fd); + fd = NULL; + } + } +#endif + } else { + _PR_MD_CLOSE_SOCKET(osfd); + } + + return fd; +} + +PR_IMPLEMENT(PRFileDesc *) PR_NewTCPSocket(void) +{ + PRInt32 domain = AF_INET; + + return PR_Socket(domain, SOCK_STREAM, 0); +} + +PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void) +{ + PRInt32 domain = AF_INET; + + 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 + PRInt32 rv, osfd[2]; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd); + if (rv == -1) { + return PR_FAILURE; + } + + f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); + if (!f[0]) { + _PR_MD_CLOSE_SOCKET(osfd[0]); + _PR_MD_CLOSE_SOCKET(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); + if (!f[1]) { + PR_Close(f[0]); + _PR_MD_CLOSE_SOCKET(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + _PR_MD_MAKE_NONBLOCK(f[0]); + _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE); + _PR_MD_MAKE_NONBLOCK(f[1]); + _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE); + return PR_SUCCESS; +#elif defined(WINNT) + /* + * A socket pair is often used for interprocess communication, + * so we need to make sure neither socket is associated with + * the I/O completion port; otherwise it can't be used by a + * child process. + * + * The default implementation below cannot be used for NT + * because PR_Accept would have associated the I/O completion + * port with the listening and accepted sockets. + */ + SOCKET listenSock; + SOCKET osfd[2]; + struct sockaddr_in selfAddr, peerAddr; + int addrLen; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + osfd[0] = osfd[1] = INVALID_SOCKET; + listenSock = socket(AF_INET, SOCK_STREAM, 0); + if (listenSock == INVALID_SOCKET) { + goto failed; + } + selfAddr.sin_family = AF_INET; + selfAddr.sin_port = 0; + selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* BugZilla: 35408 */ + addrLen = sizeof(selfAddr); + if (bind(listenSock, (struct sockaddr *) &selfAddr, + addrLen) == SOCKET_ERROR) { + goto failed; + } + if (getsockname(listenSock, (struct sockaddr *) &selfAddr, + &addrLen) == SOCKET_ERROR) { + goto failed; + } + if (listen(listenSock, 5) == SOCKET_ERROR) { + goto failed; + } + osfd[0] = socket(AF_INET, SOCK_STREAM, 0); + if (osfd[0] == INVALID_SOCKET) { + goto failed; + } + selfAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* + * Only a thread is used to do the connect and accept. + * I am relying on the fact that connect returns + * successfully as soon as the connect request is put + * into the listen queue (but before accept is called). + * This is the behavior of the BSD socket code. If + * connect does not return until accept is called, we + * will need to create another thread to call connect. + */ + if (connect(osfd[0], (struct sockaddr *) &selfAddr, + addrLen) == SOCKET_ERROR) { + goto failed; + } + /* + * A malicious local process may connect to the listening + * socket, so we need to verify that the accepted connection + * is made from our own socket osfd[0]. + */ + if (getsockname(osfd[0], (struct sockaddr *) &selfAddr, + &addrLen) == SOCKET_ERROR) { + goto failed; + } + osfd[1] = accept(listenSock, (struct sockaddr *) &peerAddr, &addrLen); + if (osfd[1] == INVALID_SOCKET) { + goto failed; + } + if (peerAddr.sin_port != selfAddr.sin_port) { + /* the connection we accepted is not from osfd[0] */ + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + goto failed; + } + closesocket(listenSock); + + f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); + if (!f[0]) { + closesocket(osfd[0]); + closesocket(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); + if (!f[1]) { + PR_Close(f[0]); + closesocket(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + _PR_MD_INIT_FD_INHERITABLE(f[0], PR_FALSE); + _PR_MD_INIT_FD_INHERITABLE(f[1], PR_FALSE); + return PR_SUCCESS; + +failed: + if (listenSock != INVALID_SOCKET) { + closesocket(listenSock); + } + if (osfd[0] != INVALID_SOCKET) { + closesocket(osfd[0]); + } + if (osfd[1] != INVALID_SOCKET) { + closesocket(osfd[1]); + } + return PR_FAILURE; +#else /* not Unix or NT */ + /* + * default implementation + */ + PRFileDesc *listenSock; + PRNetAddr selfAddr, peerAddr; + PRUint16 port; + + f[0] = f[1] = NULL; + listenSock = PR_NewTCPSocket(); + if (listenSock == NULL) { + goto failed; + } + PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr); /* BugZilla: 35408 */ + if (PR_Bind(listenSock, &selfAddr) == PR_FAILURE) { + goto failed; + } + if (PR_GetSockName(listenSock, &selfAddr) == PR_FAILURE) { + goto failed; + } + port = ntohs(selfAddr.inet.port); + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + goto failed; + } + f[0] = PR_NewTCPSocket(); + if (f[0] == NULL) { + goto failed; + } +#ifdef _PR_CONNECT_DOES_NOT_BIND + /* + * If connect does not implicitly bind the socket (e.g., on + * BeOS), we have to bind the socket so that we can get its + * port with getsockname later. + */ + PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &selfAddr); + if (PR_Bind(f[0], &selfAddr) == PR_FAILURE) { + goto failed; + } +#endif + PR_InitializeNetAddr(PR_IpAddrLoopback, port, &selfAddr); + + /* + * Only a thread is used to do the connect and accept. + * I am relying on the fact that PR_Connect returns + * successfully as soon as the connect request is put + * into the listen queue (but before PR_Accept is called). + * This is the behavior of the BSD socket code. If + * connect does not return until accept is called, we + * will need to create another thread to call connect. + */ + if (PR_Connect(f[0], &selfAddr, PR_INTERVAL_NO_TIMEOUT) + == PR_FAILURE) { + goto failed; + } + /* + * A malicious local process may connect to the listening + * socket, so we need to verify that the accepted connection + * is made from our own socket f[0]. + */ + if (PR_GetSockName(f[0], &selfAddr) == PR_FAILURE) { + goto failed; + } + f[1] = PR_Accept(listenSock, &peerAddr, PR_INTERVAL_NO_TIMEOUT); + if (f[1] == NULL) { + goto failed; + } + if (peerAddr.inet.port != selfAddr.inet.port) { + /* the connection we accepted is not from f[0] */ + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + goto failed; + } + PR_Close(listenSock); + return PR_SUCCESS; + +failed: + if (listenSock) { + PR_Close(listenSock); + } + if (f[0]) { + PR_Close(f[0]); + } + if (f[1]) { + PR_Close(f[1]); + } + return PR_FAILURE; +#endif +} + +PR_IMPLEMENT(PROsfd) +PR_FileDesc2NativeHandle(PRFileDesc *fd) +{ + if (fd) { + fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER); + } + if (!fd) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + return fd->secret->md.osfd; +} + +PR_IMPLEMENT(void) +PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PROsfd handle) +{ + if (fd) { + fd->secret->md.osfd = handle; + } +} + +/* +** Select compatibility +** +*/ + +PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set) +{ + memset(set, 0, sizeof(PR_fd_set)); +} + +PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set) +{ + PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC ); + + set->harray[set->hsize++] = fh; +} + +PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index, index2; + + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + for (index2=index; index2 < (set->hsize-1); index2++) { + set->harray[index2] = set->harray[index2+1]; + } + set->hsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index; + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + return 1; + } + return 0; +} + +PR_IMPLEMENT(void) PR_FD_NSET(PROsfd fd, PR_fd_set *set) +{ + PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC ); + + set->narray[set->nsize++] = fd; +} + +PR_IMPLEMENT(void) PR_FD_NCLR(PROsfd fd, PR_fd_set *set) +{ + PRUint32 index, index2; + + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + for (index2=index; index2 < (set->nsize-1); index2++) { + set->narray[index2] = set->narray[index2+1]; + } + set->nsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PROsfd fd, PR_fd_set *set) +{ + PRUint32 index; + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + return 1; + } + return 0; +} + + +#if !defined(NEED_SELECT) +#include "obsolete/probslet.h" + +#define PD_INCR 20 + +static PRPollDesc *_pr_setfd( + PR_fd_set *set, PRInt16 flags, PRPollDesc *polldesc) +{ + PRUintn fsidx, pdidx; + PRPollDesc *poll = polldesc; + + if (NULL == set) { + return poll; + } + + /* First set the pr file handle osfds */ + for (fsidx = 0; fsidx < set->hsize; fsidx++) + { + for (pdidx = 0; 1; pdidx++) + { + if ((PRFileDesc*)-1 == poll[pdidx].fd) + { + /* our vector is full - extend and condition it */ + poll = (PRPollDesc*)PR_Realloc( + poll, (pdidx + 1 + PD_INCR) * sizeof(PRPollDesc)); + if (NULL == poll) { + goto out_of_memory; + } + memset( + poll + pdidx * sizeof(PRPollDesc), + 0, PD_INCR * sizeof(PRPollDesc)); + poll[pdidx + PD_INCR].fd = (PRFileDesc*)-1; + } + if ((NULL == poll[pdidx].fd) + || (poll[pdidx].fd == set->harray[fsidx])) + { + /* PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); */ + /* either empty or prevously defined */ + poll[pdidx].fd = set->harray[fsidx]; /* possibly redundant */ + poll[pdidx].in_flags |= flags; /* possibly redundant */ + break; + } + } + } + +#if 0 + /* Second set the native osfds */ + for (fsidx = 0; fsidx < set->nsize; fsidx++) + { + for (pdidx = 0; ((PRFileDesc*)-1 != poll[pdidx].fd); pdidx++) + { + if ((PRFileDesc*)-1 == poll[pdidx].fd) + { + /* our vector is full - extend and condition it */ + poll = PR_Realloc( + poll, (pdidx + PD_INCR) * sizeof(PRPollDesc)); + if (NULL == poll) { + goto out_of_memory; + } + memset( + poll + pdidx * sizeof(PRPollDesc), + 0, PD_INCR * sizeof(PRPollDesc)); + poll[(pdidx + PD_INCR)].fd = (PRFileDesc*)-1; + } + if ((NULL == poll[pdidx].fd) + || (poll[pdidx].fd == set->narray[fsidx])) + { + /* either empty or prevously defined */ + poll[pdidx].fd = set->narray[fsidx]; + PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); + poll[pdidx].in_flags |= flags; + break; + } + } + } +#endif /* 0 */ + + return poll; + +out_of_memory: + if (NULL != polldesc) { + PR_DELETE(polldesc); + } + return NULL; +} /* _pr_setfd */ + +#endif /* !defined(NEED_SELECT) */ + +PR_IMPLEMENT(PRInt32) PR_Select( + PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr, + PR_fd_set *pr_ex, PRIntervalTime timeout) +{ + +#if !defined(NEED_SELECT) + PRInt32 npds = 0; + /* + ** Find out how many fds are represented in the three lists. + ** Then allocate a polling descriptor for the logical union + ** (there can't be any overlapping) and call PR_Poll(). + */ + + PRPollDesc *copy, *poll; + + static PRBool warning = PR_TRUE; + if (warning) { + warning = _PR_Obsolete( "PR_Select()", "PR_Poll()"); + } + + /* try to get an initial guesss at how much space we need */ + npds = 0; + if ((NULL != pr_rd) && ((pr_rd->hsize + pr_rd->nsize - npds) > 0)) { + npds = pr_rd->hsize + pr_rd->nsize; + } + if ((NULL != pr_wr) && ((pr_wr->hsize + pr_wr->nsize - npds) > 0)) { + npds = pr_wr->hsize + pr_wr->nsize; + } + if ((NULL != pr_ex) && ((pr_ex->hsize + pr_ex->nsize - npds) > 0)) { + npds = pr_ex->hsize + pr_ex->nsize; + } + + if (0 == npds) + { + PR_Sleep(timeout); + return 0; + } + + copy = poll = (PRPollDesc*)PR_Calloc(npds + PD_INCR, sizeof(PRPollDesc)); + if (NULL == poll) { + goto out_of_memory; + } + poll[npds + PD_INCR - 1].fd = (PRFileDesc*)-1; + + poll = _pr_setfd(pr_rd, PR_POLL_READ, poll); + if (NULL == poll) { + goto out_of_memory; + } + poll = _pr_setfd(pr_wr, PR_POLL_WRITE, poll); + if (NULL == poll) { + goto out_of_memory; + } + poll = _pr_setfd(pr_ex, PR_POLL_EXCEPT, poll); + if (NULL == poll) { + goto out_of_memory; + } + unused = 0; + while (NULL != poll[unused].fd && (PRFileDesc*)-1 != poll[unused].fd) + { + ++unused; + } + + PR_ASSERT(unused > 0); + npds = PR_Poll(poll, unused, timeout); + + if (npds > 0) + { + /* Copy the results back into the fd sets */ + if (NULL != pr_rd) { + pr_rd->nsize = pr_rd->hsize = 0; + } + if (NULL != pr_wr) { + pr_wr->nsize = pr_wr->hsize = 0; + } + if (NULL != pr_ex) { + pr_ex->nsize = pr_ex->hsize = 0; + } + for (copy = &poll[unused - 1]; copy >= poll; --copy) + { + if (copy->out_flags & PR_POLL_NVAL) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + npds = -1; + break; + } + if (copy->out_flags & PR_POLL_READ) + if (NULL != pr_rd) { + pr_rd->harray[pr_rd->hsize++] = copy->fd; + } + if (copy->out_flags & PR_POLL_WRITE) + if (NULL != pr_wr) { + pr_wr->harray[pr_wr->hsize++] = copy->fd; + } + if (copy->out_flags & PR_POLL_EXCEPT) + if (NULL != pr_ex) { + pr_ex->harray[pr_ex->hsize++] = copy->fd; + } + } + } + PR_DELETE(poll); + + return npds; +out_of_memory: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + +#endif /* !defined(NEED_SELECT) */ + +} diff --git a/nsprpub/pr/src/io/prstdio.c b/nsprpub/pr/src/io/prstdio.c new file mode 100644 index 0000000000..74b85d9cf1 --- /dev/null +++ b/nsprpub/pr/src/io/prstdio.c @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> + +/* +** fprintf to a PRFileDesc +*/ +PR_IMPLEMENT(PRUint32) PR_fprintf(PRFileDesc* fd, const char *fmt, ...) +{ + va_list ap; + PRUint32 rv; + + va_start(ap, fmt); + rv = PR_vfprintf(fd, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_vfprintf(PRFileDesc* fd, const char *fmt, va_list ap) +{ + /* XXX this could be better */ + PRUint32 rv, len; + char* msg = PR_vsmprintf(fmt, ap); + if (NULL == msg) { + return -1; + } + len = strlen(msg); +#ifdef XP_OS2 + /* + * OS/2 really needs a \r for every \n. + * In the future we should try to use scatter-gather instead of a + * succession of PR_Write. + */ + if (isatty(PR_FileDesc2NativeHandle(fd))) { + PRUint32 last = 0, idx; + PRInt32 tmp; + rv = 0; + for (idx = 0; idx < len+1; idx++) { + if ((idx - last > 0) && (('\n' == msg[idx]) || (idx == len))) { + tmp = PR_Write(fd, msg + last, idx - last); + if (tmp >= 0) { + rv += tmp; + } + last = idx; + } + /* + * if current character is \n, and + * previous character isn't \r, and + * next character isn't \r + */ + if (('\n' == msg[idx]) && + ((0 == idx) || ('\r' != msg[idx-1])) && + ('\r' != msg[idx+1])) { + /* add extra \r */ + tmp = PR_Write(fd, "\r", 1); + if (tmp >= 0) { + rv += tmp; + } + } + } + } else { + rv = PR_Write(fd, msg, len); + } +#else + rv = PR_Write(fd, msg, len); +#endif + PR_DELETE(msg); + return rv; +} diff --git a/nsprpub/pr/src/linking/Makefile.in b/nsprpub/pr/src/linking/Makefile.in new file mode 100644 index 0000000000..9292e9e91f --- /dev/null +++ b/nsprpub/pr/src/linking/Makefile.in @@ -0,0 +1,31 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = \ + prlink.c \ + $(NULL) + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/linking/prlink.c b/nsprpub/pr/src/linking/prlink.c new file mode 100644 index 0000000000..011ff17e6b --- /dev/null +++ b/nsprpub/pr/src/linking/prlink.c @@ -0,0 +1,1142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> + +#ifdef XP_UNIX +#ifdef USE_DLFCN +#include <dlfcn.h> +/* Define these on systems that don't have them. */ +#ifndef RTLD_NOW +#define RTLD_NOW 0 +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY RTLD_NOW +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif +#ifndef RTLD_LOCAL +#define RTLD_LOCAL 0 +#endif +#ifdef AIX +#include <sys/ldr.h> +#ifndef L_IGNOREUNLOAD /* AIX 4.3.3 does not have L_IGNOREUNLOAD. */ +#define L_IGNOREUNLOAD 0x10000000 +#endif +#endif +#elif defined(USE_HPSHL) +#include <dl.h> +#endif +#endif /* XP_UNIX */ + +#define _PR_DEFAULT_LD_FLAGS PR_LD_LAZY + +/* + * On these platforms, symbols have a leading '_'. + */ +#if defined(XP_OS2) \ + || ((defined(OPENBSD) || defined(NETBSD)) && !defined(__ELF__)) +#define NEED_LEADING_UNDERSCORE +#endif + +#define PR_LD_PATHW 0x8000 /* for PR_LibSpec_PathnameU */ + +/************************************************************************/ + +struct PRLibrary { + char* name; /* Our own copy of the name string */ + PRLibrary* next; + int refCount; + const PRStaticLinkTable* staticTable; + +#ifdef XP_PC +#ifdef XP_OS2 + HMODULE dlh; +#else + HINSTANCE dlh; +#endif +#endif + +#ifdef XP_UNIX +#if defined(USE_HPSHL) + shl_t dlh; +#else + void* dlh; +#endif +#endif + +}; + +static PRLibrary *pr_loadmap; +static PRLibrary *pr_exe_loadmap; +static PRMonitor *pr_linker_lock; +static char* _pr_currentLibPath = NULL; + +static PRLibrary *pr_LoadLibraryByPathname(const char *name, PRIntn flags); + +/************************************************************************/ + +#if !defined(USE_DLFCN) && !defined(HAVE_STRERROR) +#define ERR_STR_BUF_LENGTH 20 +#endif + +static void DLLErrorInternal(PRIntn oserr) +/* +** This whole function, and most of the code in this file, are run +** with a big hairy lock wrapped around it. Not the best of situations, +** but will eventually come up with the right answer. +*/ +{ + const char *error = NULL; +#ifdef USE_DLFCN + error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */ +#elif defined(HAVE_STRERROR) + error = strerror(oserr); /* this should be okay */ +#else + char errStrBuf[ERR_STR_BUF_LENGTH]; + PR_snprintf(errStrBuf, sizeof(errStrBuf), "error %d", oserr); + error = errStrBuf; +#endif + if (NULL != error) { + PR_SetErrorText(strlen(error), error); + } +} /* DLLErrorInternal */ + +void _PR_InitLinker(void) +{ + PRLibrary *lm = NULL; +#if defined(XP_UNIX) + void *h; +#endif + + if (!pr_linker_lock) { + pr_linker_lock = PR_NewNamedMonitor("linker-lock"); + } + PR_EnterMonitor(pr_linker_lock); + +#if defined(XP_PC) + lm = PR_NEWZAP(PRLibrary); + lm->name = strdup("Executable"); +#if defined(XP_OS2) + lm->dlh = NULLHANDLE; +#else + /* A module handle for the executable. */ + lm->dlh = GetModuleHandle(NULL); +#endif /* ! XP_OS2 */ + + lm->refCount = 1; + lm->staticTable = NULL; + pr_exe_loadmap = lm; + pr_loadmap = lm; + +#elif defined(XP_UNIX) +#ifdef HAVE_DLL +#if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL) + h = dlopen(0, RTLD_LAZY); + if (!h) { + char *error; + + DLLErrorInternal(_MD_ERRNO()); + error = (char*)PR_MALLOC(PR_GetErrorTextLength()); + (void) PR_GetErrorText(error); + fprintf(stderr, "failed to initialize shared libraries [%s]\n", + error); + PR_DELETE(error); + abort();/* XXX */ + } +#elif defined(USE_HPSHL) + h = NULL; + /* don't abort with this NULL */ +#elif defined(NO_DLOPEN_NULL) + h = NULL; /* XXXX toshok */ /* XXXX vlad */ +#else +#error no dll strategy +#endif /* USE_DLFCN */ + + lm = PR_NEWZAP(PRLibrary); + if (lm) { + lm->name = strdup("a.out"); + lm->refCount = 1; + lm->dlh = h; + lm->staticTable = NULL; + } + pr_exe_loadmap = lm; + pr_loadmap = lm; +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ + + if (lm) { + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("Loaded library %s (init)", lm->name)); + } + + PR_ExitMonitor(pr_linker_lock); +} + +/* + * _PR_ShutdownLinker does not unload the dlls loaded by the application + * via calls to PR_LoadLibrary. Any dlls that still remain on the + * pr_loadmap list when NSPR shuts down are application programming errors. + * The only exception is pr_exe_loadmap, which was added to the list by + * NSPR and hence should be cleaned up by NSPR. + */ +void _PR_ShutdownLinker(void) +{ + /* FIXME: pr_exe_loadmap should be destroyed. */ + + PR_DestroyMonitor(pr_linker_lock); + pr_linker_lock = NULL; + + if (_pr_currentLibPath) { + free(_pr_currentLibPath); + _pr_currentLibPath = NULL; + } +} + +/******************************************************************************/ + +PR_IMPLEMENT(PRStatus) PR_SetLibraryPath(const char *path) +{ + PRStatus rv = PR_SUCCESS; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_EnterMonitor(pr_linker_lock); + if (_pr_currentLibPath) { + free(_pr_currentLibPath); + } + if (path) { + _pr_currentLibPath = strdup(path); + if (!_pr_currentLibPath) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + rv = PR_FAILURE; + } + } else { + _pr_currentLibPath = 0; + } + PR_ExitMonitor(pr_linker_lock); + return rv; +} + +/* +** Return the library path for finding shared libraries. +*/ +PR_IMPLEMENT(char *) +PR_GetLibraryPath(void) +{ + char *ev; + char *copy = NULL; /* a copy of _pr_currentLibPath */ + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_EnterMonitor(pr_linker_lock); + if (_pr_currentLibPath != NULL) { + goto exit; + } + + /* initialize pr_currentLibPath */ + +#ifdef XP_PC + ev = getenv("LD_LIBRARY_PATH"); + if (!ev) { + ev = ".;\\lib"; + } + ev = strdup(ev); +#endif + +#if defined(XP_UNIX) +#if defined(USE_DLFCN) + { + char *p=NULL; + int len; + + ev = getenv("LD_LIBRARY_PATH"); + if (!ev) { + ev = "/usr/lib:/lib"; + } + len = strlen(ev) + 1; /* +1 for the null */ + + p = (char*) malloc(len); + if (p) { + strcpy(p, ev); + } /* if (p) */ + ev = p; + PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev)); + + } +#else + /* AFAIK there isn't a library path with the HP SHL interface --Rob */ + ev = strdup(""); +#endif +#endif + + /* + * If ev is NULL, we have run out of memory + */ + _pr_currentLibPath = ev; + +exit: + if (_pr_currentLibPath) { + copy = strdup(_pr_currentLibPath); + } + PR_ExitMonitor(pr_linker_lock); + if (!copy) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return copy; +} + +/* +** Build library name from path, lib and extensions +*/ +PR_IMPLEMENT(char*) +PR_GetLibraryName(const char *path, const char *lib) +{ + char *fullname; + +#ifdef XP_PC + if (strstr(lib, PR_DLL_SUFFIX) == NULL) + { + if (path) { + fullname = PR_smprintf("%s\\%s%s", path, lib, PR_DLL_SUFFIX); + } else { + fullname = PR_smprintf("%s%s", lib, PR_DLL_SUFFIX); + } + } else { + if (path) { + fullname = PR_smprintf("%s\\%s", path, lib); + } else { + fullname = PR_smprintf("%s", lib); + } + } +#endif /* XP_PC */ +#if defined(XP_UNIX) + if (strstr(lib, PR_DLL_SUFFIX) == NULL) + { + if (path) { + fullname = PR_smprintf("%s/lib%s%s", path, lib, PR_DLL_SUFFIX); + } else { + fullname = PR_smprintf("lib%s%s", lib, PR_DLL_SUFFIX); + } + } else { + if (path) { + fullname = PR_smprintf("%s/%s", path, lib); + } else { + fullname = PR_smprintf("%s", lib); + } + } +#endif /* XP_UNIX */ + return fullname; +} + +/* +** Free the memory allocated, for the caller, by PR_GetLibraryName +*/ +PR_IMPLEMENT(void) +PR_FreeLibraryName(char *mem) +{ + PR_smprintf_free(mem); +} + +static PRLibrary* +pr_UnlockedFindLibrary(const char *name) +{ + PRLibrary* lm = pr_loadmap; + const char* np = strrchr(name, PR_DIRECTORY_SEPARATOR); + np = np ? np + 1 : name; + while (lm) { + const char* cp = strrchr(lm->name, PR_DIRECTORY_SEPARATOR); + cp = cp ? cp + 1 : lm->name; +#ifdef WIN32 + /* Windows DLL names are case insensitive... */ + if (strcmpi(np, cp) == 0) +#elif defined(XP_OS2) + if (stricmp(np, cp) == 0) +#else + if (strcmp(np, cp) == 0) +#endif + { + /* found */ + lm->refCount++; + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("%s incr => %d (find lib)", + lm->name, lm->refCount)); + return lm; + } + lm = lm->next; + } + return NULL; +} + +PR_IMPLEMENT(PRLibrary*) +PR_LoadLibraryWithFlags(PRLibSpec libSpec, PRIntn flags) +{ + if (flags == 0) { + flags = _PR_DEFAULT_LD_FLAGS; + } + switch (libSpec.type) { + case PR_LibSpec_Pathname: + return pr_LoadLibraryByPathname(libSpec.value.pathname, flags); +#ifdef WIN32 + case PR_LibSpec_PathnameU: + /* + * cast to |char *| and set PR_LD_PATHW flag so that + * it can be cast back to PRUnichar* in the callee. + */ + return pr_LoadLibraryByPathname((const char*) + libSpec.value.pathname_u, + flags | PR_LD_PATHW); +#endif + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } +} + +PR_IMPLEMENT(PRLibrary*) +PR_LoadLibrary(const char *name) +{ + PRLibSpec libSpec; + + libSpec.type = PR_LibSpec_Pathname; + libSpec.value.pathname = name; + return PR_LoadLibraryWithFlags(libSpec, 0); +} + +/* +** Dynamically load a library. Only load libraries once, so scan the load +** map first. +*/ +static PRLibrary* +pr_LoadLibraryByPathname(const char *name, PRIntn flags) +{ + PRLibrary *lm; + PRLibrary* result = NULL; + PRInt32 oserr; +#ifdef WIN32 + char utf8name_stack[MAX_PATH]; + char *utf8name_malloc = NULL; + char *utf8name = utf8name_stack; + PRUnichar wname_stack[MAX_PATH]; + PRUnichar *wname_malloc = NULL; + PRUnichar *wname = wname_stack; + int len; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + /* See if library is already loaded */ + PR_EnterMonitor(pr_linker_lock); + +#ifdef WIN32 + if (flags & PR_LD_PATHW) { + /* cast back what's cast to |char *| for the argument passing. */ + wname = (LPWSTR) name; + } else { + int wlen = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0); + if (wlen > MAX_PATH) { + wname = wname_malloc = PR_Malloc(wlen * sizeof(PRUnichar)); + } + if (wname == NULL || + !MultiByteToWideChar(CP_ACP, 0, name, -1, wname, wlen)) { + oserr = _MD_ERRNO(); + goto unlock; + } + } + len = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL); + if (len > MAX_PATH) { + utf8name = utf8name_malloc = PR_Malloc(len); + } + if (utf8name == NULL || + !WideCharToMultiByte(CP_UTF8, 0, wname, -1, + utf8name, len, NULL, NULL)) { + oserr = _MD_ERRNO(); + goto unlock; + } + /* the list of loaded library names are always kept in UTF-8 + * on Win32 platforms */ + result = pr_UnlockedFindLibrary(utf8name); +#else + result = pr_UnlockedFindLibrary(name); +#endif + + if (result != NULL) { + goto unlock; + } + + lm = PR_NEWZAP(PRLibrary); + if (lm == NULL) { + oserr = _MD_ERRNO(); + goto unlock; + } + lm->staticTable = NULL; + +#ifdef XP_OS2 /* Why isn't all this stuff in MD code?! */ + { + HMODULE h; + UCHAR pszError[_MAX_PATH]; + ULONG ulRc = NO_ERROR; + + ulRc = DosLoadModule(pszError, _MAX_PATH, (PSZ) name, &h); + if (ulRc != NO_ERROR) { + oserr = ulRc; + PR_DELETE(lm); + goto unlock; + } + lm->name = strdup(name); + lm->dlh = h; + lm->next = pr_loadmap; + pr_loadmap = lm; + } +#endif /* XP_OS2 */ + +#ifdef WIN32 + { + HINSTANCE h; + + h = LoadLibraryExW(wname, NULL, + (flags & PR_LD_ALT_SEARCH_PATH) ? + LOAD_WITH_ALTERED_SEARCH_PATH : 0); + if (h == NULL) { + oserr = _MD_ERRNO(); + PR_DELETE(lm); + goto unlock; + } + lm->name = strdup(utf8name); + lm->dlh = h; + lm->next = pr_loadmap; + pr_loadmap = lm; + } +#endif /* WIN32 */ + +#if defined(XP_UNIX) +#ifdef HAVE_DLL + { +#if defined(USE_DLFCN) +#ifdef NTO + /* Neutrino needs RTLD_GROUP to load Netscape plugins. (bug 71179) */ + int dl_flags = RTLD_GROUP; +#elif defined(AIX) + /* AIX needs RTLD_MEMBER to load an archive member. (bug 228899) */ + int dl_flags = RTLD_MEMBER; +#else + int dl_flags = 0; +#endif + void *h = NULL; +#if defined(DARWIN) + PRBool okToLoad = PR_FALSE; +#endif + + if (flags & PR_LD_LAZY) { + dl_flags |= RTLD_LAZY; + } + if (flags & PR_LD_NOW) { + dl_flags |= RTLD_NOW; + } + if (flags & PR_LD_GLOBAL) { + dl_flags |= RTLD_GLOBAL; + } + if (flags & PR_LD_LOCAL) { + dl_flags |= RTLD_LOCAL; + } +#if defined(DARWIN) + /* If the file contains an absolute or relative path (slash) + * and the path doesn't look like a System path, then require + * the file exists. + * The reason is that DARWIN's dlopen ignores the provided path + * and checks for the plain filename in DYLD_LIBRARY_PATH, + * which could load an unexpected version of a library. */ + if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) { + /* no slash, allow to load from any location */ + okToLoad = PR_TRUE; + } else { + const char systemPrefix1[] = "/System/"; + const size_t systemPrefixLen1 = strlen(systemPrefix1); + const char systemPrefix2[] = "/usr/lib/"; + const size_t systemPrefixLen2 = strlen(systemPrefix2); + const size_t name_len = strlen(name); + if (((name_len > systemPrefixLen1) && + (strncmp(name, systemPrefix1, systemPrefixLen1) == 0)) || + ((name_len > systemPrefixLen2) && + (strncmp(name, systemPrefix2, systemPrefixLen2) == 0))) { + /* found at beginning, it's a system library. + * Skip filesystem check (required for macOS 11), + * allow loading from any location */ + okToLoad = PR_TRUE; + } else if (PR_Access(name, PR_ACCESS_EXISTS) == PR_SUCCESS) { + /* file exists, allow to load */ + okToLoad = PR_TRUE; + } + } + if (okToLoad) { + h = dlopen(name, dl_flags); + } +#else + h = dlopen(name, dl_flags); +#endif +#elif defined(USE_HPSHL) + int shl_flags = 0; + shl_t h; + + /* + * Use the DYNAMIC_PATH flag only if 'name' is a plain file + * name (containing no directory) to match the behavior of + * dlopen(). + */ + if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) { + shl_flags |= DYNAMIC_PATH; + } + if (flags & PR_LD_LAZY) { + shl_flags |= BIND_DEFERRED; + } + if (flags & PR_LD_NOW) { + shl_flags |= BIND_IMMEDIATE; + } + /* No equivalent of PR_LD_GLOBAL and PR_LD_LOCAL. */ + h = shl_load(name, shl_flags, 0L); +#else +#error Configuration error +#endif + if (!h) { + oserr = _MD_ERRNO(); + PR_DELETE(lm); + goto unlock; + } + lm->name = strdup(name); + lm->dlh = h; + lm->next = pr_loadmap; + pr_loadmap = lm; + } +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ + + lm->refCount = 1; + + result = lm; /* success */ + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name)); + +unlock: + if (result == NULL) { + PR_SetError(PR_LOAD_LIBRARY_ERROR, oserr); + DLLErrorInternal(oserr); /* sets error text */ + } +#ifdef WIN32 + if (utf8name_malloc) { + PR_Free(utf8name_malloc); + } + if (wname_malloc) { + PR_Free(wname_malloc); + } +#endif + PR_ExitMonitor(pr_linker_lock); + return result; +} + +/* +** Unload a shared library which was loaded via PR_LoadLibrary +*/ +PR_IMPLEMENT(PRStatus) +PR_UnloadLibrary(PRLibrary *lib) +{ + int result = 0; + PRStatus status = PR_SUCCESS; + + if (lib == 0) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + PR_EnterMonitor(pr_linker_lock); + + if (lib->refCount <= 0) { + PR_ExitMonitor(pr_linker_lock); + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + if (--lib->refCount > 0) { + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("%s decr => %d", + lib->name, lib->refCount)); + goto done; + } + +#ifdef XP_UNIX +#ifdef HAVE_DLL +#ifdef USE_DLFCN + result = dlclose(lib->dlh); +#elif defined(USE_HPSHL) + result = shl_unload(lib->dlh); +#else +#error Configuration error +#endif +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ +#ifdef XP_PC + if (lib->dlh) { + FreeLibrary((HINSTANCE)(lib->dlh)); + lib->dlh = (HINSTANCE)NULL; + } +#endif /* XP_PC */ + + /* unlink from library search list */ + if (pr_loadmap == lib) { + pr_loadmap = pr_loadmap->next; + } + else if (pr_loadmap != NULL) { + PRLibrary* prev = pr_loadmap; + PRLibrary* next = pr_loadmap->next; + while (next != NULL) { + if (next == lib) { + prev->next = next->next; + goto freeLib; + } + prev = next; + next = next->next; + } + /* + * fail (the library is not on the _pr_loadmap list), + * but don't wipe out an error from dlclose/shl_unload. + */ + PR_NOT_REACHED("_pr_loadmap and lib->refCount inconsistent"); + if (result == 0) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + status = PR_FAILURE; + } + } + /* + * We free the PRLibrary structure whether dlclose/shl_unload + * succeeds or not. + */ + +freeLib: + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Unloaded library %s", lib->name)); + free(lib->name); + lib->name = NULL; + PR_DELETE(lib); + if (result != 0) { + PR_SetError(PR_UNLOAD_LIBRARY_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + status = PR_FAILURE; + } + +done: + PR_ExitMonitor(pr_linker_lock); + return status; +} + +static void* +pr_FindSymbolInLib(PRLibrary *lm, const char *name) +{ + void *f = NULL; +#ifdef XP_OS2 + int rc; +#endif + + if (lm->staticTable != NULL) { + const PRStaticLinkTable* tp; + for (tp = lm->staticTable; tp->name; tp++) { + if (strcmp(name, tp->name) == 0) { + return (void*) tp->fp; + } + } + /* + ** If the symbol was not found in the static table then check if + ** the symbol was exported in the DLL... Win16 only!! + */ +#if !defined(WIN16) + PR_SetError(PR_FIND_SYMBOL_ERROR, 0); + return (void*)NULL; +#endif + } + +#ifdef XP_OS2 + rc = DosQueryProcAddr(lm->dlh, 0, (PSZ) name, (PFN *) &f); +#if defined(NEED_LEADING_UNDERSCORE) + /* + * Older plugins (not built using GCC) will have symbols that are not + * underscore prefixed. We check for that here. + */ + if (rc != NO_ERROR) { + name++; + DosQueryProcAddr(lm->dlh, 0, (PSZ) name, (PFN *) &f); + } +#endif +#endif /* XP_OS2 */ + +#ifdef WIN32 + f = GetProcAddress(lm->dlh, name); +#endif /* WIN32 */ + +#ifdef XP_UNIX +#ifdef HAVE_DLL +#ifdef USE_DLFCN + f = dlsym(lm->dlh, name); +#elif defined(USE_HPSHL) + if (shl_findsym(&lm->dlh, name, TYPE_PROCEDURE, &f) == -1) { + f = NULL; + } +#endif +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ + if (f == NULL) { + PR_SetError(PR_FIND_SYMBOL_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + } + return f; +} + +/* +** Called by class loader to resolve missing native's +*/ +PR_IMPLEMENT(void*) +PR_FindSymbol(PRLibrary *lib, const char *raw_name) +{ + void *f = NULL; +#if defined(NEED_LEADING_UNDERSCORE) + char *name; +#else + const char *name; +#endif + /* + ** Mangle the raw symbol name in any way that is platform specific. + */ +#if defined(NEED_LEADING_UNDERSCORE) + /* Need a leading _ */ + name = PR_smprintf("_%s", raw_name); +#elif defined(AIX) + /* + ** AIX with the normal linker put's a "." in front of the symbol + ** name. When use "svcc" and "svld" then the "." disappears. Go + ** figure. + */ + name = raw_name; +#else + name = raw_name; +#endif + + PR_EnterMonitor(pr_linker_lock); + PR_ASSERT(lib != NULL); + f = pr_FindSymbolInLib(lib, name); + +#if defined(NEED_LEADING_UNDERSCORE) + PR_smprintf_free(name); +#endif + + PR_ExitMonitor(pr_linker_lock); + return f; +} + +/* +** Return the address of the function 'raw_name' in the library 'lib' +*/ +PR_IMPLEMENT(PRFuncPtr) +PR_FindFunctionSymbol(PRLibrary *lib, const char *raw_name) +{ + return ((PRFuncPtr) PR_FindSymbol(lib, raw_name)); +} + +PR_IMPLEMENT(void*) +PR_FindSymbolAndLibrary(const char *raw_name, PRLibrary* *lib) +{ + void *f = NULL; +#if defined(NEED_LEADING_UNDERSCORE) + char *name; +#else + const char *name; +#endif + PRLibrary* lm; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + /* + ** Mangle the raw symbol name in any way that is platform specific. + */ +#if defined(NEED_LEADING_UNDERSCORE) + /* Need a leading _ */ + name = PR_smprintf("_%s", raw_name); +#elif defined(AIX) + /* + ** AIX with the normal linker put's a "." in front of the symbol + ** name. When use "svcc" and "svld" then the "." disappears. Go + ** figure. + */ + name = raw_name; +#else + name = raw_name; +#endif + + PR_EnterMonitor(pr_linker_lock); + + /* search all libraries */ + for (lm = pr_loadmap; lm != NULL; lm = lm->next) { + f = pr_FindSymbolInLib(lm, name); + if (f != NULL) { + *lib = lm; + lm->refCount++; + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("%s incr => %d (for %s)", + lm->name, lm->refCount, name)); + break; + } + } +#if defined(NEED_LEADING_UNDERSCORE) + PR_smprintf_free(name); +#endif + + PR_ExitMonitor(pr_linker_lock); + return f; +} + +PR_IMPLEMENT(PRFuncPtr) +PR_FindFunctionSymbolAndLibrary(const char *raw_name, PRLibrary* *lib) +{ + return ((PRFuncPtr) PR_FindSymbolAndLibrary(raw_name, lib)); +} + +/* +** Add a static library to the list of loaded libraries. If LoadLibrary +** is called with the name then we will pretend it was already loaded +*/ +PR_IMPLEMENT(PRLibrary*) +PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt) +{ + PRLibrary *lm=NULL; + PRLibrary* result = NULL; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + /* See if library is already loaded */ + PR_EnterMonitor(pr_linker_lock); + + /* If the lbrary is already loaded, then add the static table information... */ + result = pr_UnlockedFindLibrary(name); + if (result != NULL) { + PR_ASSERT( (result->staticTable == NULL) || (result->staticTable == slt) ); + result->staticTable = slt; + goto unlock; + } + + /* Add library to list...Mark it static */ + lm = PR_NEWZAP(PRLibrary); + if (lm == NULL) { + goto unlock; + } + + lm->name = strdup(name); + lm->refCount = 1; + lm->dlh = pr_exe_loadmap ? pr_exe_loadmap->dlh : 0; + lm->staticTable = slt; + lm->next = pr_loadmap; + pr_loadmap = lm; + + result = lm; /* success */ + PR_ASSERT(lm->refCount == 1); + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (static lib)", lm->name)); +unlock: + PR_ExitMonitor(pr_linker_lock); + return result; +} + +PR_IMPLEMENT(char *) +PR_GetLibraryFilePathname(const char *name, PRFuncPtr addr) +{ +#if defined(USE_DLFCN) && defined(HAVE_DLADDR) + Dl_info dli; + char *result; + + if (dladdr((void *)addr, &dli) == 0) { + PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + return NULL; + } + result = PR_Malloc(strlen(dli.dli_fname)+1); + if (result != NULL) { + strcpy(result, dli.dli_fname); + } + return result; +#elif defined(AIX) + char *result; +#define LD_INFO_INCREMENT 64 + struct ld_info *info; + unsigned int info_length = LD_INFO_INCREMENT * sizeof(struct ld_info); + struct ld_info *infop; + int loadflags = L_GETINFO | L_IGNOREUNLOAD; + + for (;;) { + info = PR_Malloc(info_length); + if (info == NULL) { + return NULL; + } + /* If buffer is too small, loadquery fails with ENOMEM. */ + if (loadquery(loadflags, info, info_length) != -1) { + break; + } + /* + * Calling loadquery when compiled for 64-bit with the + * L_IGNOREUNLOAD flag can cause an invalid argument error + * on AIX 5.1. Detect this error the first time that + * loadquery is called, and try calling it again without + * this flag set. + */ + if (errno == EINVAL && (loadflags & L_IGNOREUNLOAD)) { + loadflags &= ~L_IGNOREUNLOAD; + if (loadquery(loadflags, info, info_length) != -1) { + break; + } + } + PR_Free(info); + if (errno != ENOMEM) { + /* should not happen */ + _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); + return NULL; + } + /* retry with a larger buffer */ + info_length += LD_INFO_INCREMENT * sizeof(struct ld_info); + } + + for (infop = info; + ; + infop = (struct ld_info *)((char *)infop + infop->ldinfo_next)) { + unsigned long start = (unsigned long)infop->ldinfo_dataorg; + unsigned long end = start + infop->ldinfo_datasize; + if (start <= (unsigned long)addr && end > (unsigned long)addr) { + result = PR_Malloc(strlen(infop->ldinfo_filename)+1); + if (result != NULL) { + strcpy(result, infop->ldinfo_filename); + } + break; + } + if (!infop->ldinfo_next) { + PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0); + result = NULL; + break; + } + } + PR_Free(info); + return result; +#elif defined(HPUX) && defined(USE_HPSHL) + int index; + struct shl_descriptor desc; + char *result; + + for (index = 0; shl_get_r(index, &desc) == 0; index++) { + if (strstr(desc.filename, name) != NULL) { + result = PR_Malloc(strlen(desc.filename)+1); + if (result != NULL) { + strcpy(result, desc.filename); + } + return result; + } + } + /* + * Since the index value of a library is decremented if + * a library preceding it in the shared library search + * list was unloaded, it is possible that we missed some + * libraries as we went up the list. So we should go + * down the list to be sure that we not miss anything. + */ + for (index--; index >= 0; index--) { + if ((shl_get_r(index, &desc) == 0) + && (strstr(desc.filename, name) != NULL)) { + result = PR_Malloc(strlen(desc.filename)+1); + if (result != NULL) { + strcpy(result, desc.filename); + } + return result; + } + } + PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, 0); + return NULL; +#elif defined(HPUX) && defined(USE_DLFCN) + struct load_module_desc desc; + char *result; + const char *module_name; + + if (dlmodinfo((unsigned long)addr, &desc, sizeof desc, NULL, 0, 0) == 0) { + PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + return NULL; + } + module_name = dlgetname(&desc, sizeof desc, NULL, 0, 0); + if (module_name == NULL) { + /* should not happen */ + _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + return NULL; + } + result = PR_Malloc(strlen(module_name)+1); + if (result != NULL) { + strcpy(result, module_name); + } + return result; +#elif defined(WIN32) + PRUnichar wname[MAX_PATH]; + HMODULE handle = NULL; + PRUnichar module_name[MAX_PATH]; + int len; + char *result; + + if (MultiByteToWideChar(CP_ACP, 0, name, -1, wname, MAX_PATH)) { + handle = GetModuleHandleW(wname); + } + if (handle == NULL) { + PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + return NULL; + } + if (GetModuleFileNameW(handle, module_name, MAX_PATH) == 0) { + /* should not happen */ + _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); + return NULL; + } + len = WideCharToMultiByte(CP_ACP, 0, module_name, -1, + NULL, 0, NULL, NULL); + if (len == 0) { + _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); + return NULL; + } + result = PR_Malloc(len * sizeof(PRUnichar)); + if (result != NULL) { + WideCharToMultiByte(CP_ACP, 0, module_name, -1, + result, len, NULL, NULL); + } + return result; +#elif defined(XP_OS2) + HMODULE module = NULL; + char module_name[_MAX_PATH]; + char *result; + APIRET ulrc = DosQueryModFromEIP(&module, NULL, 0, NULL, NULL, (ULONG) addr); + if ((NO_ERROR != ulrc) || (NULL == module) ) { + PR_SetError(PR_LIBRARY_NOT_LOADED_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + return NULL; + } + ulrc = DosQueryModuleName(module, sizeof module_name, module_name); + if (NO_ERROR != ulrc) { + /* should not happen */ + _PR_MD_MAP_DEFAULT_ERROR(_MD_ERRNO()); + return NULL; + } + result = PR_Malloc(strlen(module_name)+1); + if (result != NULL) { + strcpy(result, module_name); + } + return result; +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +#endif +} diff --git a/nsprpub/pr/src/malloc/Makefile.in b/nsprpub/pr/src/malloc/Makefile.in new file mode 100644 index 0000000000..51f2a5a52a --- /dev/null +++ b/nsprpub/pr/src/malloc/Makefile.in @@ -0,0 +1,28 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +CSRCS = prmalloc.c prmem.c + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/malloc/prmalloc.c b/nsprpub/pr/src/malloc/prmalloc.c new file mode 100644 index 0000000000..c03b7d11e5 --- /dev/null +++ b/nsprpub/pr/src/malloc/prmalloc.c @@ -0,0 +1,1200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/* +** We override malloc etc. on any platform which has preemption + +** nspr20 user level threads. When we're debugging, we can make our +** version of malloc fail occasionally. +*/ +#ifdef _PR_OVERRIDE_MALLOC + +/* +** Thread safe version of malloc, calloc, realloc, free +*/ +#include <stdarg.h> + +#ifdef DEBUG +#define SANITY +#define EXTRA_SANITY +#else +#undef SANITY +#undef EXTRA_SANITY +#endif + +/* Forward decls */ +void *_PR_UnlockedMalloc(size_t size); +void _PR_UnlockedFree(void *ptr); +void *_PR_UnlockedRealloc(void *ptr, size_t size); +void *_PR_UnlockedCalloc(size_t n, size_t elsize); + +/************************************************************************/ + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + */ + +/* + * Defining SANITY will enable some checks which will tell you if the users + * program did botch something + */ + +/* + * Defining EXTRA_SANITY will enable some checks which are mostly related + * to internal conditions in malloc.c + */ + +/* + * Very verbose progress on stdout... + */ +#if 0 +# define TRACE(foo) printf foo +static int malloc_event; +#else +# define TRACE(foo) +#endif + +/* XXX Pick a number, any number */ +# define malloc_pagesize 4096UL +# define malloc_pageshift 12UL + +#ifdef XP_UNIX +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/mman.h> +#endif + +/* + * This structure describes a page's worth of chunks. + */ + +struct pginfo { + struct pginfo *next; /* next on the free list */ + char *page; /* Pointer to the page */ + u_short size; /* size of this page's chunks */ + u_short shift; /* How far to shift for this size chunks */ + u_short free; /* How many free chunks */ + u_short total; /* How many chunk */ + u_long bits[1]; /* Which chunks are free */ +}; + +struct pgfree { + struct pgfree *next; /* next run of free pages */ + struct pgfree *prev; /* prev run of free pages */ + char *page; /* pointer to free pages */ + char *end; /* pointer to end of free pages */ + u_long size; /* number of bytes free */ +}; + +/* + * How many bits per u_long in the bitmap. + * Change only if not 8 bits/byte + */ +#define MALLOC_BITS (8*sizeof(u_long)) + +/* + * Magic values to put in the page_directory + */ +#define MALLOC_NOT_MINE ((struct pginfo*) 0) +#define MALLOC_FREE ((struct pginfo*) 1) +#define MALLOC_FIRST ((struct pginfo*) 2) +#define MALLOC_FOLLOW ((struct pginfo*) 3) +#define MALLOC_MAGIC ((struct pginfo*) 4) + +/* + * Set to one when malloc_init has been called + */ +static unsigned initialized; + +/* + * The size of a page. + * Must be a integral multiplum of the granularity of mmap(2). + * Your toes will curl if it isn't a power of two + */ +#define malloc_pagemask ((malloc_pagesize)-1) + +/* + * The size of the largest chunk. + * Half a page. + */ +#define malloc_maxsize ((malloc_pagesize)>>1) + +/* + * malloc_pagesize == 1 << malloc_pageshift + */ +#ifndef malloc_pageshift +static unsigned malloc_pageshift; +#endif /* malloc_pageshift */ + +/* + * The smallest allocation we bother about. + * Must be power of two + */ +#ifndef malloc_minsize +static unsigned malloc_minsize; +#endif /* malloc_minsize */ + +/* + * The largest chunk we care about. + * Must be smaller than pagesize + * Must be power of two + */ +#ifndef malloc_maxsize +static unsigned malloc_maxsize; +#endif /* malloc_maxsize */ + +#ifndef malloc_cache +static unsigned malloc_cache; +#endif /* malloc_cache */ + +/* + * The offset from pagenumber to index into the page directory + */ +static u_long malloc_origo; + +/* + * The last index in the page directory we care about + */ +static u_long last_index; + +/* + * Pointer to page directory. + * Allocated "as if with" malloc + */ +static struct pginfo **page_dir; + +/* + * How many slots in the page directory + */ +static unsigned malloc_ninfo; + +/* + * Free pages line up here + */ +static struct pgfree free_list; + +/* + * Abort() if we fail to get VM ? + */ +static int malloc_abort; + +#ifdef SANITY +/* + * Are we trying to die ? + */ +static int suicide; +#endif + +/* + * dump statistics + */ +static int malloc_stats; + +/* + * always realloc ? + */ +static int malloc_realloc; + +/* + * my last break. + */ +static void *malloc_brk; + +/* + * one location cache for free-list holders + */ +static struct pgfree *px; + +static int set_pgdir(void *ptr, struct pginfo *info); +static int extend_page_directory(u_long index); + +#ifdef SANITY +void +malloc_dump(FILE *fd) +{ + struct pginfo **pd; + struct pgfree *pf; + int j; + + pd = page_dir; + + /* print out all the pages */ + for(j=0; j<=last_index; j++) { + fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j); + if (pd[j] == MALLOC_NOT_MINE) { + for(j++; j<=last_index && pd[j] == MALLOC_NOT_MINE; j++) + ; + j--; + fprintf(fd,".. %5d not mine\n", j); + } else if (pd[j] == MALLOC_FREE) { + for(j++; j<=last_index && pd[j] == MALLOC_FREE; j++) + ; + j--; + fprintf(fd,".. %5d free\n", j); + } else if (pd[j] == MALLOC_FIRST) { + for(j++; j<=last_index && pd[j] == MALLOC_FOLLOW; j++) + ; + j--; + fprintf(fd,".. %5d in use\n", j); + } else if (pd[j] < MALLOC_MAGIC) { + fprintf(fd,"(%p)\n", pd[j]); + } else { + fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n", + pd[j],pd[j]->free, pd[j]->total, + pd[j]->size, pd[j]->page, pd[j]->next); + } + } + + for(pf=free_list.next; pf; pf=pf->next) { + fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n", + pf,pf->page,pf->end,pf->size,pf->prev,pf->next); + if (pf == pf->next) { + fprintf(fd,"Free_list loops.\n"); + break; + } + } + + /* print out various info */ + fprintf(fd,"Minsize\t%d\n",malloc_minsize); + fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize); + fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize); + fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift); + fprintf(fd,"FirstPage\t%ld\n",malloc_origo); + fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift, + (last_index + malloc_pageshift) << malloc_pageshift); + fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift); +} + +static void wrterror(char *fmt, ...) +{ + char *q = "malloc() error: "; + char buf[100]; + va_list ap; + + suicide = 1; + + va_start(ap, fmt); + PR_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fputs(q, stderr); + fputs(buf, stderr); + + malloc_dump(stderr); + PR_Abort(); +} + +static void wrtwarning(char *fmt, ...) +{ + char *q = "malloc() warning: "; + char buf[100]; + va_list ap; + + va_start(ap, fmt); + PR_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fputs(q, stderr); + fputs(buf, stderr); +} +#endif /* SANITY */ + + +/* + * Allocate a number of pages from the OS + */ +static caddr_t +map_pages(int pages, int update) +{ + caddr_t result,tail; + + result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; + result = (caddr_t) ((u_long)result & ~malloc_pagemask); + tail = result + (pages << malloc_pageshift); + if (!brk(tail)) { + last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1; + malloc_brk = tail; + TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail)); + if (!update || last_index < malloc_ninfo || + extend_page_directory(last_index)) { + return result; + } + } + TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno)); +#ifdef EXTRA_SANITY + wrterror("map_pages fails\n"); +#endif + return 0; +} + +#define set_bit(_pi,_bit) \ + (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS) + +#define clr_bit(_pi,_bit) \ + (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS)); + +#define tst_bit(_pi,_bit) \ + ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS))) + +/* + * Extend page directory + */ +static int +extend_page_directory(u_long index) +{ + struct pginfo **young, **old; + int i; + + TRACE(("%6d E %lu\n",malloc_event++,index)); + + /* Make it this many pages */ + i = index * sizeof *page_dir; + i /= malloc_pagesize; + i += 2; + + /* Get new pages, if you used this much mem you don't care :-) */ + young = (struct pginfo**) map_pages(i,0); + if (!young) { + return 0; + } + + /* Copy the old stuff */ + memset(young, 0, i * malloc_pagesize); + memcpy(young, page_dir, + malloc_ninfo * sizeof *page_dir); + + /* register the new size */ + malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; + + /* swap the pointers */ + old = page_dir; + page_dir = young; + + /* Mark the pages */ + index = ((u_long)young >> malloc_pageshift) - malloc_origo; + page_dir[index] = MALLOC_FIRST; + while (--i) { + page_dir[++index] = MALLOC_FOLLOW; + } + + /* Now free the old stuff */ + _PR_UnlockedFree(old); + return 1; +} + +/* + * Set entry in page directory. + * Extend page directory if need be. + */ +static int +set_pgdir(void *ptr, struct pginfo *info) +{ + u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; + + if (index >= malloc_ninfo && !extend_page_directory(index)) { + return 0; + } + page_dir[index] = info; + return 1; +} + +/* + * Initialize the world + */ +static void +malloc_init (void) +{ + int i; + char *p; + + TRACE(("%6d I\n",malloc_event++)); +#ifdef DEBUG + for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) { + switch (*p) { + case 'a': malloc_abort = 0; break; + case 'A': malloc_abort = 1; break; + case 'd': malloc_stats = 0; break; + case 'D': malloc_stats = 1; break; + case 'r': malloc_realloc = 0; break; + case 'R': malloc_realloc = 1; break; + default: + wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); + break; + } + } +#endif + +#ifndef malloc_pagesize + /* determine our pagesize */ + malloc_pagesize = getpagesize(); +#endif /* malloc_pagesize */ + +#ifndef malloc_pageshift + /* determine how much we shift by to get there */ + for (i = malloc_pagesize; i > 1; i >>= 1) { + malloc_pageshift++; + } +#endif /* malloc_pageshift */ + +#ifndef malloc_cache + malloc_cache = 50 << malloc_pageshift; +#endif /* malloc_cache */ + +#ifndef malloc_minsize + /* + * find the smallest size allocation we will bother about. + * this is determined as the smallest allocation that can hold + * it's own pginfo; + */ + i = 2; + for(;;) { + int j; + + /* Figure out the size of the bits */ + j = malloc_pagesize/i; + j /= 8; + if (j < sizeof(u_long)) { + j = sizeof (u_long); + } + if (sizeof(struct pginfo) + j - sizeof (u_long) <= i) { + break; + } + i += i; + } + malloc_minsize = i; +#endif /* malloc_minsize */ + + + /* Allocate one page for the page directory */ + page_dir = (struct pginfo **) map_pages(1,0); +#ifdef SANITY + if (!page_dir) { + wrterror("fatal: my first mmap failed. (check limits ?)\n"); + } +#endif + + /* + * We need a maximum of malloc_pageshift buckets, steal these from the + * front of the page_directory; + */ + malloc_origo = (u_long) page_dir >> malloc_pageshift; + malloc_origo -= malloc_pageshift; + + /* Clear it */ + memset(page_dir,0,malloc_pagesize); + + /* Find out how much it tells us */ + malloc_ninfo = malloc_pagesize / sizeof *page_dir; + + /* Plug the page directory into itself */ + i = set_pgdir(page_dir,MALLOC_FIRST); +#ifdef SANITY + if (!i) { + wrterror("fatal: couldn't set myself in the page directory\n"); + } +#endif + + /* Been here, done that */ + initialized++; +} + +/* + * Allocate a number of complete pages + */ +static void *malloc_pages(size_t size) +{ + void *p,*delay_free = 0; + int i; + struct pgfree *pf; + u_long index; + + /* How many pages ? */ + size += (malloc_pagesize-1); + size &= ~malloc_pagemask; + + p = 0; + /* Look for free pages before asking for more */ + for(pf = free_list.next; pf; pf = pf->next) { +#ifdef EXTRA_SANITY + if (pf->page == pf->end) { + wrterror("zero entry on free_list\n"); + } + if (pf->page > pf->end) { + TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++, + pf,pf->page,pf->end,__LINE__)); + wrterror("sick entry on free_list\n"); + } + if ((void*)pf->page >= (void*)sbrk(0)) { + wrterror("entry on free_list past brk\n"); + } + if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] + != MALLOC_FREE) { + TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++, + pf,pf->page,pf->end,__LINE__)); + wrterror("non-free first page on free-list\n"); + } + if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] + != MALLOC_FREE) { + wrterror("non-free last page on free-list\n"); + } +#endif /* EXTRA_SANITY */ + if (pf->size < size) { + continue; + } + else if (pf->size == size) { + p = pf->page; + if (pf->next) { + pf->next->prev = pf->prev; + } + pf->prev->next = pf->next; + delay_free = pf; + break; + } else { + p = pf->page; + pf->page += size; + pf->size -= size; + break; + } + } +#ifdef EXTRA_SANITY + if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] + != MALLOC_FREE) { + wrterror("allocated non-free page on free-list\n"); + } +#endif /* EXTRA_SANITY */ + + size >>= malloc_pageshift; + + /* Map new pages */ + if (!p) { + p = map_pages(size,1); + } + + if (p) { + /* Mark the pages in the directory */ + index = ((u_long)p >> malloc_pageshift) - malloc_origo; + page_dir[index] = MALLOC_FIRST; + for (i=1; i<size; i++) { + page_dir[index+i] = MALLOC_FOLLOW; + } + } + if (delay_free) { + if (!px) { + px = (struct pgfree*)delay_free; + } + else { + _PR_UnlockedFree(delay_free); + } + } + return p; +} + +/* + * Allocate a page of fragments + */ + +static int +malloc_make_chunks(int bits) +{ + struct pginfo *bp; + void *pp; + int i,k,l; + + /* Allocate a new bucket */ + pp = malloc_pages(malloc_pagesize); + if (!pp) { + return 0; + } + l = sizeof *bp - sizeof(u_long); + l += sizeof(u_long) * + (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS); + if ((1<<(bits)) <= l+l) { + bp = (struct pginfo *)pp; + } else { + bp = (struct pginfo *)_PR_UnlockedMalloc(l); + } + if (!bp) { + return 0; + } + bp->size = (1<<bits); + bp->shift = bits; + bp->total = bp->free = malloc_pagesize >> bits; + bp->next = page_dir[bits]; + bp->page = (char*)pp; + i = set_pgdir(pp,bp); + if (!i) { + return 0; + } + + /* We can safely assume that there is nobody in this chain */ + page_dir[bits] = bp; + + /* set all valid bits in the bits */ + k = bp->total; + i = 0; + /* + for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) + bp->bits[i / MALLOC_BITS] = ~0; + */ + for(; i < k; i++) { + set_bit(bp,i); + } + + if (bp != pp) { + return 1; + } + + /* We may have used the first ones already */ + for(i=0; l > 0; i++) { + clr_bit(bp,i); + bp->free--; + bp->total--; + l -= (1 << bits); + } + return 1; +} + +/* + * Allocate a fragment + */ +static void *malloc_bytes(size_t size) +{ + size_t s; + int j; + struct pginfo *bp; + int k; + u_long *lp, bf; + + /* Don't bother with anything less than this */ + if (size < malloc_minsize) { + size = malloc_minsize; + } + + /* Find the right bucket */ + j = 1; + s = size - 1; + while (s >>= 1) { + j++; + } + + /* If it's empty, make a page more of that size chunks */ + if (!page_dir[j] && !malloc_make_chunks(j)) { + return 0; + } + + /* Find first word of bitmap which isn't empty */ + bp = page_dir[j]; + for (lp = bp->bits; !*lp; lp++) + ; + + /* Find that bit */ + bf = *lp; + k = 0; + while ((bf & 1) == 0) { + bf >>= 1; + k++; + } + + *lp ^= 1L<<k; /* clear it */ + bp->free--; + if (!bp->free) { + page_dir[j] = bp->next; + bp->next = 0; + } + k += (lp - bp->bits)*MALLOC_BITS; + return bp->page + (k << bp->shift); +} + +void *_PR_UnlockedMalloc(size_t size) +{ + void *result; + + /* Round up to a multiple of 8 bytes */ + if (size & 7) { + size = size + 8 - (size & 7); + } + + if (!initialized) { + malloc_init(); + } + +#ifdef SANITY + if (suicide) { + PR_Abort(); + } +#endif + + if (size <= malloc_maxsize) { + result = malloc_bytes(size); + } + else { + result = malloc_pages(size); + } +#ifdef SANITY + if (malloc_abort && !result) { + wrterror("malloc() returns NULL\n"); + } +#endif + TRACE(("%6d M %p %d\n",malloc_event++,result,size)); + + return result; +} + +void *_PR_UnlockedMemalign(size_t alignment, size_t size) +{ + void *result; + + /* + * alignment has to be a power of 2 + */ + + if ((size <= alignment) && (alignment <= malloc_maxsize)) { + size = alignment; + } + else { + size += alignment - 1; + } + + /* Round up to a multiple of 8 bytes */ + if (size & 7) { + size = size + 8 - (size & 7); + } + + if (!initialized) { + malloc_init(); + } + +#ifdef SANITY + if (suicide) { + abort(); + } +#endif + + if (size <= malloc_maxsize) { + result = malloc_bytes(size); + } + else { + result = malloc_pages(size); + } +#ifdef SANITY + if (malloc_abort && !result) { + wrterror("malloc() returns NULL\n"); + } +#endif + TRACE(("%6d A %p %d\n",malloc_event++,result,size)); + + if ((u_long)result & (alignment - 1)) { + return ((void *)(((u_long)result + alignment) & ~(alignment - 1))); + } + else { + return result; + } +} + +void *_PR_UnlockedCalloc(size_t n, size_t nelem) +{ + void *p; + + /* Compute total size and then round up to a double word amount */ + n *= nelem; + if (n & 7) { + n = n + 8 - (n & 7); + } + + /* Get the memory */ + p = _PR_UnlockedMalloc(n); + if (p) { + /* Zero it */ + memset(p, 0, n); + } + return p; +} + +/* + * Change an allocation's size + */ +void *_PR_UnlockedRealloc(void *ptr, size_t size) +{ + void *p; + u_long osize,page,index,tmp_index; + struct pginfo **mp; + + if (!initialized) { + malloc_init(); + } + +#ifdef SANITY + if (suicide) { + PR_Abort(); + } +#endif + + /* used as free() */ + TRACE(("%6d R %p %d\n",malloc_event++, ptr, size)); + if (ptr && !size) { + _PR_UnlockedFree(ptr); + return _PR_UnlockedMalloc (1); + } + + /* used as malloc() */ + if (!ptr) { + p = _PR_UnlockedMalloc(size); + return p; + } + + /* Find the page directory entry for the page in question */ + page = (u_long)ptr >> malloc_pageshift; + index = page - malloc_origo; + + /* + * check if memory was allocated by memalign + */ + tmp_index = index; + while (page_dir[tmp_index] == MALLOC_FOLLOW) { + tmp_index--; + } + if (tmp_index != index) { + /* + * memalign-allocated memory + */ + index = tmp_index; + page = index + malloc_origo; + ptr = (void *) (page << malloc_pageshift); + } + TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size)); + + /* make sure it makes sense in some fashion */ + if (index < malloc_pageshift || index > last_index) { +#ifdef SANITY + wrtwarning("junk pointer passed to realloc()\n"); +#endif + return 0; + } + + /* find the size of that allocation, and see if we need to relocate */ + mp = &page_dir[index]; + if (*mp == MALLOC_FIRST) { + osize = malloc_pagesize; + while (mp[1] == MALLOC_FOLLOW) { + osize += malloc_pagesize; + mp++; + } + if (!malloc_realloc && + size < osize && + size > malloc_maxsize && + size > (osize - malloc_pagesize)) { + return ptr; + } + } else if (*mp >= MALLOC_MAGIC) { + osize = (*mp)->size; + if (!malloc_realloc && + size < osize && + (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) { + return ptr; + } + } else { +#ifdef SANITY + wrterror("realloc() of wrong page.\n"); +#endif + } + + /* try to reallocate */ + p = _PR_UnlockedMalloc(size); + + if (p) { + /* copy the lesser of the two sizes */ + if (osize < size) { + memcpy(p,ptr,osize); + } + else { + memcpy(p,ptr,size); + } + _PR_UnlockedFree(ptr); + } +#ifdef DEBUG + else if (malloc_abort) { + wrterror("realloc() returns NULL\n"); + } +#endif + + return p; +} + +/* + * Free a sequence of pages + */ + +static void +free_pages(char *ptr, u_long page, int index, struct pginfo *info) +{ + int i; + struct pgfree *pf,*pt; + u_long l; + char *tail; + + TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page)); + /* Is it free already ? */ + if (info == MALLOC_FREE) { +#ifdef SANITY + wrtwarning("freeing free page at %p.\n", ptr); +#endif + return; + } + +#ifdef SANITY + /* Is it not the right place to begin ? */ + if (info != MALLOC_FIRST) { + wrterror("freeing wrong page.\n"); + } + + /* Is this really a pointer to a page ? */ + if ((u_long)ptr & malloc_pagemask) { + wrterror("freeing messed up page pointer.\n"); + } +#endif + + /* Count how many pages it is anyway */ + page_dir[index] = MALLOC_FREE; + for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++) { + page_dir[index + i] = MALLOC_FREE; + } + + l = i << malloc_pageshift; + + tail = ptr+l; + + /* add to free-list */ + if (!px) { + px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt); + } + /* XXX check success */ + px->page = ptr; + px->end = tail; + px->size = l; + if (!free_list.next) { + px->next = free_list.next; + px->prev = &free_list; + free_list.next = px; + pf = px; + px = 0; + } else { + tail = ptr+l; + for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next) + ; + for(; pf; pf = pf->next) { + if (pf->end == ptr ) { + /* append to entry */ + pf->end += l; + pf->size += l; + if (pf->next && pf->end == pf->next->page ) { + pt = pf->next; + pf->end = pt->end; + pf->size += pt->size; + pf->next = pt->next; + if (pf->next) { + pf->next->prev = pf; + } + _PR_UnlockedFree(pt); + } + } else if (pf->page == tail) { + /* prepend to entry */ + pf->size += l; + pf->page = ptr; + } else if (pf->page > ptr) { + px->next = pf; + px->prev = pf->prev; + pf->prev = px; + px->prev->next = px; + pf = px; + px = 0; + } else if (!pf->next) { + px->next = 0; + px->prev = pf; + pf->next = px; + pf = px; + px = 0; + } else { + continue; + } + break; + } + } + if (!pf->next && + pf->size > malloc_cache && + pf->end == malloc_brk && + malloc_brk == (void*)sbrk(0)) { + pf->end = pf->page + malloc_cache; + pf->size = malloc_cache; + TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page)); + brk(pf->end); + malloc_brk = pf->end; + /* Find the page directory entry for the page in question */ + page = (u_long)pf->end >> malloc_pageshift; + index = page - malloc_origo; + /* Now update the directory */ + for(i=index; i <= last_index;) { + page_dir[i++] = MALLOC_NOT_MINE; + } + last_index = index - 1; + } +} + +/* + * Free a chunk, and possibly the page it's on, if the page becomes empty. + */ + +static void +free_bytes(void *ptr, u_long page, int index, struct pginfo *info) +{ + int i; + struct pginfo **mp; + void *vp; + + /* Make sure that pointer is multiplum of chunk-size */ +#ifdef SANITY + if ((u_long)ptr & (info->size - 1)) { + wrterror("freeing messed up chunk pointer\n"); + } +#endif + + /* Find the chunk number on the page */ + i = ((u_long)ptr & malloc_pagemask) >> info->shift; + + /* See if it's free already */ + if (tst_bit(info,i)) { +#ifdef SANITY + wrtwarning("freeing free chunk at %p\n", ptr); +#endif + return; + } + + /* Mark it free */ + set_bit(info,i); + info->free++; + + /* If the page was full before, we need to put it on the queue now */ + if (info->free == 1) { + mp = page_dir + info->shift; + while (*mp && (*mp)->next && (*mp)->next->page < info->page) { + mp = &(*mp)->next; + } + info->next = *mp; + *mp = info; + return; + } + + /* If this page isn't empty, don't do anything. */ + if (info->free != info->total) { + return; + } + + /* We may want to keep at least one page of each size chunks around. */ + mp = page_dir + info->shift; + if (0 && (*mp == info) && !info->next) { + return; + } + + /* Find & remove this page in the queue */ + while (*mp != info) { + mp = &((*mp)->next); +#ifdef EXTRA_SANITY + if (!*mp) { + TRACE(("%6d !q %p\n",malloc_event++,info)); + wrterror("Not on queue\n"); + } +#endif + } + *mp = info->next; + + /* Free the page & the info structure if need be */ + set_pgdir(info->page,MALLOC_FIRST); + if((void*)info->page == (void*)info) { + _PR_UnlockedFree(info->page); + } else { + vp = info->page; + _PR_UnlockedFree(info); + _PR_UnlockedFree(vp); + } +} + +void _PR_UnlockedFree(void *ptr) +{ + u_long page; + struct pginfo *info; + int index, tmp_index; + + TRACE(("%6d F %p\n",malloc_event++,ptr)); + /* This is legal */ + if (!ptr) { + return; + } + +#ifdef SANITY + /* There wouldn't be anything to free */ + if (!initialized) { + wrtwarning("free() called before malloc() ever got called\n"); + return; + } +#endif + +#ifdef SANITY + if (suicide) { + PR_Abort(); + } +#endif + + /* Find the page directory entry for the page in question */ + page = (u_long)ptr >> malloc_pageshift; + index = page - malloc_origo; + + /* + * check if memory was allocated by memalign + */ + tmp_index = index; + while (page_dir[tmp_index] == MALLOC_FOLLOW) { + tmp_index--; + } + if (tmp_index != index) { + /* + * memalign-allocated memory + */ + index = tmp_index; + page = index + malloc_origo; + ptr = (void *) (page << malloc_pageshift); + } + /* make sure it makes sense in some fashion */ + if (index < malloc_pageshift) { +#ifdef SANITY + wrtwarning("junk pointer %p (low) passed to free()\n", ptr); +#endif + return; + } + if (index > last_index) { +#ifdef SANITY + wrtwarning("junk pointer %p (high) passed to free()\n", ptr); +#endif + return; + } + + /* handle as page-allocation or chunk allocation */ + info = page_dir[index]; + if (info < MALLOC_MAGIC) { + free_pages((char*)ptr, page, index, info); + } + else { + free_bytes(ptr,page,index,info); + } + return; +} +#endif /* _PR_OVERRIDE_MALLOC */ diff --git a/nsprpub/pr/src/malloc/prmem.c b/nsprpub/pr/src/malloc/prmem.c new file mode 100644 index 0000000000..08a700f7a9 --- /dev/null +++ b/nsprpub/pr/src/malloc/prmem.c @@ -0,0 +1,678 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Thread safe versions of malloc, free, realloc, calloc and cfree. +*/ + +#include "primpl.h" + +#ifdef _PR_ZONE_ALLOCATOR + +/* +** The zone allocator code must use native mutexes and cannot +** use PRLocks because PR_NewLock calls PR_Calloc, resulting +** in cyclic dependency of initialization. +*/ + +#include <string.h> + +union memBlkHdrUn; + +typedef struct MemoryZoneStr { + union memBlkHdrUn *head; /* free list */ + pthread_mutex_t lock; + size_t blockSize; /* size of blocks on this free list */ + PRUint32 locked; /* current state of lock */ + PRUint32 contention; /* counter: had to wait for lock */ + PRUint32 hits; /* allocated from free list */ + PRUint32 misses; /* had to call malloc */ + PRUint32 elements; /* on free list */ +} MemoryZone; + +typedef union memBlkHdrUn { + unsigned char filler[48]; /* fix the size of this beast */ + struct memBlkHdrStr { + union memBlkHdrUn *next; + MemoryZone *zone; + size_t blockSize; + size_t requestedSize; + PRUint32 magic; + } s; +} MemBlockHdr; + +#define MEM_ZONES 7 +#define THREAD_POOLS 11 /* prime number for modulus */ +#define ZONE_MAGIC 0x0BADC0DE + +static MemoryZone zones[MEM_ZONES][THREAD_POOLS]; + +static PRBool use_zone_allocator = PR_FALSE; + +static void pr_ZoneFree(void *ptr); + +void +_PR_DestroyZones(void) +{ + int i, j; + + if (!use_zone_allocator) { + return; + } + + for (j = 0; j < THREAD_POOLS; j++) { + for (i = 0; i < MEM_ZONES; i++) { + MemoryZone *mz = &zones[i][j]; + pthread_mutex_destroy(&mz->lock); + while (mz->head) { + MemBlockHdr *hdr = mz->head; + mz->head = hdr->s.next; /* unlink it */ + free(hdr); + mz->elements--; + } + } + } + use_zone_allocator = PR_FALSE; +} + +/* +** pr_FindSymbolInProg +** +** Find the specified data symbol in the program and return +** its address. +*/ + +#ifdef HAVE_DLL + +#if defined(USE_DLFCN) && !defined(NO_DLOPEN_NULL) + +#include <dlfcn.h> + +static void * +pr_FindSymbolInProg(const char *name) +{ + void *h; + void *sym; + + h = dlopen(0, RTLD_LAZY); + if (h == NULL) { + return NULL; + } + sym = dlsym(h, name); + (void)dlclose(h); + return sym; +} + +#elif defined(USE_HPSHL) + +#include <dl.h> + +static void * +pr_FindSymbolInProg(const char *name) +{ + shl_t h = NULL; + void *sym; + + if (shl_findsym(&h, name, TYPE_DATA, &sym) == -1) { + return NULL; + } + return sym; +} + +#elif defined(USE_MACH_DYLD) || defined(NO_DLOPEN_NULL) + +static void * +pr_FindSymbolInProg(const char *name) +{ + /* FIXME: not implemented */ + return NULL; +} + +#else + +#error "The zone allocator is not supported on this platform" + +#endif + +#else /* !defined(HAVE_DLL) */ + +static void * +pr_FindSymbolInProg(const char *name) +{ + /* can't be implemented */ + return NULL; +} + +#endif /* HAVE_DLL */ + +void +_PR_InitZones(void) +{ + int i, j; + char *envp; + PRBool *sym; + + if ((sym = (PRBool *)pr_FindSymbolInProg("nspr_use_zone_allocator")) != NULL) { + use_zone_allocator = *sym; + } else if ((envp = getenv("NSPR_USE_ZONE_ALLOCATOR")) != NULL) { + use_zone_allocator = (atoi(envp) == 1); + } + + if (!use_zone_allocator) { + return; + } + + for (j = 0; j < THREAD_POOLS; j++) { + for (i = 0; i < MEM_ZONES; i++) { + MemoryZone *mz = &zones[i][j]; + int rv = pthread_mutex_init(&mz->lock, NULL); + PR_ASSERT(0 == rv); + if (rv != 0) { + goto loser; + } + mz->blockSize = 16 << ( 2 * i); + } + } + return; + +loser: + _PR_DestroyZones(); + return; +} + +PR_IMPLEMENT(void) +PR_FPrintZoneStats(PRFileDesc *debug_out) +{ + int i, j; + + for (j = 0; j < THREAD_POOLS; j++) { + for (i = 0; i < MEM_ZONES; i++) { + MemoryZone *mz = &zones[i][j]; + MemoryZone zone = *mz; + if (zone.elements || zone.misses || zone.hits) { + PR_fprintf(debug_out, + "pool: %d, zone: %d, size: %d, free: %d, hit: %d, miss: %d, contend: %d\n", + j, i, zone.blockSize, zone.elements, + zone.hits, zone.misses, zone.contention); + } + } + } +} + +static void * +pr_ZoneMalloc(PRUint32 size) +{ + void *rv; + unsigned int zone; + size_t blockSize; + MemBlockHdr *mb, *mt; + MemoryZone *mz; + + /* Always allocate a non-zero amount of bytes */ + if (size < 1) { + size = 1; + } + for (zone = 0, blockSize = 16; zone < MEM_ZONES; ++zone, blockSize <<= 2) { + if (size <= blockSize) { + break; + } + } + if (zone < MEM_ZONES) { + pthread_t me = pthread_self(); + unsigned int pool = (PRUptrdiff)me % THREAD_POOLS; + PRUint32 wasLocked; + mz = &zones[zone][pool]; + wasLocked = mz->locked; + pthread_mutex_lock(&mz->lock); + mz->locked = 1; + if (wasLocked) { + mz->contention++; + } + if (mz->head) { + mb = mz->head; + PR_ASSERT(mb->s.magic == ZONE_MAGIC); + PR_ASSERT(mb->s.zone == mz); + PR_ASSERT(mb->s.blockSize == blockSize); + PR_ASSERT(mz->blockSize == blockSize); + + mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize); + PR_ASSERT(mt->s.magic == ZONE_MAGIC); + PR_ASSERT(mt->s.zone == mz); + PR_ASSERT(mt->s.blockSize == blockSize); + + mz->hits++; + mz->elements--; + mz->head = mb->s.next; /* take off free list */ + mz->locked = 0; + pthread_mutex_unlock(&mz->lock); + + mt->s.next = mb->s.next = NULL; + mt->s.requestedSize = mb->s.requestedSize = size; + + rv = (void *)(mb + 1); + return rv; + } + + mz->misses++; + mz->locked = 0; + pthread_mutex_unlock(&mz->lock); + + mb = (MemBlockHdr *)malloc(blockSize + 2 * (sizeof *mb)); + if (!mb) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + mb->s.next = NULL; + mb->s.zone = mz; + mb->s.magic = ZONE_MAGIC; + mb->s.blockSize = blockSize; + mb->s.requestedSize = size; + + mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize); + memcpy(mt, mb, sizeof *mb); + + rv = (void *)(mb + 1); + return rv; + } + + /* size was too big. Create a block with no zone */ + blockSize = (size & 15) ? size + 16 - (size & 15) : size; + mb = (MemBlockHdr *)malloc(blockSize + 2 * (sizeof *mb)); + if (!mb) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + mb->s.next = NULL; + mb->s.zone = NULL; + mb->s.magic = ZONE_MAGIC; + mb->s.blockSize = blockSize; + mb->s.requestedSize = size; + + mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize); + memcpy(mt, mb, sizeof *mb); + + rv = (void *)(mb + 1); + return rv; +} + + +static void * +pr_ZoneCalloc(PRUint32 nelem, PRUint32 elsize) +{ + PRUint32 size = nelem * elsize; + void *p = pr_ZoneMalloc(size); + if (p) { + memset(p, 0, size); + } + return p; +} + +static void * +pr_ZoneRealloc(void *oldptr, PRUint32 bytes) +{ + void *rv; + MemBlockHdr *mb; + int ours; + MemBlockHdr phony; + + if (!oldptr) { + return pr_ZoneMalloc(bytes); + } + mb = (MemBlockHdr *)((char *)oldptr - (sizeof *mb)); + if (mb->s.magic != ZONE_MAGIC) { + /* Maybe this just came from ordinary malloc */ +#ifdef DEBUG + fprintf(stderr, + "Warning: reallocing memory block %p from ordinary malloc\n", + oldptr); +#endif + /* + * We are going to realloc oldptr. If realloc succeeds, the + * original value of oldptr will point to freed memory. So this + * function must not fail after a successfull realloc call. We + * must perform any operation that may fail before the realloc + * call. + */ + rv = pr_ZoneMalloc(bytes); /* this may fail */ + if (!rv) { + return rv; + } + + /* We don't know how big it is. But we can fix that. */ + oldptr = realloc(oldptr, bytes); + /* + * If realloc returns NULL, this function loses the original + * value of oldptr. This isn't a leak because the caller of + * this function still has the original value of oldptr. + */ + if (!oldptr) { + if (bytes) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + pr_ZoneFree(rv); + return oldptr; + } + } + phony.s.requestedSize = bytes; + mb = &phony; + ours = 0; + } else { + size_t blockSize = mb->s.blockSize; + MemBlockHdr *mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize); + + PR_ASSERT(mt->s.magic == ZONE_MAGIC); + PR_ASSERT(mt->s.zone == mb->s.zone); + PR_ASSERT(mt->s.blockSize == blockSize); + + if (bytes <= blockSize) { + /* The block is already big enough. */ + mt->s.requestedSize = mb->s.requestedSize = bytes; + return oldptr; + } + ours = 1; + rv = pr_ZoneMalloc(bytes); + if (!rv) { + return rv; + } + } + + if (oldptr && mb->s.requestedSize) { + memcpy(rv, oldptr, mb->s.requestedSize); + } + if (ours) { + pr_ZoneFree(oldptr); + } + else if (oldptr) { + free(oldptr); + } + return rv; +} + +static void +pr_ZoneFree(void *ptr) +{ + MemBlockHdr *mb, *mt; + MemoryZone *mz; + size_t blockSize; + PRUint32 wasLocked; + + if (!ptr) { + return; + } + + mb = (MemBlockHdr *)((char *)ptr - (sizeof *mb)); + + if (mb->s.magic != ZONE_MAGIC) { + /* maybe this came from ordinary malloc */ +#ifdef DEBUG + fprintf(stderr, + "Warning: freeing memory block %p from ordinary malloc\n", ptr); +#endif + free(ptr); + return; + } + + blockSize = mb->s.blockSize; + mz = mb->s.zone; + mt = (MemBlockHdr *)(((char *)(mb + 1)) + blockSize); + PR_ASSERT(mt->s.magic == ZONE_MAGIC); + PR_ASSERT(mt->s.zone == mz); + PR_ASSERT(mt->s.blockSize == blockSize); + if (!mz) { + PR_ASSERT(blockSize > 65536); + /* This block was not in any zone. Just free it. */ + free(mb); + return; + } + PR_ASSERT(mz->blockSize == blockSize); + wasLocked = mz->locked; + pthread_mutex_lock(&mz->lock); + mz->locked = 1; + if (wasLocked) { + mz->contention++; + } + mt->s.next = mb->s.next = mz->head; /* put on head of list */ + mz->head = mb; + mz->elements++; + mz->locked = 0; + pthread_mutex_unlock(&mz->lock); +} + +PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + return use_zone_allocator ? pr_ZoneMalloc(size) : malloc(size); +} + +PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + return use_zone_allocator ? + pr_ZoneCalloc(nelem, elsize) : calloc(nelem, elsize); +} + +PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + return use_zone_allocator ? pr_ZoneRealloc(ptr, size) : realloc(ptr, size); +} + +PR_IMPLEMENT(void) PR_Free(void *ptr) +{ + if (use_zone_allocator) { + pr_ZoneFree(ptr); + } + else { + free(ptr); + } +} + +#else /* !defined(_PR_ZONE_ALLOCATOR) */ + +/* +** The PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free functions simply +** call their libc equivalents now. This may seem redundant, but it +** ensures that we are calling into the same runtime library. On +** Win32, it is possible to have multiple runtime libraries (e.g., +** objects compiled with /MD and /MDd) in the same process, and +** they maintain separate heaps, which cannot be mixed. +*/ +PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size) +{ +#if defined (WIN16) + return PR_MD_malloc( (size_t) size); +#else + return malloc(size); +#endif +} + +PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize) +{ +#if defined (WIN16) + return PR_MD_calloc( (size_t)nelem, (size_t)elsize ); + +#else + return calloc(nelem, elsize); +#endif +} + +PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size) +{ +#if defined (WIN16) + return PR_MD_realloc( ptr, (size_t) size); +#else + return realloc(ptr, size); +#endif +} + +PR_IMPLEMENT(void) PR_Free(void *ptr) +{ +#if defined (WIN16) + PR_MD_free( ptr ); +#else + free(ptr); +#endif +} + +#endif /* _PR_ZONE_ALLOCATOR */ + +/* +** Complexity alert! +** +** If malloc/calloc/free (etc.) were implemented to use pr lock's then +** the entry points could block when called if some other thread had the +** lock. +** +** Most of the time this isn't a problem. However, in the case that we +** are using the thread safe malloc code after PR_Init but before +** PR_AttachThread has been called (on a native thread that nspr has yet +** to be told about) we could get royally screwed if the lock was busy +** and we tried to context switch the thread away. In this scenario +** PR_CURRENT_THREAD() == NULL +** +** To avoid this unfortunate case, we use the low level locking +** facilities for malloc protection instead of the slightly higher level +** locking. This makes malloc somewhat faster so maybe it's a good thing +** anyway. +*/ +#ifdef _PR_OVERRIDE_MALLOC + +/* Imports */ +extern void *_PR_UnlockedMalloc(size_t size); +extern void *_PR_UnlockedMemalign(size_t alignment, size_t size); +extern void _PR_UnlockedFree(void *ptr); +extern void *_PR_UnlockedRealloc(void *ptr, size_t size); +extern void *_PR_UnlockedCalloc(size_t n, size_t elsize); + +static PRBool _PR_malloc_initialised = PR_FALSE; + +#ifdef _PR_PTHREADS +static pthread_mutex_t _PR_MD_malloc_crustylock; + +#define _PR_Lock_Malloc() { \ + if(PR_TRUE == _PR_malloc_initialised) { \ + PRStatus rv; \ + rv = pthread_mutex_lock(&_PR_MD_malloc_crustylock); \ + PR_ASSERT(0 == rv); \ + } + +#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \ + PRStatus rv; \ + rv = pthread_mutex_unlock(&_PR_MD_malloc_crustylock); \ + PR_ASSERT(0 == rv); \ + } \ + } +#else /* _PR_PTHREADS */ +static _MDLock _PR_MD_malloc_crustylock; + +#define _PR_Lock_Malloc() { \ + PRIntn _is; \ + if(PR_TRUE == _PR_malloc_initialised) { \ + if (_PR_MD_CURRENT_THREAD() && \ + !_PR_IS_NATIVE_THREAD( \ + _PR_MD_CURRENT_THREAD())) \ + _PR_INTSOFF(_is); \ + _PR_MD_LOCK(&_PR_MD_malloc_crustylock); \ + } + +#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \ + _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock); \ + if (_PR_MD_CURRENT_THREAD() && \ + !_PR_IS_NATIVE_THREAD( \ + _PR_MD_CURRENT_THREAD())) \ + _PR_INTSON(_is); \ + } \ + } +#endif /* _PR_PTHREADS */ + +PR_IMPLEMENT(PRStatus) _PR_MallocInit(void) +{ + PRStatus rv = PR_SUCCESS; + + if( PR_TRUE == _PR_malloc_initialised ) { + return PR_SUCCESS; + } + +#ifdef _PR_PTHREADS + { + int status; + pthread_mutexattr_t mattr; + + status = _PT_PTHREAD_MUTEXATTR_INIT(&mattr); + PR_ASSERT(0 == status); + status = _PT_PTHREAD_MUTEX_INIT(_PR_MD_malloc_crustylock, mattr); + PR_ASSERT(0 == status); + status = _PT_PTHREAD_MUTEXATTR_DESTROY(&mattr); + PR_ASSERT(0 == status); + } +#else /* _PR_PTHREADS */ + _MD_NEW_LOCK(&_PR_MD_malloc_crustylock); +#endif /* _PR_PTHREADS */ + + if( PR_SUCCESS == rv ) + { + _PR_malloc_initialised = PR_TRUE; + } + + return rv; +} + +void *malloc(size_t size) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedMalloc(size); + _PR_Unlock_Malloc(); + return p; +} + +void free(void *ptr) +{ + _PR_Lock_Malloc(); + _PR_UnlockedFree(ptr); + _PR_Unlock_Malloc(); +} + +void *realloc(void *ptr, size_t size) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedRealloc(ptr, size); + _PR_Unlock_Malloc(); + return p; +} + +void *calloc(size_t n, size_t elsize) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedCalloc(n, elsize); + _PR_Unlock_Malloc(); + return p; +} + +void cfree(void *p) +{ + _PR_Lock_Malloc(); + _PR_UnlockedFree(p); + _PR_Unlock_Malloc(); +} + +void _PR_InitMem(void) +{ + PRStatus rv; + rv = _PR_MallocInit(); + PR_ASSERT(PR_SUCCESS == rv); +} + +#endif /* _PR_OVERRIDE_MALLOC */ diff --git a/nsprpub/pr/src/md/Makefile.in b/nsprpub/pr/src/md/Makefile.in new file mode 100644 index 0000000000..e72bc65ab4 --- /dev/null +++ b/nsprpub/pr/src/md/Makefile.in @@ -0,0 +1,32 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +DIRS = $(PR_MD_ARCH_DIR) + +CSRCS = \ + prosdep.c \ + $(NULL) + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/md/os2/Makefile.in b/nsprpub/pr/src/md/os2/Makefile.in new file mode 100644 index 0000000000..9a7648ef8b --- /dev/null +++ b/nsprpub/pr/src/md/os2/Makefile.in @@ -0,0 +1,47 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +ifeq ($(OS_TARGET), OS2) +CSRCS = \ + os2misc.c \ + os2sem.c \ + os2inrval.c \ + os2gc.c \ + os2thred.c \ + os2io.c \ + os2cv.c \ + os2sock.c \ + os2_errors.c \ + os2poll.c \ + os2rng.c \ + $(NULL) +endif + +ASFILES = os2emx.s os2vaclegacy.s + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + + + + diff --git a/nsprpub/pr/src/md/os2/objs.mk b/nsprpub/pr/src/md/os2/objs.mk new file mode 100644 index 0000000000..c1a40e3f7d --- /dev/null +++ b/nsprpub/pr/src/md/os2/objs.mk @@ -0,0 +1,27 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This makefile appends to the variable OBJS the platform-dependent +# object modules that will be part of the nspr20 library. + +CSRCS = \ + os2io.c \ + os2sock.c \ + os2thred.c \ + os2cv.c \ + os2gc.c \ + os2misc.c \ + os2inrval.c \ + os2sem.c \ + os2_errors.c \ + os2poll.c \ + os2rng.c \ + $(NULL) + +ASFILES = os2emx.s os2vaclegacy.s + +OBJS += $(addprefix md/os2/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \ + $(addprefix md/os2/$(OBJDIR)/,$(ASFILES:.$(ASM_SUFFIX)=.$(OBJ_SUFFIX))) + diff --git a/nsprpub/pr/src/md/os2/os2_errors.c b/nsprpub/pr/src/md/os2/os2_errors.c new file mode 100644 index 0000000000..dfcca6108d --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2_errors.c @@ -0,0 +1,1095 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prerror.h" +#include "primpl.h" + +void _MD_os2_map_default_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} +void _MD_os2_map_opendir_error(PRInt32 err) +{ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + case ERROR_INVALID_ACCESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_INVALID_NAME: + case ERROR_INVALID_PARAMETER: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ERROR_TOO_MANY_OPEN_FILES: + case ERROR_NOT_DOS_DISK: + case ERROR_NOT_READY: + case ERROR_OPEN_FAILED: + case ERROR_PATH_BUSY: + case ERROR_CANNOT_MAKE: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_DEVICE_IN_USE: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_FILENAME_EXCED_RANGE: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_SHARING_BUFFER_EXCEEDED: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_closedir_error(PRInt32 err) +{ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_ACCESS_DENIED: + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_readdir_error(PRInt32 err) +{ + + switch (err) { + case ERROR_NO_MORE_FILES: + PR_SetError(PR_NO_MORE_FILES_ERROR, err); + break; + case ERROR_FILE_NOT_FOUND: + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_NOT_DOS_DISK: + case ERROR_LOCK_VIOLATION: + case ERROR_BROKEN_PIPE: + case ERROR_NOT_READY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_delete_error(PRInt32 err) +{ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_ACCESS_DENIED: + case ERROR_WRITE_PROTECT: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_LOCKED: + case ERROR_SHARING_VIOLATION: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +/* The error code for stat() is in errno. */ +void _MD_os2_map_stat_error(PRInt32 err) +{ + switch (err) { + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + } +} + +void _MD_os2_map_fstat_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_LOCKED: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_rename_error(PRInt32 err) +{ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_INVALID_NAME: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_FILENAME_EXCED_RANGE: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + PR_SetError(PR_FILE_EXISTS_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +/* The error code for access() is in errno. */ +void _MD_os2_map_access_error(PRInt32 err) +{ + switch (err) { + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + } +} + +void _MD_os2_map_mkdir_error(PRInt32 err) +{ + switch (err) { + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + PR_SetError(PR_FILE_EXISTS_ERROR, err); + break; + case ERROR_FILE_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_INVALID_NAME: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_FILENAME_EXCED_RANGE: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ERROR_TOO_MANY_OPEN_FILES: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + case ERROR_WRITE_PROTECT: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_rmdir_error(PRInt32 err) +{ + + switch (err) { + case ERROR_FILE_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_INVALID_NAME: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_FILENAME_EXCED_RANGE: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ERROR_TOO_MANY_OPEN_FILES: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_WRITE_PROTECT: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_read_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_LOCKED: + case ERROR_SHARING_VIOLATION: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_transmitfile_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_LOCKED: + case ERROR_SHARING_VIOLATION: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_FILENAME_EXCED_RANGE: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ERROR_TOO_MANY_OPEN_FILES: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_write_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + case ERROR_WRITE_PROTECT: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_LOCKED: + case ERROR_SHARING_VIOLATION: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + case ENOSPC: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_lseek_error(PRInt32 err) +{ + switch (err) { + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_SEEK_ON_DEVICE: + PR_SetError(PR_IO_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_fsync_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + case ERROR_WRITE_PROTECT: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_close_error(PRInt32 err) +{ + switch (err) { + case ERROR_INVALID_HANDLE: + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_socket_error(PRInt32 err) +{ + switch (err) { + case EPROTONOSUPPORT: + PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_recv_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_recvfrom_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_send_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_sendto_error(PRInt32 err) +{ + _MD_os2_map_default_error(err); +} + +void _MD_os2_map_writev_error(int err) +{ + _MD_os2_map_default_error(err); +} + +void _MD_os2_map_accept_error(PRInt32 err) +{ + _MD_os2_map_default_error(err); +} + +void _MD_os2_map_acceptex_error(PRInt32 err) +{ + switch (err) { + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +/* + * An error code of 0 means that the nonblocking connect succeeded. + */ + +int _MD_os2_get_nonblocking_connect_error(int osfd) +{ + int err; + int len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) { + return sock_errno(); + } else { + return err; + } +} + +void _MD_os2_map_connect_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EINPROGRESS: + PR_SetError(PR_IN_PROGRESS_ERROR, err); + break; + case EALREADY: + case EINVAL: + PR_SetError(PR_ALREADY_INITIATED_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EAFNOSUPPORT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case ENETUNREACH: + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_bind_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_listen_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EOPNOTSUPP: + PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_shutdown_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOTCONN: + PR_SetError(PR_NOT_CONNECTED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_socketpair_error(PRInt32 err) +{ + switch (err) { + case ENOMEM: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case EAFNOSUPPORT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case EPROTONOSUPPORT: + PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err); + break; + case EOPNOTSUPP: + PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err); + break; + case EPROTOTYPE: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + default: + _MD_os2_map_default_error(err); + return; + } +} + +void _MD_os2_map_getsockname_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_getpeername_error(PRInt32 err) +{ + + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOTCONN: + PR_SetError(PR_NOT_CONNECTED_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_getsockopt_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOPROTOOPT: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case EINVAL: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_setsockopt_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOPROTOOPT: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case EINVAL: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_open_error(PRInt32 err) +{ + switch (err) { + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + PR_SetError(PR_FILE_EXISTS_ERROR, err); + break; + case ERROR_FILE_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_INVALID_NAME: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ERROR_NOT_READY: + case ERROR_OPEN_FAILED: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_FILENAME_EXCED_RANGE: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ERROR_TOO_MANY_OPEN_FILES: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case ERROR_PATH_NOT_FOUND: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + case ERROR_WRITE_PROTECT: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_gethostname_error(PRInt32 err) +{ + switch (err) { +#ifdef SOCEFAULT + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#endif + case ENETDOWN: + case EINPROGRESS: + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_select_error(PRInt32 err) +{ + PRErrorCode prerror; + + switch (err) { + /* + * OS/2 select() only works on sockets. So in this + * context, ENOTSOCK is equivalent to EBADF on Unix. + */ + case ENOTSOCK: + prerror = PR_BAD_DESCRIPTOR_ERROR; + break; + case EINVAL: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; +#ifdef SOCEFAULT + case SOCEFAULT: + prerror = PR_ACCESS_FAULT_ERROR; + break; +#endif + default: + prerror = PR_UNKNOWN_ERROR; + } + PR_SetError(prerror, err); +} + +void _MD_os2_map_lockf_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_INVALID_ADDRESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_LOCKED: + case ERROR_SHARING_VIOLATION: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} diff --git a/nsprpub/pr/src/md/os2/os2cv.c b/nsprpub/pr/src/md/os2/os2cv.c new file mode 100644 index 0000000000..91e85c0ebd --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2cv.c @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * os2cv.c -- OS/2 Machine-Dependent Code for Condition Variables + * + * We implement our own condition variable wait queue. Each thread + * has a semaphore object (thread->md.blocked_sema) to block on while + * waiting on a condition variable. + * + * We use a deferred condition notify algorithm. When PR_NotifyCondVar + * or PR_NotifyAllCondVar is called, the condition notifies are simply + * recorded in the _MDLock structure. We defer the condition notifies + * until right after we unlock the lock. This way the awakened threads + * have a better chance to reaquire the lock. + */ + +#include "primpl.h" + +/* + * AddThreadToCVWaitQueueInternal -- + * + * Add the thread to the end of the condition variable's wait queue. + * The CV's lock must be locked when this function is called. + */ + +static void +AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv) +{ + PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) + || (cv->waitTail == NULL && cv->waitHead == NULL)); + cv->nwait += 1; + thred->md.inCVWaitQueue = PR_TRUE; + thred->md.next = NULL; + thred->md.prev = cv->waitTail; + if (cv->waitHead == NULL) { + cv->waitHead = thred; + } else { + cv->waitTail->md.next = thred; + } + cv->waitTail = thred; +} + +/* + * md_UnlockAndPostNotifies -- + * + * Unlock the lock, and then do the deferred condition notifies. + * If waitThred and waitCV are not NULL, waitThred is added to + * the wait queue of waitCV before the lock is unlocked. + * + * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK, + * the two places where a lock is unlocked. + */ +void +md_UnlockAndPostNotifies( + _MDLock *lock, + PRThread *waitThred, + _MDCVar *waitCV) +{ + PRIntn index; + _MDNotified post; + _MDNotified *notified, *prev = NULL; + + /* + * Time to actually notify any conditions that were affected + * while the lock was held. Get a copy of the list that's in + * the lock structure and then zero the original. If it's + * linked to other such structures, we own that storage. + */ + post = lock->notified; /* a safe copy; we own the lock */ + +#if defined(DEBUG) + memset(&lock->notified, 0, sizeof(_MDNotified)); /* reset */ +#else + lock->notified.length = 0; /* these are really sufficient */ + lock->notified.link = NULL; +#endif + + /* + * Figure out how many threads we need to wake up. + */ + notified = &post; /* this is where we start */ + do { + for (index = 0; index < notified->length; ++index) { + _MDCVar *cv = notified->cv[index].cv; + PRThread *thred; + int i; + + /* Fast special case: no waiting threads */ + if (cv->waitHead == NULL) { + notified->cv[index].notifyHead = NULL; + continue; + } + + /* General case */ + if (-1 == notified->cv[index].times) { + /* broadcast */ + thred = cv->waitHead; + while (thred != NULL) { + thred->md.inCVWaitQueue = PR_FALSE; + thred = thred->md.next; + } + notified->cv[index].notifyHead = cv->waitHead; + cv->waitHead = cv->waitTail = NULL; + cv->nwait = 0; + } else { + thred = cv->waitHead; + i = notified->cv[index].times; + while (thred != NULL && i > 0) { + thred->md.inCVWaitQueue = PR_FALSE; + thred = thred->md.next; + i--; + } + notified->cv[index].notifyHead = cv->waitHead; + cv->waitHead = thred; + if (cv->waitHead == NULL) { + cv->waitTail = NULL; + } else { + if (cv->waitHead->md.prev != NULL) { + cv->waitHead->md.prev->md.next = NULL; + cv->waitHead->md.prev = NULL; + } + } + cv->nwait -= notified->cv[index].times - i; + } + } + notified = notified->link; + } while (NULL != notified); + + if (waitThred) { + AddThreadToCVWaitQueueInternal(waitThred, waitCV); + } + + /* Release the lock before notifying */ + DosReleaseMutexSem(lock->mutex); + + notified = &post; /* this is where we start */ + do { + for (index = 0; index < notified->length; ++index) { + PRThread *thred; + PRThread *next; + + thred = notified->cv[index].notifyHead; + while (thred != NULL) { + BOOL rv; + + next = thred->md.next; + thred->md.prev = thred->md.next = NULL; + rv = DosPostEventSem(thred->md.blocked_sema); + PR_ASSERT(rv == NO_ERROR); + thred = next; + } + } + prev = notified; + notified = notified->link; + if (&post != prev) { + PR_DELETE(prev); + } + } while (NULL != notified); +} + +/* + * Notifies just get posted to the protecting mutex. The + * actual notification is done when the lock is released so that + * MP systems don't contend for a lock that they can't have. + */ +static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock, + PRBool broadcast) +{ + PRIntn index = 0; + _MDNotified *notified = &lock->notified; + + while (1) { + for (index = 0; index < notified->length; ++index) { + if (notified->cv[index].cv == cvar) { + if (broadcast) { + notified->cv[index].times = -1; + } else if (-1 != notified->cv[index].times) { + notified->cv[index].times += 1; + } + return; + } + } + /* if not full, enter new CV in this array */ + if (notified->length < _MD_CV_NOTIFIED_LENGTH) { + break; + } + + /* if there's no link, create an empty array and link it */ + if (NULL == notified->link) { + notified->link = PR_NEWZAP(_MDNotified); + } + + notified = notified->link; + } + + /* A brand new entry in the array */ + notified->cv[index].times = (broadcast) ? -1 : 1; + notified->cv[index].cv = cvar; + notified->length += 1; +} + +/* + * _PR_MD_NEW_CV() -- Creating new condition variable + * ... Solaris uses cond_init() in similar function. + * + * returns: -1 on failure + * 0 when it succeeds. + * + */ +PRInt32 +_PR_MD_NEW_CV(_MDCVar *cv) +{ + cv->magic = _MD_MAGIC_CV; + /* + * The waitHead, waitTail, and nwait fields are zeroed + * when the PRCondVar structure is created. + */ + return 0; +} + +void _PR_MD_FREE_CV(_MDCVar *cv) +{ + cv->magic = (PRUint32)-1; + return; +} + +/* + * _PR_MD_WAIT_CV() -- Wait on condition variable + */ +void +_PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout ) +{ + PRThread *thred = _PR_MD_CURRENT_THREAD(); + ULONG rv, count; + ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ? + SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout); + + /* + * If we have pending notifies, post them now. + */ + if (0 != lock->notified.length) { + md_UnlockAndPostNotifies(lock, thred, cv); + } else { + AddThreadToCVWaitQueueInternal(thred, cv); + DosReleaseMutexSem(lock->mutex); + } + + /* Wait for notification or timeout; don't really care which */ + rv = DosWaitEventSem(thred->md.blocked_sema, msecs); + if (rv == NO_ERROR) { + DosResetEventSem(thred->md.blocked_sema, &count); + } + + DosRequestMutexSem((lock->mutex), SEM_INDEFINITE_WAIT); + + PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT); + + if(rv == ERROR_TIMEOUT) + { + if (thred->md.inCVWaitQueue) { + PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) + || (cv->waitTail == NULL && cv->waitHead == NULL)); + cv->nwait -= 1; + thred->md.inCVWaitQueue = PR_FALSE; + if (cv->waitHead == thred) { + cv->waitHead = thred->md.next; + if (cv->waitHead == NULL) { + cv->waitTail = NULL; + } else { + cv->waitHead->md.prev = NULL; + } + } else { + PR_ASSERT(thred->md.prev != NULL); + thred->md.prev->md.next = thred->md.next; + if (thred->md.next != NULL) { + thred->md.next->md.prev = thred->md.prev; + } else { + PR_ASSERT(cv->waitTail == thred); + cv->waitTail = thred->md.prev; + } + } + thred->md.next = thred->md.prev = NULL; + } else { + /* + * This thread must have been notified, but the + * SemRelease call happens after SemRequest + * times out. Wait on the semaphore again to make it + * non-signaled. We assume this wait won't take long. + */ + rv = DosWaitEventSem(thred->md.blocked_sema, SEM_INDEFINITE_WAIT); + if (rv == NO_ERROR) { + DosResetEventSem(thred->md.blocked_sema, &count); + } + PR_ASSERT(rv == NO_ERROR); + } + } + PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE); + return; +} /* --- end _PR_MD_WAIT_CV() --- */ + +void +_PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock) +{ + md_PostNotifyToCvar(cv, lock, PR_FALSE); + return; +} + +PRStatus +_PR_MD_NEW_LOCK(_MDLock *lock) +{ + DosCreateMutexSem(0, &(lock->mutex), 0, 0); + (lock)->notified.length=0; + (lock)->notified.link=NULL; + return PR_SUCCESS; +} + +void +_PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock) +{ + md_PostNotifyToCvar(cv, lock, PR_TRUE); + return; +} + +void _PR_MD_UNLOCK(_MDLock *lock) +{ + if (0 != lock->notified.length) { + md_UnlockAndPostNotifies(lock, NULL, NULL); + } else { + DosReleaseMutexSem(lock->mutex); + } +} diff --git a/nsprpub/pr/src/md/os2/os2emx.s b/nsprpub/pr/src/md/os2/os2emx.s new file mode 100644 index 0000000000..ef18e3bb14 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2emx.s @@ -0,0 +1,82 @@ +/ -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/ +/ This Source Code Form is subject to the terms of the Mozilla Public +/ License, v. 2.0. If a copy of the MPL was not distributed with this +/ file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/ PRInt32 __PR_MD_ATOMIC_INCREMENT(PRInt32 *val) +/ +/ Atomically increment the integer pointed to by 'val' and return +/ the result of the increment. +/ + .text + .globl __PR_MD_ATOMIC_INCREMENT + .align 4 +__PR_MD_ATOMIC_INCREMENT: + movl 4(%esp), %ecx + movl $1, %eax + lock + xaddl %eax, (%ecx) + incl %eax + ret + +/ PRInt32 __PR_MD_ATOMIC_DECREMENT(PRInt32 *val) +/ +/ Atomically decrement the integer pointed to by 'val' and return +/ the result of the decrement. +/ + .text + .globl __PR_MD_ATOMIC_DECREMENT + .align 4 +__PR_MD_ATOMIC_DECREMENT: + movl 4(%esp), %ecx + movl $-1, %eax + lock + xaddl %eax, (%ecx) + decl %eax + ret + +/ PRInt32 __PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +/ +/ Atomically set the integer pointed to by 'val' to the new +/ value 'newval' and return the old value. +/ +/ An alternative implementation: +/ .text +/ .globl __PR_MD_ATOMIC_SET +/ .align 4 +/__PR_MD_ATOMIC_SET: +/ movl 4(%esp), %ecx +/ movl 8(%esp), %edx +/ movl (%ecx), %eax +/retry: +/ lock +/ cmpxchgl %edx, (%ecx) +/ jne retry +/ ret +/ + .text + .globl __PR_MD_ATOMIC_SET + .align 4 +__PR_MD_ATOMIC_SET: + movl 4(%esp), %ecx + movl 8(%esp), %eax + xchgl %eax, (%ecx) + ret + +/ PRInt32 __PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) +/ +/ Atomically add 'val' to the integer pointed to by 'ptr' +/ and return the result of the addition. +/ + .text + .globl __PR_MD_ATOMIC_ADD + .align 4 +__PR_MD_ATOMIC_ADD: + movl 4(%esp), %ecx + movl 8(%esp), %eax + movl %eax, %edx + lock + xaddl %eax, (%ecx) + addl %edx, %eax + ret diff --git a/nsprpub/pr/src/md/os2/os2gc.c b/nsprpub/pr/src/md/os2/os2gc.c new file mode 100644 index 0000000000..79b6de5736 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2gc.c @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * GC related routines + * + */ +#include "primpl.h" + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + CONTEXTRECORD context; + context.ContextFlags = CONTEXT_INTEGER; + + if (_PR_IS_NATIVE_THREAD(t)) { + context.ContextFlags |= CONTEXT_CONTROL; + if (QueryThreadContext(t->md.handle, CONTEXT_CONTROL, &context)) { + t->md.gcContext[0] = context.ctx_RegEax; + t->md.gcContext[1] = context.ctx_RegEbx; + t->md.gcContext[2] = context.ctx_RegEcx; + t->md.gcContext[3] = context.ctx_RegEdx; + t->md.gcContext[4] = context.ctx_RegEsi; + t->md.gcContext[5] = context.ctx_RegEdi; + t->md.gcContext[6] = context.ctx_RegEsp; + t->md.gcContext[7] = context.ctx_RegEbp; + *np = PR_NUM_GCREGS; + } else { + PR_ASSERT(0);/* XXX */ + } + } + return (PRWord *)&t->md.gcContext; +} + +/* This function is not used right now, but is left as a reference. + * If you ever need to get the fiberID from the currently running fiber, + * this is it. + */ +void * +GetMyFiberID() +{ + void *fiberData = 0; + + /* A pointer to our tib entry is found at FS:[18] + * At offset 10h is the fiberData pointer. The context of the + * fiber is stored in there. + */ +#ifdef HAVE_ASM + __asm { + mov EDX, FS:[18h] + mov EAX, DWORD PTR [EDX+10h] + mov [fiberData], EAX + } +#endif + + return fiberData; +} diff --git a/nsprpub/pr/src/md/os2/os2inrval.c b/nsprpub/pr/src/md/os2/os2inrval.c new file mode 100644 index 0000000000..bd283f06d9 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2inrval.c @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * OS/2 interval timers + * + */ + +#include "primpl.h" + +static PRBool useHighResTimer = PR_FALSE; +PRIntervalTime _os2_ticksPerSec = -1; +PRIntn _os2_bitShift = 0; +PRInt32 _os2_highMask = 0; + +void +_PR_MD_INTERVAL_INIT() +{ + char *envp; + ULONG timerFreq; + APIRET rc; + + if ((envp = getenv("NSPR_OS2_NO_HIRES_TIMER")) != NULL) { + if (atoi(envp) == 1) { + return; + } + } + + timerFreq = 0; /* OS/2 high-resolution timer frequency in Hz */ + rc = DosTmrQueryFreq(&timerFreq); + if (NO_ERROR == rc) { + useHighResTimer = PR_TRUE; + PR_ASSERT(timerFreq != 0); + while (timerFreq > PR_INTERVAL_MAX) { + timerFreq >>= 1; + _os2_bitShift++; + _os2_highMask = (_os2_highMask << 1)+1; + } + + _os2_ticksPerSec = timerFreq; + PR_ASSERT(_os2_ticksPerSec > PR_INTERVAL_MIN); + } +} + +PRIntervalTime +_PR_MD_GET_INTERVAL() +{ + if (useHighResTimer) { + QWORD timestamp; + PRInt32 top; + APIRET rc = DosTmrQueryTime(×tamp); + if (NO_ERROR != rc) { + return -1; + } + /* Sadly, nspr requires the interval to range from 1000 ticks per + * second to only 100000 ticks per second. DosTmrQueryTime is too + * high resolution... + */ + top = timestamp.ulHi & _os2_highMask; + top = top << (32 - _os2_bitShift); + timestamp.ulLo = timestamp.ulLo >> _os2_bitShift; + timestamp.ulLo = timestamp.ulLo + top; + return (PRUint32)timestamp.ulLo; + } else { + ULONG msCount = -1; + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &msCount, sizeof(msCount)); + return msCount; + } +} + +PRIntervalTime +_PR_MD_INTERVAL_PER_SEC() +{ + if (useHighResTimer) { + return _os2_ticksPerSec; + } else { + return 1000; + } +} diff --git a/nsprpub/pr/src/md/os2/os2io.c b/nsprpub/pr/src/md/os2/os2io.c new file mode 100644 index 0000000000..639a0d3880 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2io.c @@ -0,0 +1,968 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* OS2 IO module + * + * Assumes synchronous I/O. + * + */ + +#include "primpl.h" +#include "prio.h" +#include <ctype.h> +#include <string.h> +#include <limits.h> +#include <dirent.h> +#include <fcntl.h> +#include <io.h> + +struct _MDLock _pr_ioq_lock; + +static PRBool isWSEB = PR_FALSE; /* whether we are using an OS/2 kernel that supports large files */ + +typedef APIRET (*DosOpenLType)(PSZ pszFileName, PHFILE pHf, PULONG pulAction, + LONGLONG cbFile, ULONG ulAttribute, + ULONG fsOpenFlags, ULONG fsOpenMode, + PEAOP2 peaop2); + +typedef APIRET (*DosSetFileLocksLType)(HFILE hFile, PFILELOCKL pflUnlock, + PFILELOCKL pflLock, ULONG timeout, + ULONG flags); + +typedef APIRET (*DosSetFilePtrLType)(HFILE hFile, LONGLONG ib, ULONG method, + PLONGLONG ibActual); + +DosOpenLType myDosOpenL; +DosSetFileLocksLType myDosSetFileLocksL; +DosSetFilePtrLType myDosSetFilePtrL; + +void +_PR_MD_INIT_IO() +{ + APIRET rc; + HMODULE module; + + sock_init(); + + rc = DosLoadModule(NULL, 0, "DOSCALL1", &module); + if (rc != NO_ERROR) + { + return; + } + rc = DosQueryProcAddr(module, 981, NULL, (PFN*) &myDosOpenL); + if (rc != NO_ERROR) + { + return; + } + rc = DosQueryProcAddr(module, 986, NULL, (PFN*) &myDosSetFileLocksL); + if (rc != NO_ERROR) + { + return; + } + rc = DosQueryProcAddr(module, 988, NULL, (PFN*) &myDosSetFilePtrL); + if (rc != NO_ERROR) + { + return; + } + isWSEB = PR_TRUE; +} + +PRStatus +_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PRInt32 rv; + ULONG count; + + PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? + SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(ticks); + rv = DosWaitEventSem(thread->md.blocked_sema, msecs); + DosResetEventSem(thread->md.blocked_sema, &count); + switch(rv) + { + case NO_ERROR: + return PR_SUCCESS; + break; + case ERROR_TIMEOUT: + _PR_THREAD_LOCK(thread); + if (thread->state == _PR_IO_WAIT) { + ; + } else { + if (thread->wait.cvar != NULL) { + thread->wait.cvar = NULL; + _PR_THREAD_UNLOCK(thread); + } else { + /* The CVAR was notified just as the timeout + * occurred. This led to us being notified twice. + * call SemRequest() to clear the semaphore. + */ + _PR_THREAD_UNLOCK(thread); + rv = DosWaitEventSem(thread->md.blocked_sema, 0); + DosResetEventSem(thread->md.blocked_sema, &count); + PR_ASSERT(rv == NO_ERROR); + } + } + return PR_SUCCESS; + break; + default: + break; + } + return PR_FAILURE; +} +PRStatus +_PR_MD_WAKEUP_WAITER(PRThread *thread) +{ + if ( _PR_IS_NATIVE_THREAD(thread) ) + { + if (DosPostEventSem(thread->md.blocked_sema) != NO_ERROR) { + return PR_FAILURE; + } + else { + return PR_SUCCESS; + } + } +} + + +/* --- FILE IO ----------------------------------------------------------- */ +/* + * _PR_MD_OPEN() -- Open a file + * + * returns: a fileHandle + * + * The NSPR open flags (osflags) are translated into flags for OS/2 + * + * Mode seems to be passed in as a unix style file permissions argument + * as in 0666, in the case of opening the logFile. + * + */ +PRInt32 +_PR_MD_OPEN(const char *name, PRIntn osflags, int mode) +{ + HFILE file; + PRInt32 access = OPEN_SHARE_DENYNONE; + PRInt32 flags = 0L; + APIRET rc = 0; + PRUword actionTaken; + +#ifdef MOZ_OS2_HIGH_MEMORY + /* + * All the pointer arguments (&file, &actionTaken and name) have to be in + * low memory for DosOpen to use them. + * The following moves name to low memory. + */ + if ((ULONG)name >= 0x20000000) + { + size_t len = strlen(name) + 1; + char *copy = (char *)alloca(len); + memcpy(copy, name, len); + name = copy; + } +#endif + + if (osflags & PR_SYNC) { + access |= OPEN_FLAGS_WRITE_THROUGH; + } + + if (osflags & PR_RDONLY) { + access |= OPEN_ACCESS_READONLY; + } + else if (osflags & PR_WRONLY) { + access |= OPEN_ACCESS_WRITEONLY; + } + else if(osflags & PR_RDWR) { + access |= OPEN_ACCESS_READWRITE; + } + + if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) + { + flags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS; + } + else if (osflags & PR_CREATE_FILE) + { + if (osflags & PR_TRUNCATE) { + flags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS; + } + else { + flags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; + } + } + else + { + if (osflags & PR_TRUNCATE) { + flags = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS; + } + else { + flags = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; + } + } + + do { + if (isWSEB) + { + rc = myDosOpenL((char*)name, + &file, /* file handle if successful */ + &actionTaken, /* reason for failure */ + 0, /* initial size of new file */ + FILE_NORMAL, /* file system attributes */ + flags, /* Open flags */ + access, /* Open mode and rights */ + 0); /* OS/2 Extended Attributes */ + } + else + { + rc = DosOpen((char*)name, + &file, /* file handle if successful */ + &actionTaken, /* reason for failure */ + 0, /* initial size of new file */ + FILE_NORMAL, /* file system attributes */ + flags, /* Open flags */ + access, /* Open mode and rights */ + 0); /* OS/2 Extended Attributes */ + }; + if (rc == ERROR_TOO_MANY_OPEN_FILES) { + ULONG CurMaxFH = 0; + LONG ReqCount = 20; + APIRET rc2; + rc2 = DosSetRelMaxFH(&ReqCount, &CurMaxFH); + if (rc2 != NO_ERROR) { + break; + } + } + } while (rc == ERROR_TOO_MANY_OPEN_FILES); + + if (rc != NO_ERROR) { + _PR_MD_MAP_OPEN_ERROR(rc); + return -1; + } + + return (PRInt32)file; +} + +PRInt32 +_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) +{ + ULONG bytes; + int rv; + + rv = DosRead((HFILE)fd->secret->md.osfd, + (PVOID)buf, + len, + &bytes); + + if (rv != NO_ERROR) + { + /* ERROR_HANDLE_EOF can only be returned by async io */ + PR_ASSERT(rv != ERROR_HANDLE_EOF); + if (rv == ERROR_BROKEN_PIPE) { + return 0; + } + else { + _PR_MD_MAP_READ_ERROR(rv); + return -1; + } + } + return (PRInt32)bytes; +} + +PRInt32 +_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len) +{ + PRInt32 bytes; + int rv; + + rv = DosWrite((HFILE)fd->secret->md.osfd, + (PVOID)buf, + len, + (PULONG)&bytes); + + if (rv != NO_ERROR) + { + _PR_MD_MAP_WRITE_ERROR(rv); + return -1; + } + + if (len != bytes) { + rv = ERROR_DISK_FULL; + _PR_MD_MAP_WRITE_ERROR(rv); + return -1; + } + + return bytes; +} /* --- end _PR_MD_WRITE() --- */ + +PRInt32 +_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + PRInt32 rv; + PRUword newLocation; + + rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, offset, whence, &newLocation); + + if (rv != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(rv); + return -1; + } else { + return newLocation; + } +} + +PRInt64 +_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ +#ifdef NO_LONG_LONG + PRInt64 result; + PRInt32 rv, low = offset.lo, hi = offset.hi; + PRUword newLocation; + + rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, low, whence, &newLocation); + rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, hi, FILE_CURRENT, &newLocation); + + if (rv != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(rv); + hi = newLocation = -1; + } + + result.lo = newLocation; + result.hi = hi; + return result; + +#else + PRInt32 where, rc, lo = (PRInt32)offset, hi = (PRInt32)(offset >> 32); + PRUint64 rv; + PRUint32 newLocation, uhi; + PRUint64 newLocationL; + + switch (whence) + { + case PR_SEEK_SET: + where = FILE_BEGIN; + break; + case PR_SEEK_CUR: + where = FILE_CURRENT; + break; + case PR_SEEK_END: + where = FILE_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + if (isWSEB) + { + rc = myDosSetFilePtrL((HFILE)fd->secret->md.osfd, offset, where, (PLONGLONG)&newLocationL); + } + else + { + rc = DosSetFilePtr((HFILE)fd->secret->md.osfd, lo, where, (PULONG)&newLocation); + } + + if (rc != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(rc); + return -1; + } + + if (isWSEB) + { + return newLocationL; + } + + uhi = (PRUint32)hi; + PR_ASSERT((PRInt32)uhi >= 0); + rv = uhi; + PR_ASSERT((PRInt64)rv >= 0); + rv = (rv << 32); + PR_ASSERT((PRInt64)rv >= 0); + rv += newLocation; + PR_ASSERT((PRInt64)rv >= 0); + return (PRInt64)rv; +#endif +} + +PRInt32 +_PR_MD_FSYNC(PRFileDesc *fd) +{ + PRInt32 rc = DosResetBuffer((HFILE)fd->secret->md.osfd); + + if (rc != NO_ERROR) { + if (rc != ERROR_ACCESS_DENIED) { + _PR_MD_MAP_FSYNC_ERROR(rc); + return -1; + } + } + return 0; +} + +PRInt32 +_MD_CloseFile(PRInt32 osfd) +{ + PRInt32 rv; + + rv = DosClose((HFILE)osfd); + if (rv != NO_ERROR) { + _PR_MD_MAP_CLOSE_ERROR(rv); + } + return rv; +} + + +/* --- DIR IO ------------------------------------------------------------ */ +#define GetFileFromDIR(d) (isWSEB?(d)->d_entry.large.achName:(d)->d_entry.small.achName) +#define GetFileAttr(d) (isWSEB?(d)->d_entry.large.attrFile:(d)->d_entry.small.attrFile) + +void FlipSlashes(char *cp, int len) +{ + while (--len >= 0) { + if (cp[0] == '/') { + cp[0] = PR_DIRECTORY_SEPARATOR; + } + cp++; + } +} + +/* +** +** Local implementations of standard Unix RTL functions which are not provided +** by the VAC RTL. +** +*/ + +PRInt32 +_PR_MD_CLOSE_DIR(_MDDir *d) +{ + PRInt32 rc; + + if ( d ) { + rc = DosFindClose(d->d_hdl); + if(rc == NO_ERROR) { + d->magic = (PRUint32)-1; + return PR_SUCCESS; + } else { + _PR_MD_MAP_CLOSEDIR_ERROR(rc); + return PR_FAILURE; + } + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; +} + + +PRStatus +_PR_MD_OPEN_DIR(_MDDir *d, const char *name) +{ + char filename[ CCHMAXPATH ]; + PRUword numEntries, rc; + + numEntries = 1; + + PR_snprintf(filename, CCHMAXPATH, "%s%s%s", + name, PR_DIRECTORY_SEPARATOR_STR, "*.*"); + FlipSlashes( filename, strlen(filename) ); + + d->d_hdl = HDIR_CREATE; + + if (isWSEB) + { + rc = DosFindFirst( filename, + &d->d_hdl, + FILE_DIRECTORY | FILE_HIDDEN, + &(d->d_entry.large), + sizeof(d->d_entry.large), + &numEntries, + FIL_STANDARDL); + } + else + { + rc = DosFindFirst( filename, + &d->d_hdl, + FILE_DIRECTORY | FILE_HIDDEN, + &(d->d_entry.small), + sizeof(d->d_entry.small), + &numEntries, + FIL_STANDARD); + } + if ( rc != NO_ERROR ) { + _PR_MD_MAP_OPENDIR_ERROR(rc); + return PR_FAILURE; + } + d->firstEntry = PR_TRUE; + d->magic = _MD_MAGIC_DIR; + return PR_SUCCESS; +} + +char * +_PR_MD_READ_DIR(_MDDir *d, PRIntn flags) +{ + PRUword numFiles = 1; + BOOL rv; + char *fileName; + USHORT fileAttr; + + if ( d ) { + while (1) { + if (d->firstEntry) { + d->firstEntry = PR_FALSE; + rv = NO_ERROR; + } else { + rv = DosFindNext(d->d_hdl, + &(d->d_entry), + sizeof(d->d_entry), + &numFiles); + } + if (rv != NO_ERROR) { + break; + } + fileName = GetFileFromDIR(d); + fileAttr = GetFileAttr(d); + if ( (flags & PR_SKIP_DOT) && + (fileName[0] == '.') && (fileName[1] == '\0')) { + continue; + } + if ( (flags & PR_SKIP_DOT_DOT) && + (fileName[0] == '.') && (fileName[1] == '.') && + (fileName[2] == '\0')) { + continue; + } + /* + * XXX + * Is this the correct definition of a hidden file on OS/2? + */ + if ((flags & PR_SKIP_NONE) && (fileAttr & FILE_HIDDEN)) { + return fileName; + } + else if ((flags & PR_SKIP_HIDDEN) && (fileAttr & FILE_HIDDEN)) { + continue; + } + return fileName; + } + PR_ASSERT(NO_ERROR != rv); + _PR_MD_MAP_READDIR_ERROR(rv); + return NULL; + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; +} + +PRInt32 +_PR_MD_DELETE(const char *name) +{ + PRInt32 rc = DosDelete((char*)name); + if(rc == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_DELETE_ERROR(rc); + return -1; + } +} + +PRInt32 +_PR_MD_STAT(const char *fn, struct stat *info) +{ + PRInt32 rv; + char filename[CCHMAXPATH]; + + PR_snprintf(filename, CCHMAXPATH, "%s", fn); + FlipSlashes(filename, strlen(filename)); + + rv = _stat((char*)filename, info); + if (-1 == rv) { + /* + * Check for MSVC runtime library _stat() bug. + * (It's really a bug in FindFirstFile().) + * If a pathname ends in a backslash or slash, + * e.g., c:\temp\ or c:/temp/, _stat() will fail. + * Note: a pathname ending in a slash (e.g., c:/temp/) + * can be handled by _stat() on NT but not on Win95. + * + * We remove the backslash or slash at the end and + * try again. + * + * Not sure if this happens on OS/2 or not, + * but it doesn't hurt to be careful. + */ + + int len = strlen(fn); + if (len > 0 && len <= _MAX_PATH + && (fn[len - 1] == '\\' || fn[len - 1] == '/')) { + char newfn[_MAX_PATH + 1]; + + strcpy(newfn, fn); + newfn[len - 1] = '\0'; + rv = _stat(newfn, info); + } + } + + if (-1 == rv) { + _PR_MD_MAP_STAT_ERROR(errno); + } + return rv; +} + +PRInt32 +_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info) +{ + struct stat sb; + PRInt32 rv; + PRInt64 s, s2us; + + if ( (rv = _PR_MD_STAT(fn, &sb)) == 0 ) { + if (info) { + if (S_IFREG & sb.st_mode) { + info->type = PR_FILE_FILE ; + } + else if (S_IFDIR & sb.st_mode) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_OTHER; + } + info->size = sb.st_size; + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, sb.st_mtime); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb.st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; + } + } + return rv; +} + +PRInt32 +_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info) +{ + PRFileInfo info32; + PRInt32 rv = _PR_MD_GETFILEINFO(fn, &info32); + if (rv != 0) + { + return rv; + } + info->type = info32.type; + LL_UI2L(info->size,info32.size); + info->modifyTime = info32.modifyTime; + info->creationTime = info32.creationTime; + + if (isWSEB) + { + APIRET rc ; + FILESTATUS3L fstatus; + + rc = DosQueryPathInfo(fn, FIL_STANDARDL, &fstatus, sizeof(fstatus)); + + if (NO_ERROR != rc) + { + _PR_MD_MAP_OPEN_ERROR(rc); + return -1; + } + + if (! (fstatus.attrFile & FILE_DIRECTORY)) + { + info->size = fstatus.cbFile; + } + } + + return rv; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info) +{ + /* For once, the VAC compiler/library did a nice thing. + * The file handle used by the C runtime is the same one + * returned by the OS when you call DosOpen(). This means + * that you can take an OS HFILE and use it with C file + * functions. The only caveat is that you have to call + * _setmode() first to initialize some junk. This is + * immensely useful because I did not have a clue how to + * implement this function otherwise. The windows folks + * took the source from the Microsoft C library source, but + * IBM wasn't kind enough to ship the source with VAC. + * On second thought, the needed function could probably + * be gotten from the OS/2 GNU library source, but the + * point is now moot. + */ + struct stat hinfo; + PRInt64 s, s2us; + + _setmode(fd->secret->md.osfd, O_BINARY); + if(fstat((int)fd->secret->md.osfd, &hinfo) != NO_ERROR) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + + if (hinfo.st_mode & S_IFDIR) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_FILE; + } + + info->size = hinfo.st_size; + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, hinfo.st_mtime); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, hinfo.st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; + + return 0; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info) +{ + PRFileInfo info32; + PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, &info32); + if (0 == rv) + { + info->type = info32.type; + LL_UI2L(info->size,info32.size); + + info->modifyTime = info32.modifyTime; + info->creationTime = info32.creationTime; + } + + if (isWSEB) + { + APIRET rc ; + FILESTATUS3L fstatus; + + rc = DosQueryFileInfo(fd->secret->md.osfd, FIL_STANDARDL, &fstatus, sizeof(fstatus)); + + if (NO_ERROR != rc) + { + _PR_MD_MAP_OPEN_ERROR(rc); + return -1; + } + + if (! (fstatus.attrFile & FILE_DIRECTORY)) + { + info->size = fstatus.cbFile; + } + } + + return rv; +} + + +PRInt32 +_PR_MD_RENAME(const char *from, const char *to) +{ + PRInt32 rc; + /* Does this work with dot-relative pathnames? */ + if ( (rc = DosMove((char *)from, (char *)to)) == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_RENAME_ERROR(rc); + return -1; + } +} + +PRInt32 +_PR_MD_ACCESS(const char *name, PRAccessHow how) +{ + PRInt32 rv; + switch (how) { + case PR_ACCESS_WRITE_OK: + rv = access(name, 02); + break; + case PR_ACCESS_READ_OK: + rv = access(name, 04); + break; + case PR_ACCESS_EXISTS: + return access(name, 00); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + if (rv < 0) { + _PR_MD_MAP_ACCESS_ERROR(errno); + } + return rv; +} + +PRInt32 +_PR_MD_MKDIR(const char *name, PRIntn mode) +{ + PRInt32 rc; + /* XXXMB - how to translate the "mode"??? */ + if ((rc = DosCreateDir((char *)name, NULL))== NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_MKDIR_ERROR(rc); + return -1; + } +} + +PRInt32 +_PR_MD_RMDIR(const char *name) +{ + PRInt32 rc; + if ( (rc = DosDeleteDir((char *)name)) == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_RMDIR_ERROR(rc); + return -1; + } +} + +PRStatus +_PR_MD_LOCKFILE(PRInt32 f) +{ + PRInt32 rv; + FILELOCK lock, unlock; + FILELOCKL lockL, unlockL; + + lock.lOffset = 0; + lockL.lOffset = 0; + lock.lRange = 0xffffffff; + lockL.lRange = 0xffffffffffffffff; + unlock.lOffset = 0; + unlock.lRange = 0; + unlockL.lOffset = 0; + unlockL.lRange = 0; + + /* + * loop trying to DosSetFileLocks(), + * pause for a few miliseconds when can't get the lock + * and try again + */ + for( rv = FALSE; rv == FALSE; /* do nothing */ ) + { + if (isWSEB) + { + rv = myDosSetFileLocksL( (HFILE) f, + &unlockL, &lockL, + 0, 0); + } + else + { + rv = DosSetFileLocks( (HFILE) f, + &unlock, &lock, + 0, 0); + } + if ( rv != NO_ERROR ) + { + DosSleep( 50 ); /* Sleep() a few milisecs and try again. */ + } + } /* end for() */ + return PR_SUCCESS; +} /* end _PR_MD_LOCKFILE() */ + +PRStatus +_PR_MD_TLOCKFILE(PRInt32 f) +{ + return _PR_MD_LOCKFILE(f); +} /* end _PR_MD_TLOCKFILE() */ + + +PRStatus +_PR_MD_UNLOCKFILE(PRInt32 f) +{ + PRInt32 rv; + FILELOCK lock, unlock; + FILELOCKL lockL, unlockL; + + lock.lOffset = 0; + lockL.lOffset = 0; + lock.lRange = 0; + lockL.lRange = 0; + unlock.lOffset = 0; + unlockL.lOffset = 0; + unlock.lRange = 0xffffffff; + unlockL.lRange = 0xffffffffffffffff; + + if (isWSEB) + { + rv = myDosSetFileLocksL( (HFILE) f, + &unlockL, &lockL, + 0, 0); + } + else + { + rv = DosSetFileLocks( (HFILE) f, + &unlock, &lock, + 0, 0); + } + + if ( rv != NO_ERROR ) + { + return PR_SUCCESS; + } + else + { + return PR_FAILURE; + } +} /* end _PR_MD_UNLOCKFILE() */ + +PRStatus +_PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable) +{ + APIRET rc = 0; + ULONG flags; + switch (fd->methods->file_type) + { + case PR_DESC_PIPE: + case PR_DESC_FILE: + rc = DosQueryFHState((HFILE)fd->secret->md.osfd, &flags); + if (rc != NO_ERROR) { + PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } + + if (inheritable) { + flags &= ~OPEN_FLAGS_NOINHERIT; + } + else { + flags |= OPEN_FLAGS_NOINHERIT; + } + + /* Mask off flags DosSetFHState don't want. */ + flags &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT); + rc = DosSetFHState((HFILE)fd->secret->md.osfd, flags); + if (rc != NO_ERROR) { + PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } + break; + + case PR_DESC_LAYERED: + /* what to do here? */ + PR_SetError(PR_UNKNOWN_ERROR, 87 /*ERROR_INVALID_PARAMETER*/); + return PR_FAILURE; + + case PR_DESC_SOCKET_TCP: + case PR_DESC_SOCKET_UDP: + /* These are global on OS/2. */ + break; + } + + return PR_SUCCESS; +} + +void +_PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported) +{ + /* XXX this function needs to be implemented */ + fd->secret->inheritable = _PR_TRI_UNKNOWN; +} + +void +_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd) +{ + /* XXX this function needs to be reviewed */ + ULONG flags; + + PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); + if (DosQueryFHState((HFILE)fd->secret->md.osfd, &flags) == 0) { + if (flags & OPEN_FLAGS_NOINHERIT) { + fd->secret->inheritable = _PR_TRI_FALSE; + } else { + fd->secret->inheritable = _PR_TRI_TRUE; + } + } +} diff --git a/nsprpub/pr/src/md/os2/os2misc.c b/nsprpub/pr/src/md/os2/os2misc.c new file mode 100644 index 0000000000..240f0905c9 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2misc.c @@ -0,0 +1,620 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * os2misc.c + * + */ + +#ifdef MOZ_OS2_HIGH_MEMORY +/* os2safe.h has to be included before os2.h, needed for high mem */ +#include <os2safe.h> +#endif + +#include <string.h> +#include "primpl.h" + +extern int _CRT_init(void); +extern void _CRT_term(void); +extern void __ctordtorInit(int flag); +extern void __ctordtorTerm(int flag); + +char * +_PR_MD_GET_ENV(const char *name) +{ + return getenv(name); +} + +PRIntn +_PR_MD_PUT_ENV(const char *name) +{ + return putenv(name); +} + + +/* + ************************************************************************** + ************************************************************************** + ** + ** Date and time routines + ** + ************************************************************************** + ************************************************************************** + */ + +#include <sys/timeb.h> +/* + *----------------------------------------------------------------------- + * + * PR_Now -- + * + * Returns the current time in microseconds since the epoch. + * The epoch is midnight January 1, 1970 GMT. + * The implementation is machine dependent. This is the + * implementation for OS/2. + * Cf. time_t time(time_t *tp) + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(PRTime) +PR_Now(void) +{ + PRInt64 s, ms, ms2us, s2us; + struct timeb b; + + ftime(&b); + LL_I2L(ms2us, PR_USEC_PER_MSEC); + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, b.time); + LL_I2L(ms, b.millitm); + LL_MUL(ms, ms, ms2us); + LL_MUL(s, s, s2us); + LL_ADD(s, s, ms); + return s; +} + + +/* + *********************************************************************** + *********************************************************************** + * + * Process creation routines + * + *********************************************************************** + *********************************************************************** + */ + +/* + * Assemble the command line by concatenating the argv array. + * Special characters intentionally do not get escaped, and it is + * expected that the caller wraps arguments in quotes if needed + * (e.g. for filename with spaces). + * + * On success, this function returns 0 and the resulting command + * line is returned in *cmdLine. On failure, it returns -1. + */ +static int assembleCmdLine(char *const *argv, char **cmdLine) +{ + char *const *arg; + int cmdLineSize; + + /* + * Find out how large the command line buffer should be. + */ + cmdLineSize = 1; /* final null */ + for (arg = argv+1; *arg; arg++) { + cmdLineSize += strlen(*arg) + 1; /* space in between */ + } + *cmdLine = PR_MALLOC(cmdLineSize); + if (*cmdLine == NULL) { + return -1; + } + + (*cmdLine)[0] = '\0'; + + for (arg = argv+1; *arg; arg++) { + if (arg > argv +1) { + strcat(*cmdLine, " "); + } + strcat(*cmdLine, *arg); + } + return 0; +} + +/* + * Assemble the environment block by concatenating the envp array + * (preserving the terminating null byte in each array element) + * and adding a null byte at the end. + * + * Returns 0 on success. The resulting environment block is returned + * in *envBlock. Note that if envp is NULL, a NULL pointer is returned + * in *envBlock. Returns -1 on failure. + */ +static int assembleEnvBlock(char **envp, char **envBlock) +{ + char *p; + char *q; + char **env; + char *curEnv; + char *cwdStart, *cwdEnd; + int envBlockSize; + + PPIB ppib = NULL; + PTIB ptib = NULL; + + if (envp == NULL) { + *envBlock = NULL; + return 0; + } + + if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + return -1; + } + + curEnv = ppib->pib_pchenv; + + cwdStart = curEnv; + while (*cwdStart) { + if (cwdStart[0] == '=' && cwdStart[1] != '\0' + && cwdStart[2] == ':' && cwdStart[3] == '=') { + break; + } + cwdStart += strlen(cwdStart) + 1; + } + cwdEnd = cwdStart; + if (*cwdEnd) { + cwdEnd += strlen(cwdEnd) + 1; + while (*cwdEnd) { + if (cwdEnd[0] != '=' || cwdEnd[1] == '\0' + || cwdEnd[2] != ':' || cwdEnd[3] != '=') { + break; + } + cwdEnd += strlen(cwdEnd) + 1; + } + } + envBlockSize = cwdEnd - cwdStart; + + for (env = envp; *env; env++) { + envBlockSize += strlen(*env) + 1; + } + envBlockSize++; + + p = *envBlock = PR_MALLOC(envBlockSize); + if (p == NULL) { + return -1; + } + + q = cwdStart; + while (q < cwdEnd) { + *p++ = *q++; + } + + for (env = envp; *env; env++) { + q = *env; + while (*q) { + *p++ = *q++; + } + *p++ = '\0'; + } + *p = '\0'; + return 0; +} + +/* + * For qsort. We sort (case-insensitive) the environment strings + * before generating the environment block. + */ +static int compare(const void *arg1, const void *arg2) +{ + return stricmp(* (char**)arg1, * (char**)arg2); +} + +PRProcess * _PR_CreateOS2Process( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + PRProcess *proc = NULL; + char *cmdLine = NULL; + char **newEnvp = NULL; + char *envBlock = NULL; + + STARTDATA startData = {0}; + APIRET rc; + ULONG ulAppType = 0; + PID pid = 0; + char *pszComSpec; + char pszEXEName[CCHMAXPATH] = ""; + char pszFormatString[CCHMAXPATH]; + char pszObjectBuffer[CCHMAXPATH]; + char *pszFormatResult = NULL; + + /* + * Variables for DosExecPgm + */ + char szFailed[CCHMAXPATH]; + char *pszCmdLine = NULL; + RESULTCODES procInfo; + HFILE hStdIn = 0, + hStdOut = 0, + hStdErr = 0; + HFILE hStdInSave = -1, + hStdOutSave = -1, + hStdErrSave = -1; + + proc = PR_NEW(PRProcess); + if (!proc) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto errorExit; + } + + if (assembleCmdLine(argv, &cmdLine) == -1) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto errorExit; + } + +#ifdef MOZ_OS2_HIGH_MEMORY + /* + * DosQueryAppType() fails if path (the char* in the first argument) is in + * high memory. If that is the case, the following moves it to low memory. + */ + if ((ULONG)path >= 0x20000000) { + size_t len = strlen(path) + 1; + char *copy = (char *)alloca(len); + memcpy(copy, path, len); + path = copy; + } +#endif + + if (envp == NULL) { + newEnvp = NULL; + } else { + int i; + int numEnv = 0; + while (envp[numEnv]) { + numEnv++; + } + newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *)); + for (i = 0; i <= numEnv; i++) { + newEnvp[i] = envp[i]; + } + qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare); + } + if (assembleEnvBlock(newEnvp, &envBlock) == -1) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto errorExit; + } + + rc = DosQueryAppType(path, &ulAppType); + if (rc != NO_ERROR) { + char *pszDot = strrchr(path, '.'); + if (pszDot) { + /* If it is a CMD file, launch the users command processor */ + if (!stricmp(pszDot, ".cmd")) { + rc = DosScanEnv("COMSPEC", (PSZ *)&pszComSpec); + if (!rc) { + strcpy(pszFormatString, "/C %s %s"); + strcpy(pszEXEName, pszComSpec); + ulAppType = FAPPTYP_WINDOWCOMPAT; + } + } + } + } + if (ulAppType == 0) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + goto errorExit; + } + + if ((ulAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) { + startData.SessionType = SSF_TYPE_PM; + } + else if (ulAppType & FAPPTYP_WINDOWCOMPAT) { + startData.SessionType = SSF_TYPE_WINDOWABLEVIO; + } + else { + startData.SessionType = SSF_TYPE_DEFAULT; + } + + if (ulAppType & (FAPPTYP_WINDOWSPROT31 | FAPPTYP_WINDOWSPROT | FAPPTYP_WINDOWSREAL)) + { + strcpy(pszEXEName, "WINOS2.COM"); + startData.SessionType = PROG_31_STDSEAMLESSVDM; + strcpy(pszFormatString, "/3 %s %s"); + } + + startData.InheritOpt = SSF_INHERTOPT_SHELL; + + if (pszEXEName[0]) { + pszFormatResult = PR_MALLOC(strlen(pszFormatString)+strlen(path)+strlen(cmdLine)); + sprintf(pszFormatResult, pszFormatString, path, cmdLine); + startData.PgmInputs = pszFormatResult; + } else { + strcpy(pszEXEName, path); + startData.PgmInputs = cmdLine; + } + startData.PgmName = pszEXEName; + + startData.Length = sizeof(startData); + startData.Related = SSF_RELATED_INDEPENDENT; + startData.ObjectBuffer = pszObjectBuffer; + startData.ObjectBuffLen = CCHMAXPATH; + startData.Environment = envBlock; + + if (attr) { + /* On OS/2, there is really no way to pass file handles for stdin, + * stdout, and stderr to a new process. Instead, we can make it + * a child process and make the given file handles a copy of our + * stdin, stdout, and stderr. The child process then inherits + * ours, and we set ours back. Twisted and gross I know. If you + * know a better way, please use it. + */ + if (attr->stdinFd) { + hStdIn = 0; + DosDupHandle(hStdIn, &hStdInSave); + DosDupHandle((HFILE) attr->stdinFd->secret->md.osfd, &hStdIn); + } + + if (attr->stdoutFd) { + hStdOut = 1; + DosDupHandle(hStdOut, &hStdOutSave); + DosDupHandle((HFILE) attr->stdoutFd->secret->md.osfd, &hStdOut); + } + + if (attr->stderrFd) { + hStdErr = 2; + DosDupHandle(hStdErr, &hStdErrSave); + DosDupHandle((HFILE) attr->stderrFd->secret->md.osfd, &hStdErr); + } + /* + * Build up the Command Line for DosExecPgm + */ + pszCmdLine = PR_MALLOC(strlen(pszEXEName) + + strlen(startData.PgmInputs) + 3); + sprintf(pszCmdLine, "%s%c%s%c", pszEXEName, '\0', + startData.PgmInputs, '\0'); + rc = DosExecPgm(szFailed, + CCHMAXPATH, + EXEC_ASYNCRESULT, + pszCmdLine, + envBlock, + &procInfo, + pszEXEName); + PR_DELETE(pszCmdLine); + + /* Restore our old values. Hope this works */ + if (hStdInSave != -1) { + DosDupHandle(hStdInSave, &hStdIn); + DosClose(hStdInSave); + } + + if (hStdOutSave != -1) { + DosDupHandle(hStdOutSave, &hStdOut); + DosClose(hStdOutSave); + } + + if (hStdErrSave != -1) { + DosDupHandle(hStdErrSave, &hStdErr); + DosClose(hStdErrSave); + } + + if (rc != NO_ERROR) { + /* XXX what error code? */ + PR_SetError(PR_UNKNOWN_ERROR, rc); + goto errorExit; + } + + proc->md.pid = procInfo.codeTerminate; + } else { + /* + * If no STDIN/STDOUT redirection is not needed, use DosStartSession + * to create a new, independent session + */ + rc = DosStartSession(&startData, &ulAppType, &pid); + + if ((rc != NO_ERROR) && (rc != ERROR_SMG_START_IN_BACKGROUND)) { + PR_SetError(PR_UNKNOWN_ERROR, rc); + goto errorExit; + } + + proc->md.pid = pid; + } + + if (pszFormatResult) { + PR_DELETE(pszFormatResult); + } + + PR_DELETE(cmdLine); + if (newEnvp) { + PR_DELETE(newEnvp); + } + if (envBlock) { + PR_DELETE(envBlock); + } + return proc; + +errorExit: + if (cmdLine) { + PR_DELETE(cmdLine); + } + if (newEnvp) { + PR_DELETE(newEnvp); + } + if (envBlock) { + PR_DELETE(envBlock); + } + if (proc) { + PR_DELETE(proc); + } + return NULL; +} /* _PR_CreateOS2Process */ + +PRStatus _PR_DetachOS2Process(PRProcess *process) +{ + /* On OS/2, a process is either created as a child or not. + * You can't 'detach' it later on. + */ + PR_DELETE(process); + return PR_SUCCESS; +} + +/* + * XXX: This will currently only work on a child process. + */ +PRStatus _PR_WaitOS2Process(PRProcess *process, + PRInt32 *exitCode) +{ + ULONG ulRetVal; + RESULTCODES results; + PID pidEnded = 0; + + ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, + &results, + &pidEnded, process->md.pid); + + if (ulRetVal != NO_ERROR) { + printf("\nDosWaitChild rc = %lu\n", ulRetVal); + PR_SetError(PR_UNKNOWN_ERROR, ulRetVal); + return PR_FAILURE; + } + PR_DELETE(process); + return PR_SUCCESS; +} + +PRStatus _PR_KillOS2Process(PRProcess *process) +{ + ULONG ulRetVal; + if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) { + return PR_SUCCESS; + } + PR_SetError(PR_UNKNOWN_ERROR, ulRetVal); + return PR_FAILURE; +} + +PRStatus _MD_OS2GetHostName(char *name, PRUint32 namelen) +{ + PRIntn rv; + + rv = gethostname(name, (PRInt32) namelen); + if (0 == rv) { + return PR_SUCCESS; + } + _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno()); + return PR_FAILURE; +} + +void +_PR_MD_WAKEUP_CPUS( void ) +{ + return; +} + +/* + ********************************************************************** + * + * Memory-mapped files + * + * A credible emulation of memory-mapped i/o on OS/2 would require + * an exception-handler on each thread that might access the mapped + * memory. In the Mozilla environment, that would be impractical. + * Instead, the following simulates those modes which don't modify + * the mapped file. It reads the entire mapped file segment at once + * when MemMap is called, and frees it on MemUnmap. CreateFileMap + * only does sanity-checks, while CloseFileMap does almost nothing. + * + ********************************************************************** + */ + +PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) +{ + PRFileInfo64 info; + + /* assert on PR_PROT_READWRITE which modifies the underlying file */ + PR_ASSERT(fmap->prot == PR_PROT_READONLY || + fmap->prot == PR_PROT_WRITECOPY); + if (fmap->prot != PR_PROT_READONLY && + fmap->prot != PR_PROT_WRITECOPY) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + if (PR_GetOpenFileInfo64(fmap->fd, &info) == PR_FAILURE) { + return PR_FAILURE; + } + /* reject zero-byte mappings & zero-byte files */ + if (!size || !info.size) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + /* file size rounded up to the next page boundary */ + fmap->md.maxExtent = (info.size + 0xfff) & ~(0xfff); + + return PR_SUCCESS; +} + +PRInt32 _MD_GetMemMapAlignment(void) +{ + /* OS/2 pages are 4k */ + return 0x1000; +} + +void * _MD_MemMap(PRFileMap *fmap, PROffset64 offset, PRUint32 len) +{ + PRUint32 rv; + void *addr; + + /* prevent mappings beyond EOF + remainder of page */ + if (offset + len > fmap->md.maxExtent) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + if (PR_Seek64(fmap->fd, offset, PR_SEEK_SET) == -1) { + return NULL; + } + /* try for high memory, fall back to low memory if hi-mem fails */ + rv = DosAllocMem(&addr, len, OBJ_ANY | PAG_COMMIT | PAG_READ | PAG_WRITE); + if (rv) { + rv = DosAllocMem(&addr, len, PAG_COMMIT | PAG_READ | PAG_WRITE); + if (rv) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, rv); + return NULL; + } + } + if (PR_Read(fmap->fd, addr, len) == -1) { + DosFreeMem(addr); + return NULL; + } + /* don't permit writes if readonly */ + if (fmap->prot == PR_PROT_READONLY) { + rv = DosSetMem(addr, len, PAG_READ); + if (rv) { + DosFreeMem(addr); + PR_SetError(PR_UNKNOWN_ERROR, rv); + return NULL; + } + } + return addr; +} + +PRStatus _MD_MemUnmap(void *addr, PRUint32 len) +{ + PRUint32 rv; + + /* we just have to trust that addr & len are those used by MemMap */ + rv = DosFreeMem(addr); + if (rv) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PRStatus _MD_CloseFileMap(PRFileMap *fmap) +{ + /* nothing to do except free the PRFileMap struct */ + PR_Free(fmap); + return PR_SUCCESS; +} + diff --git a/nsprpub/pr/src/md/os2/os2poll.c b/nsprpub/pr/src/md/os2/os2poll.c new file mode 100644 index 0000000000..47d9710454 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2poll.c @@ -0,0 +1,361 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file implements _PR_MD_PR_POLL for OS/2. + */ + +#include <sys/time.h> /* For timeval. */ + +#include "primpl.h" + +#ifndef BSD_SELECT +/* Utility functions called when using OS/2 select */ + +PRBool IsSocketSet( PRInt32 osfd, int* socks, int start, int count ) +{ + int i; + PRBool isSet = PR_FALSE; + + for( i = start; i < start+count; i++ ) + { + if( socks[i] == osfd ) { + isSet = PR_TRUE; + } + } + + return isSet; +} +#endif + +PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ +#ifdef BSD_SELECT + fd_set rd, wt, ex; +#else + int rd, wt, ex; + int* socks; + unsigned long msecs; + int i, j; +#endif + PRFileDesc *bottom; + PRPollDesc *pd, *epd; + PRInt32 maxfd = -1, ready, err; + PRIntervalTime remaining, elapsed, start; + +#ifdef BSD_SELECT + struct timeval tv, *tvp = NULL; + + FD_ZERO(&rd); + FD_ZERO(&wt); + FD_ZERO(&ex); +#else + rd = 0; + wt = 0; + ex = 0; + socks = (int) PR_MALLOC( npds * 3 * sizeof(int) ); + + if (!socks) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } +#endif + + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + if (pd->in_flags & PR_POLL_READ) + { + in_flags_read = (pd->fd->methods->poll)( + pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read); + } + if (pd->in_flags & PR_POLL_WRITE) + { + in_flags_write = (pd->fd->methods->poll)( + pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) || + (0 != (in_flags_write & out_flags_write))) + { + /* this one's ready right now */ + if (0 == ready) + { + /* + * We will have to return without calling the + * system poll/select function. So zero the + * out_flags fields of all the poll descriptors + * before this one. + */ + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; + pd->out_flags = out_flags_read | out_flags_write; + } + else + { + pd->out_flags = 0; /* pre-condition */ + + /* make sure this is an NSPR supported stack */ + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); /* what to do about that? */ + if ((NULL != bottom) && + (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + PRInt32 osfd = bottom->secret->md.osfd; + if (osfd > maxfd) { + maxfd = osfd; + } + if (in_flags_read & PR_POLL_READ) + { + pd->out_flags |= _PR_POLL_READ_SYS_READ; +#ifdef BSD_SELECT + FD_SET(osfd, &rd); +#else + socks[rd] = osfd; + rd++; +#endif + } + if (in_flags_read & PR_POLL_WRITE) + { + pd->out_flags |= _PR_POLL_READ_SYS_WRITE; +#ifdef BSD_SELECT + FD_SET(osfd, &wt); +#else + socks[npds+wt] = osfd; + wt++; +#endif + } + if (in_flags_write & PR_POLL_READ) + { + pd->out_flags |= _PR_POLL_WRITE_SYS_READ; +#ifdef BSD_SELECT + FD_SET(osfd, &rd); +#else + socks[rd] = osfd; + rd++; +#endif + } + if (in_flags_write & PR_POLL_WRITE) + { + pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE; +#ifdef BSD_SELECT + FD_SET(osfd, &wt); +#else + socks[npds+wt] = osfd; + wt++; +#endif + } + if (pd->in_flags & PR_POLL_EXCEPT) + { +#ifdef BSD_SELECT + FD_SET(osfd, &ex); +#else + socks[npds*2+ex] = osfd; + ex++; +#endif + } + } + } + else + { + if (0 == ready) + { + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pd->out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + pd->out_flags = 0; + } + } + + if (0 != ready) + { +#ifndef BSD_SELECT + PR_Free(socks); +#endif + return ready; /* no need to block */ + } + + remaining = timeout; + start = PR_IntervalNow(); + +retry: +#ifdef BSD_SELECT + if (timeout != PR_INTERVAL_NO_TIMEOUT) + { + PRInt32 ticksPerSecond = PR_TicksPerSecond(); + tv.tv_sec = remaining / ticksPerSecond; + tv.tv_usec = PR_IntervalToMicroseconds( remaining % ticksPerSecond ); + tvp = &tv; + } + + ready = bsdselect(maxfd + 1, &rd, &wt, &ex, tvp); +#else + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: + msecs = 0; + break; + case PR_INTERVAL_NO_TIMEOUT: + msecs = -1; + break; + default: + msecs = PR_IntervalToMilliseconds(remaining); + } + + /* compact array */ + for( i = rd, j = npds; j < npds+wt; i++,j++ ) { + socks[i] = socks[j]; + } + for( i = rd+wt, j = npds*2; j < npds*2+ex; i++,j++ ) { + socks[i] = socks[j]; + } + + ready = os2_select(socks, rd, wt, ex, msecs); +#endif + + if (ready == -1 && errno == EINTR) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else + { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + goto retry; + } + } + } + + /* + ** Now to unravel the select sets back into the client's poll + ** descriptor list. Is this possibly an area for pissing away + ** a few cycles or what? + */ + if (ready > 0) + { + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRInt16 out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + PRInt32 osfd; + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + + osfd = bottom->secret->md.osfd; + +#ifdef BSD_SELECT + if (FD_ISSET(osfd, &rd)) +#else + if( IsSocketSet(osfd, socks, 0, rd) ) +#endif + { + if (pd->out_flags & _PR_POLL_READ_SYS_READ) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) { + out_flags |= PR_POLL_WRITE; + } + } + +#ifdef BSD_SELECT + if (FD_ISSET(osfd, &wt)) +#else + if( IsSocketSet(osfd, socks, rd, wt) ) +#endif + { + if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) { + out_flags |= PR_POLL_WRITE; + } + } + +#ifdef BSD_SELECT + if (FD_ISSET(osfd, &ex)) +#else + if( IsSocketSet(osfd, socks, rd+wt, ex) ) +#endif + { + out_flags |= PR_POLL_EXCEPT; + } + } + pd->out_flags = out_flags; + if (out_flags) { + ready++; + } + } + PR_ASSERT(ready > 0); + } + else if (ready < 0) + { + err = _MD_ERRNO(); + if (err == EBADF) + { + /* Find the bad fds */ + int optval; + int optlen = sizeof(optval); + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + pd->out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET, + SO_TYPE, (char *) &optval, &optlen) == -1) + { + PR_ASSERT(sock_errno() == ENOTSOCK); + if (sock_errno() == ENOTSOCK) + { + pd->out_flags = PR_POLL_NVAL; + ready++; + } + } + } + } + PR_ASSERT(ready > 0); + } + else { + _PR_MD_MAP_SELECT_ERROR(err); + } + } + +#ifndef BSD_SELECT + PR_Free(socks); +#endif + return ready; +} + diff --git a/nsprpub/pr/src/md/os2/os2rng.c b/nsprpub/pr/src/md/os2/os2rng.c new file mode 100644 index 0000000000..aaa9693817 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2rng.c @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#define INCL_DOS +#define INCL_DOSERRORS +#include <os2.h> +#include <stdlib.h> +#include <time.h> +#include "primpl.h" + +static BOOL clockTickTime(unsigned long *phigh, unsigned long *plow) +{ + APIRET rc = NO_ERROR; + QWORD qword = {0,0}; + + rc = DosTmrQueryTime(&qword); + if (rc != NO_ERROR) { + return FALSE; + } + + *phigh = qword.ulHi; + *plow = qword.ulLo; + + return TRUE; +} + +extern PRSize _PR_MD_GetRandomNoise(void *buf, PRSize size ) +{ + unsigned long high = 0; + unsigned long low = 0; + clock_t val = 0; + int n = 0; + int nBytes = 0; + time_t sTime; + + if (size <= 0) { + return 0; + } + + clockTickTime(&high, &low); + + /* get the maximally changing bits first */ + nBytes = sizeof(low) > size ? size : sizeof(low); + memcpy(buf, &low, nBytes); + n += nBytes; + size -= nBytes; + + if (size <= 0) { + return n; + } + + nBytes = sizeof(high) > size ? size : sizeof(high); + memcpy(((char *)buf) + n, &high, nBytes); + n += nBytes; + size -= nBytes; + + if (size <= 0) { + return n; + } + + /* get the number of milliseconds that have elapsed since application started */ + val = clock(); + + nBytes = sizeof(val) > size ? size : sizeof(val); + memcpy(((char *)buf) + n, &val, nBytes); + n += nBytes; + size -= nBytes; + + if (size <= 0) { + return n; + } + + /* get the time in seconds since midnight Jan 1, 1970 */ + time(&sTime); + nBytes = sizeof(sTime) > size ? size : sizeof(sTime); + memcpy(((char *)buf) + n, &sTime, nBytes); + n += nBytes; + + return n; +} diff --git a/nsprpub/pr/src/md/os2/os2sem.c b/nsprpub/pr/src/md/os2/os2sem.c new file mode 100644 index 0000000000..a0dd88e4bd --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2sem.c @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * OS/2-specific semaphore handling code. + * + */ + +#include "primpl.h" + + +void +_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value) +{ + int rv; + + /* Our Sems don't support a value > 1 */ + PR_ASSERT(value <= 1); + + rv = DosCreateEventSem(NULL, &md->sem, 0, 0); + PR_ASSERT(rv == NO_ERROR); +} + +void +_PR_MD_DESTROY_SEM(_MDSemaphore *md) +{ + int rv; + rv = DosCloseEventSem(md->sem); + PR_ASSERT(rv == NO_ERROR); + +} + +PRStatus +_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks) +{ + int rv; + rv = DosWaitEventSem(md->sem, PR_IntervalToMilliseconds(ticks)); + + if (rv == NO_ERROR) { + return PR_SUCCESS; + } + else { + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_WAIT_SEM(_MDSemaphore *md) +{ + return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT); +} + +void +_PR_MD_POST_SEM(_MDSemaphore *md) +{ + int rv; + rv = DosPostEventSem(md->sem); + PR_ASSERT(rv == NO_ERROR); +} + + diff --git a/nsprpub/pr/src/md/os2/os2sock.c b/nsprpub/pr/src/md/os2/os2sock.c new file mode 100644 index 0000000000..c6b3ea3a52 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2sock.c @@ -0,0 +1,669 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* OS/2 Sockets module + * + */ + +/*Note from DSR111297 - it should be noted that there are two flavors of select() on OS/2 */ +/*There is standard BSD (which is kind of slow) and a new flavor of select() that takes */ +/*an integer list of sockets, the number of read sockets, write sockets, except sockets, and */ +/*a millisecond count for timeout. In the interest of performance I have choosen the OS/2 */ +/*specific version of select(). See OS/2 TCP/IP Programmer's Toolkit for more info. */ + +#include "primpl.h" + +#include <sys/time.h> /* For timeval. */ + +#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5 +#define READ_FD 1 +#define WRITE_FD 2 + +/* --- SOCKET IO --------------------------------------------------------- */ + + +PRInt32 +_PR_MD_SOCKET(int domain, int type, int flags) +{ + PRInt32 osfd, err; + + osfd = socket(domain, type, flags); + + if (osfd == -1) + { + err = sock_errno(); + _PR_MD_MAP_SOCKET_ERROR(err); + } + + return(osfd); +} + +/* +** _MD_CloseSocket() -- Close a socket +** +*/ +PRInt32 +_MD_CloseSocket(PRInt32 osfd) +{ + PRInt32 rv, err; + + rv = soclose(osfd); + if (rv == -1) { + err = sock_errno(); + _PR_MD_MAP_CLOSE_ERROR(err); + } + return rv; +} + +PRInt32 +_MD_SocketAvailable(PRFileDesc *fd) +{ + PRInt32 result; + + if (so_ioctl(fd->secret->md.osfd, FIONREAD, (char *) &result, sizeof(result)) < 0) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, sock_errno()); + return -1; + } + return result; +} + +static PRInt32 +socket_io_wait( PRInt32 osfd, PRInt32 fd_type, PRIntervalTime timeout ) +{ + PRInt32 rv = -1; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRInt32 syserror; +#ifdef BSD_SELECT + struct timeval tv; + fd_set rd_wr; +#else + int socks[1]; + long lTimeout; +#endif + + switch (timeout) { + case PR_INTERVAL_NO_WAIT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_INTERVAL_NO_TIMEOUT: + /* + * This is a special case of the 'default' case below. + * Please see the comments there. + */ +#ifdef BSD_SELECT + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + FD_ZERO(&rd_wr); + do { + FD_SET(osfd, &rd_wr); + if (fd_type == READ_FD) { + rv = bsdselect(osfd + 1, &rd_wr, NULL, NULL, &tv); + } + else { + rv = bsdselect(osfd + 1, NULL, &rd_wr, NULL, &tv); + } +#else + lTimeout = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000; + do { + socks[0] = osfd; + if (fd_type == READ_FD) { + rv = os2_select(socks, 1, 0, 0, lTimeout); + } + else { + rv = os2_select(socks, 0, 1, 0, lTimeout); + } +#endif + if (rv == -1 && (syserror = sock_errno()) != EINTR) { + _PR_MD_MAP_SELECT_ERROR(syserror); + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = timeout; +#ifdef BSD_SELECT + FD_ZERO(&rd_wr); +#endif + do { + /* + * We block in select for at most + * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, + * so that there is an upper limit on the delay + * before the interrupt bit is checked. + */ +#ifdef BSD_SELECT + wait_for_remaining = PR_TRUE; + tv.tv_sec = PR_IntervalToSeconds(remaining); + if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) { + wait_for_remaining = PR_FALSE; + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + } else { + tv.tv_usec = PR_IntervalToMicroseconds( + remaining - + PR_SecondsToInterval(tv.tv_sec)); + } + FD_SET(osfd, &rd_wr); + if (fd_type == READ_FD) { + rv = bsdselect(osfd + 1, &rd_wr, NULL, NULL, &tv); + } + else { + rv = bsdselect(osfd + 1, NULL, &rd_wr, NULL, &tv); + } +#else + wait_for_remaining = PR_TRUE; + lTimeout = PR_IntervalToMilliseconds(remaining); + if (lTimeout > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) { + wait_for_remaining = PR_FALSE; + lTimeout = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000; + } + socks[0] = osfd; + if (fd_type == READ_FD) { + rv = os2_select(socks, 1, 0, 0, lTimeout); + } + else { + rv = os2_select(socks, 0, 1, 0, lTimeout); + } +#endif + /* + * we don't consider EINTR a real error + */ + if (rv == -1 && (syserror = sock_errno()) != EINTR) { + _PR_MD_MAP_SELECT_ERROR(syserror); + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + /* + * We loop again if select timed out or got interrupted + * by a signal, and the timeout deadline has not passed yet. + */ + if (rv == 0 || (rv == -1 && syserror == EINTR)) { + /* + * If select timed out, we know how much time + * we spent in blocking, so we can avoid a + * PR_IntervalNow() call. + */ + if (rv == 0) { + if (wait_for_remaining) { + now += remaining; + } else { +#ifdef BSD_SELECT + now += PR_SecondsToInterval(tv.tv_sec) + + PR_MicrosecondsToInterval(tv.tv_usec); +#else + now += PR_MillisecondsToInterval(lTimeout); +#endif + } + } else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= timeout) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } else { + remaining = timeout - elapsed; + } + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + } + return(rv); +} + +PRInt32 +_MD_Accept(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = accept(osfd, (struct sockaddr*) addr, (int*)addrlen)) == -1) + { + err = sock_errno(); + if ((err == EWOULDBLOCK) || (err == ECONNABORTED)) + { + if (fd->secret->nonblocking) { + break; + } + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_ACCEPT_ERROR(err); + } +done: + return(rv); +} + +PRInt32 +_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 osfd = fd->secret->md.osfd; + PRNetAddr addrCopy = *addr; /* Work around a bug in OS/2 where connect + * modifies the sockaddr structure. + * See Bugzilla bug 100776. */ + + /* + * We initiate the connection setup by making a nonblocking connect() + * call. If the connect() call fails, there are two cases we handle + * specially: + * 1. The connect() call was interrupted by a signal. In this case + * we simply retry connect(). + * 2. The NSPR socket is nonblocking and connect() fails with + * EINPROGRESS. We first wait until the socket becomes writable. + * Then we try to find out whether the connection setup succeeded + * or failed. + */ + +retry: + if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1) + { + err = sock_errno(); + + if (err == EINTR) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + goto retry; + } + + if (!fd->secret->nonblocking && (err == EINPROGRESS)) + { + /* + * socket_io_wait() may return -1 or 1. + */ + + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if (rv == -1) { + return -1; + } + + PR_ASSERT(rv == 1); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + err = _MD_os2_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } + return 0; + } + + _PR_MD_MAP_CONNECT_ERROR(err); + } + + return rv; +} /* _MD_connect */ + +PRInt32 +_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) +{ + PRInt32 rv, err; + rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen); + if (rv < 0) { + err = sock_errno(); + _PR_MD_MAP_BIND_ERROR(err); + } + return(rv); +} + + +PRInt32 +_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 rv, err; + rv = listen(fd->secret->md.osfd, backlog); + if (rv < 0) { + err = sock_errno(); + _PR_MD_MAP_DEFAULT_ERROR(err); + } + return(rv); +} + + +PRInt32 +_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = recv(osfd,buf,amount,flags)) == -1) + { + err = sock_errno(); + if ((err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_RECV_ERROR(err); + } +done: + return(rv); +} + +PRInt32 +_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = send(osfd,buf,amount,flags)) == -1) + { + err = sock_errno(); + if ((err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) { + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + + /* + * optimization; if bytes sent is less than "amount" call + * select before returning. This is because it is likely that + * the next send() call will return EWOULDBLOCK. + */ + if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) + && (timeout != PR_INTERVAL_NO_WAIT)) + { + if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) { + rv = -1; + goto done; + } + } + if (rv < 0) { + _PR_MD_MAP_SEND_ERROR(err); + } +done: + return(rv); +} + +PRInt32 +_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + while ((rv = sendto(osfd, buf, amount, flags, + (struct sockaddr *) addr, addrlen)) == -1) + { + err = sock_errno(); + if ((err == EWOULDBLOCK)) + { + if (fd->secret->nonblocking) { + break; + } + if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) { + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_SENDTO_ERROR(err); + } +done: + return(rv); +} + +PRInt32 +_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while( (*addrlen = PR_NETADDR_SIZE(addr)), + ((rv = recvfrom(osfd, buf, amount, flags, + (struct sockaddr *) addr, (int *)addrlen)) == -1)) + { + err = sock_errno(); + if ((err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_RECVFROM_ERROR(err); + } +done: + return(rv); +} + +PRInt32 +_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, + PRIntervalTime timeout) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 index, amount = 0; + PRInt32 osfd = fd->secret->md.osfd; + struct iovec osiov[PR_MAX_IOVECTOR_SIZE]; + + /* Ensured by PR_Writev */ + PR_ASSERT(iov_size <= PR_MAX_IOVECTOR_SIZE); + + /* + * We can't pass iov to so_writev because PRIOVec and struct iovec + * may not be binary compatible. Make osiov a copy of iov and + * pass osiov to so_writev . + */ + for (index = 0; index < iov_size; index++) { + osiov[index].iov_base = iov[index].iov_base; + osiov[index].iov_len = iov[index].iov_len; + } + + /* + * Calculate the total number of bytes to be sent; needed for + * optimization later. + * We could avoid this if this number was passed in; but it is + * probably not a big deal because iov_size is usually small (less than + * 3) + */ + if (!fd->secret->nonblocking) { + for (index=0; index<iov_size; index++) { + amount += iov[index].iov_len; + } + } + + while ((rv = so_writev(osfd, osiov, iov_size)) == -1) { + err = sock_errno(); + if ((err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) { + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + + /* + * optimization; if bytes sent is less than "amount" call + * select before returning. This is because it is likely that + * the next writev() call will return EWOULDBLOCK. + */ + if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) + && (timeout != PR_INTERVAL_NO_WAIT)) { + if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) { + rv = -1; + goto done; + } + } + if (rv < 0) { + _PR_MD_MAP_WRITEV_ERROR(err); + } +done: + return(rv); +} + +PRInt32 +_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how) +{ + PRInt32 rv; + + rv = shutdown(fd->secret->md.osfd, how); + if (rv < 0) { + _PR_MD_MAP_SHUTDOWN_ERROR(sock_errno()); + } + return rv; +} + +PRInt32 +_PR_MD_SOCKETPAIR(int af, int type, int flags, PRInt32 *osfd) +{ + PRInt32 rv, err; + + rv = socketpair(af, type, flags, osfd); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SOCKETPAIR_ERROR(err); + } + return rv; +} + +PRStatus +_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) +{ + PRInt32 rv, err; + + rv = getsockname(fd->secret->md.osfd, + (struct sockaddr *) addr, (int *)addrlen); + if (rv < 0) { + err = sock_errno(); + _PR_MD_MAP_GETSOCKNAME_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus +_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) +{ + PRInt32 rv, err; + + rv = getpeername(fd->secret->md.osfd, + (struct sockaddr *) addr, (int *)addrlen); + if (rv < 0) { + err = sock_errno(); + _PR_MD_MAP_GETPEERNAME_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus +_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, + char* optval, PRInt32* optlen) +{ + PRInt32 rv, err; + + rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (int *)optlen); + if (rv < 0) { + err = sock_errno(); + _PR_MD_MAP_GETSOCKOPT_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus +_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, + const char* optval, PRInt32 optlen) +{ + PRInt32 rv, err; + + rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen); + if (rv < 0) { + err = sock_errno(); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +void +_MD_MakeNonblock(PRFileDesc *fd) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 err; + PRUint32 one = 1; + + if (osfd <= 2) { + /* Don't mess around with stdin, stdout or stderr */ + return; + } + + err = so_ioctl( osfd, FIONBIO, (char *) &one, sizeof(one)); + if ( err != 0 ) + { + err = sock_errno(); + _PR_MD_MAP_SOCKET_ERROR(err); + } +} diff --git a/nsprpub/pr/src/md/os2/os2thred.c b/nsprpub/pr/src/md/os2/os2thred.c new file mode 100644 index 0000000000..aa929a1a23 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2thred.c @@ -0,0 +1,353 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include <process.h> /* for _beginthread() */ +#include <signal.h> +#include <float.h> + +/* --- globals ------------------------------------------------ */ +_NSPR_TLS* pThreadLocalStorage = 0; +_PRInterruptTable _pr_interruptTable[] = { { 0 } }; +APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD); + +void +_PR_MD_ENSURE_TLS(void) +{ + if(!pThreadLocalStorage) + { + /* Allocate thread local storage (TLS). Note, that only 32 bytes can + * be allocated at a time. + */ + int rc = DosAllocThreadLocalMemory(sizeof(_NSPR_TLS) / 4, (PULONG*)&pThreadLocalStorage); + PR_ASSERT(rc == NO_ERROR); + memset(pThreadLocalStorage, 0, sizeof(_NSPR_TLS)); + } +} + +void +_PR_MD_EARLY_INIT() +{ + HMODULE hmod; + + if (DosLoadModule(NULL, 0, "DOSCALL1", &hmod) == 0) + DosQueryProcAddr(hmod, 877, "DOSQUERYTHREADCONTEXT", + (PFN *)&QueryThreadContext); +} + +static void +_pr_SetThreadMDHandle(PRThread *thread) +{ + PTIB ptib; + PPIB ppib; + PRUword rc; + + rc = DosGetInfoBlocks(&ptib, &ppib); + + thread->md.handle = ptib->tib_ptib2->tib2_ultid; +} + +/* On OS/2, some system function calls seem to change the FPU control word, + * such that we crash with a floating underflow exception. The FIX_FPU() call + * in jsnum.c does not always work, as sometimes FIX_FPU() is called BEFORE the + * OS/2 system call that horks the FPU control word. So, we set an exception + * handler that covers any floating point exceptions and resets the FPU CW to + * the required value. + */ +static ULONG +_System OS2_FloatExcpHandler(PEXCEPTIONREPORTRECORD p1, + PEXCEPTIONREGISTRATIONRECORD p2, + PCONTEXTRECORD p3, + PVOID pv) +{ +#ifdef DEBUG_pedemonte + printf("Entering exception handler; ExceptionNum = %x\n", p1->ExceptionNum); + switch(p1->ExceptionNum) { + case XCPT_FLOAT_DENORMAL_OPERAND: + printf("got XCPT_FLOAT_DENORMAL_OPERAND\n"); + break; + case XCPT_FLOAT_DIVIDE_BY_ZERO: + printf("got XCPT_FLOAT_DIVIDE_BY_ZERO\n"); + break; + case XCPT_FLOAT_INEXACT_RESULT: + printf("got XCPT_FLOAT_INEXACT_RESULT\n"); + break; + case XCPT_FLOAT_INVALID_OPERATION: + printf("got XCPT_FLOAT_INVALID_OPERATION\n"); + break; + case XCPT_FLOAT_OVERFLOW: + printf("got XCPT_FLOAT_OVERFLOW\n"); + break; + case XCPT_FLOAT_STACK_CHECK: + printf("got XCPT_FLOAT_STACK_CHECK\n"); + break; + case XCPT_FLOAT_UNDERFLOW: + printf("got XCPT_FLOAT_UNDERFLOW\n"); + break; + } +#endif + + switch(p1->ExceptionNum) { + case XCPT_FLOAT_DENORMAL_OPERAND: + case XCPT_FLOAT_DIVIDE_BY_ZERO: + case XCPT_FLOAT_INEXACT_RESULT: + case XCPT_FLOAT_INVALID_OPERATION: + case XCPT_FLOAT_OVERFLOW: + case XCPT_FLOAT_STACK_CHECK: + case XCPT_FLOAT_UNDERFLOW: + { + unsigned cw = p3->ctx_env[0]; + if ((cw & MCW_EM) != MCW_EM) { + /* Mask out all floating point exceptions */ + p3->ctx_env[0] |= MCW_EM; + /* Following two lines set precision to 53 bit mantissa. See jsnum.c */ + p3->ctx_env[0] &= ~MCW_PC; + p3->ctx_env[0] |= PC_53; + return XCPT_CONTINUE_EXECUTION; + } + } + } + return XCPT_CONTINUE_SEARCH; +} + +PR_IMPLEMENT(void) +PR_OS2_SetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg) +{ + /* setup the exception handler for the thread */ + APIRET rv; + excpreg->ExceptionHandler = OS2_FloatExcpHandler; + excpreg->prev_structure = NULL; + rv = DosSetExceptionHandler(excpreg); + PR_ASSERT(rv == NO_ERROR); +} + +PR_IMPLEMENT(void) +PR_OS2_UnsetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg) +{ + /* unset exception handler */ + APIRET rv = DosUnsetExceptionHandler(excpreg); + PR_ASSERT(rv == NO_ERROR); +} + +PRStatus +_PR_MD_INIT_THREAD(PRThread *thread) +{ + APIRET rv; + + if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { + _pr_SetThreadMDHandle(thread); + } + + /* Create the blocking IO semaphore */ + rv = DosCreateEventSem(NULL, &(thread->md.blocked_sema), 0, 0); + return (rv == NO_ERROR) ? PR_SUCCESS : PR_FAILURE; +} + +typedef struct param_store +{ + void (*start)(void *); + PRThread* thread; +} PARAMSTORE; + +/* This is a small intermediate function that sets/unsets the exception + handler before calling the initial thread function */ +static void +ExcpStartFunc(void* arg) +{ + EXCEPTIONREGISTRATIONRECORD excpreg; + PARAMSTORE params, *pParams = arg; + + PR_OS2_SetFloatExcpHandler(&excpreg); + + params = *pParams; + PR_Free(pParams); + params.start(params.thread); + + PR_OS2_UnsetFloatExcpHandler(&excpreg); +} + +PRStatus +_PR_MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PARAMSTORE* params = PR_Malloc(sizeof(PARAMSTORE)); + params->start = start; + params->thread = thread; + thread->md.handle = thread->id = (TID) _beginthread(ExcpStartFunc, + NULL, + thread->stack->stackSize, + params); + if(thread->md.handle == -1) { + return PR_FAILURE; + } + + /* + * On OS/2, a thread is created with a thread priority of + * THREAD_PRIORITY_NORMAL + */ + + if (priority != PR_PRIORITY_NORMAL) { + _PR_MD_SET_PRIORITY(&(thread->md), priority); + } + + return PR_SUCCESS; +} + +void +_PR_MD_YIELD(void) +{ + /* Isn't there some problem with DosSleep(0) on OS/2? */ + DosSleep(0); +} + +void +_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) +{ + int nativePri = PRTYC_NOCHANGE; + BOOL rv; + + if (newPri < PR_PRIORITY_FIRST) { + newPri = PR_PRIORITY_FIRST; + } else if (newPri > PR_PRIORITY_LAST) { + newPri = PR_PRIORITY_LAST; + } + switch (newPri) { + case PR_PRIORITY_LOW: + case PR_PRIORITY_NORMAL: + nativePri = PRTYC_REGULAR; + break; + case PR_PRIORITY_HIGH: + nativePri = PRTYC_FOREGROUNDSERVER; + break; + case PR_PRIORITY_URGENT: + nativePri = PRTYC_TIMECRITICAL; + } + rv = DosSetPriority(PRTYS_THREAD, nativePri, 0, thread->handle); + PR_ASSERT(rv == NO_ERROR); + if (rv != NO_ERROR) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_SetThreadPriority: can't set thread priority\n")); + } + return; +} + +void +_PR_MD_CLEAN_THREAD(PRThread *thread) +{ + APIRET rv; + + if (thread->md.blocked_sema) { + rv = DosCloseEventSem(thread->md.blocked_sema); + PR_ASSERT(rv == NO_ERROR); + thread->md.blocked_sema = 0; + } + + if (thread->md.handle) { + thread->md.handle = 0; + } +} + +void +_PR_MD_EXIT_THREAD(PRThread *thread) +{ + _PR_MD_CLEAN_THREAD(thread); + _PR_MD_SET_CURRENT_THREAD(NULL); +} + + +void +_PR_MD_EXIT(PRIntn status) +{ + _exit(status); +} + +#ifdef HAVE_THREAD_AFFINITY +PR_EXTERN(PRInt32) +_PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) +{ + /* Can we do this on OS/2? Only on SMP versions? */ + PR_NOT_REACHED("Not implemented"); + return 0; + + /* This is what windows does: + int rv; + + rv = SetThreadAffinityMask(thread->md.handle, mask); + + return rv?0:-1; + */ +} + +PR_EXTERN(PRInt32) +_PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) +{ + /* Can we do this on OS/2? Only on SMP versions? */ + PR_NOT_REACHED("Not implemented"); + return 0; + + /* This is what windows does: + PRInt32 rv, system_mask; + + rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask); + + return rv?0:-1; + */ +} +#endif /* HAVE_THREAD_AFFINITY */ + +void +_PR_MD_SUSPEND_CPU(_PRCPU *cpu) +{ + _PR_MD_SUSPEND_THREAD(cpu->thread); +} + +void +_PR_MD_RESUME_CPU(_PRCPU *cpu) +{ + _PR_MD_RESUME_THREAD(cpu->thread); +} + +void +_PR_MD_SUSPEND_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + APIRET rc; + + /* XXXMB - DosSuspendThread() is not a blocking call; how do we + * know when the thread is *REALLY* suspended? + */ + rc = DosSuspendThread(thread->md.handle); + PR_ASSERT(rc == NO_ERROR); + } +} + +void +_PR_MD_RESUME_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + DosResumeThread(thread->md.handle); + } +} + + +PRThread* +_MD_CURRENT_THREAD(void) +{ + PRThread *thread; + + thread = _MD_GET_ATTACHED_THREAD(); + + if (NULL == thread) { + thread = _PRI_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0); + } + + PR_ASSERT(thread != NULL); + return thread; +} + diff --git a/nsprpub/pr/src/md/os2/os2vaclegacy.s b/nsprpub/pr/src/md/os2/os2vaclegacy.s new file mode 100644 index 0000000000..3dc1468752 --- /dev/null +++ b/nsprpub/pr/src/md/os2/os2vaclegacy.s @@ -0,0 +1,45 @@ +/ -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/ +/ This Source Code Form is subject to the terms of the Mozilla Public +/ License, v. 2.0. If a copy of the MPL was not distributed with this +/ file, You can obtain one at http://mozilla.org/MPL/2.0/. + + .text + .align 4 + .globl PR_NewMonitor +PR_NewMonitor: + jmp _PR_NewMonitor + + .align 4 + .globl PR_EnterMonitor +PR_EnterMonitor: + mov %eax, 4(%esp) + jmp _PR_EnterMonitor + + .align 4 + .globl PR_ExitMonitor +PR_ExitMonitor: + mov %eax, 4(%esp) + jmp _PR_ExitMonitor + + + + .align 4 + .globl PR_AttachThread +PR_AttachThread: + mov %eax, 4(%esp) + mov %edx, 8(%esp) + mov %ecx, 12(%esp) + jmp _PR_AttachThread + + .align 4 + .globl PR_DetachThread +PR_DetachThread: + jmp _PR_DetachThread + + .align 4 + .globl PR_GetCurrentThread +PR_GetCurrentThread: + jmp _PR_GetCurrentThread + + diff --git a/nsprpub/pr/src/md/prosdep.c b/nsprpub/pr/src/md/prosdep.c new file mode 100644 index 0000000000..0b1f3e597a --- /dev/null +++ b/nsprpub/pr/src/md/prosdep.c @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prbit.h" +#include "prsystem.h" + +#ifdef XP_UNIX +#include <unistd.h> +#endif +#ifdef _WIN32 +#include <windows.h> +#endif + +PRInt32 _pr_pageShift; +PRInt32 _pr_pageSize; + +/* +** Get system page size +*/ +static void GetPageSize(void) +{ + PRInt32 pageSize; + + /* Get page size */ +#ifdef XP_UNIX +#if defined BSDI || defined AIX \ + || defined LINUX || defined __GNU__ || defined __GLIBC__ \ + || defined FREEBSD || defined NETBSD || defined OPENBSD \ + || defined DARWIN + _pr_pageSize = getpagesize(); +#elif defined(HPUX) + /* I have no idea. Don't get me started. --Rob */ + _pr_pageSize = sysconf(_SC_PAGE_SIZE); +#else + _pr_pageSize = sysconf(_SC_PAGESIZE); +#endif +#endif /* XP_UNIX */ + +#ifdef XP_PC +#ifdef _WIN32 + SYSTEM_INFO info; + GetSystemInfo(&info); + _pr_pageSize = info.dwPageSize; +#else + _pr_pageSize = 4096; +#endif +#endif /* XP_PC */ + + pageSize = _pr_pageSize; + PR_CEILING_LOG2(_pr_pageShift, pageSize); +} + +PR_IMPLEMENT(PRInt32) PR_GetPageShift(void) +{ + if (!_pr_pageSize) { + GetPageSize(); + } + return _pr_pageShift; +} + +PR_IMPLEMENT(PRInt32) PR_GetPageSize(void) +{ + if (!_pr_pageSize) { + GetPageSize(); + } + return _pr_pageSize; +} diff --git a/nsprpub/pr/src/md/unix/Makefile.in b/nsprpub/pr/src/md/unix/Makefile.in new file mode 100644 index 0000000000..f241840f8b --- /dev/null +++ b/nsprpub/pr/src/md/unix/Makefile.in @@ -0,0 +1,99 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +MOD_DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = \ + unix.c \ + unix_errors.c \ + uxproces.c \ + uxrng.c \ + uxshm.c \ + uxwrap.c \ + $(NULL) + +ifneq ($(USE_PTHREADS),1) +CSRCS += uxpoll.c +endif + +ifeq ($(PTHREADS_USER),1) +CSRCS += pthreads_user.c +endif + +CSRCS += $(PR_MD_CSRCS) +ASFILES += $(PR_MD_ASFILES) + +TARGETS = $(OBJS) + +ifeq ($(OS_ARCH),SunOS) + ifeq ($(CPU_ARCH),sparc) + ifdef USE_64 + ULTRASPARC_ASFILES = os_SunOS_sparcv9.s + ULTRASPARC_ASOBJS = $(addprefix $(OBJDIR)/,$(ULTRASPARC_ASFILES:.s=.$(OBJ_SUFFIX))) + else + LIBRARY_NAME = $(ULTRASPARC_LIBRARY) + LIBRARY_VERSION = $(MOD_MAJOR_VERSION) + ULTRASPARC_ASFILES = os_SunOS_ultrasparc.s + ULTRASPARC_ASOBJS = $(addprefix $(OBJDIR)/,$(ULTRASPARC_ASFILES:.s=.$(OBJ_SUFFIX))) + TARGETS += $(ULTRASPARC_ASOBJS) $(SHARED_LIBRARY) + RELEASE_LIBS = $(SHARED_LIBRARY) + RELEASE_LIBS_DEST = $(RELEASE_LIB_DIR)/cpu/sparcv8plus + lib_subdir = cpu/sparcv8plus + endif + endif +endif + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + +ifeq ($(OS_ARCH),SunOS) +ifeq ($(CPU_ARCH),sparc) + +ifdef USE_64 +$(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES) + /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v9 $< +else +$(SHARED_LIBRARY): $(ULTRASPARC_ASOBJS) + $(LD) -G -z text -z endfiltee -o $@ $(ULTRASPARC_ASOBJS) + $(INSTALL) -m 444 $@ $(dist_libdir)/cpu/sparcv8plus + $(INSTALL) -m 444 $@ $(dist_bindir)/cpu/sparcv8plus +# +# The -f $(ORIGIN)/... linker flag uses the real file, after symbolic links +# are resolved, as the origin. If NSDISTMODE is not "copy", libnspr4.so +# will be installed as a symbolic link in $(dist_libdir), pointing to the +# real libnspr4.so file in pr/src. Therefore we need to install an +# additional copy of libnspr_flt4.so in pr/src/cpu/sparcv8plus. +# +ifneq ($(NSDISTMODE),copy) + $(INSTALL) -m 444 $@ ../../cpu/sparcv8plus +endif + +ifneq ($(NSDISTMODE),copy) +clobber realclean clobber_all distclean:: + rm -rf ../../cpu +endif + +$(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES) + /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v8plus $< + +clean:: + rm -rf $(ULTRASPARC_ASOBJS) +endif + +endif +endif diff --git a/nsprpub/pr/src/md/unix/aix.c b/nsprpub/pr/src/md/unix/aix.c new file mode 100644 index 0000000000..c632c5790e --- /dev/null +++ b/nsprpub/pr/src/md/unix/aix.c @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#ifdef AIX_HAVE_ATOMIC_OP_H +#include <sys/atomic_op.h> + +PRInt32 _AIX_AtomicSet(PRInt32 *val, PRInt32 newval) +{ + PRIntn oldval; + boolean_t stored; + oldval = fetch_and_add((atomic_p)val, 0); + do + { + stored = compare_and_swap((atomic_p)val, &oldval, newval); + } while (!stored); + return oldval; +} /* _AIX_AtomicSet */ +#endif /* AIX_HAVE_ATOMIC_OP_H */ + +#if defined(AIX_TIMERS) + +#include <sys/time.h> + +static PRUint32 _aix_baseline_epoch; + +static void _MD_AixIntervalInit(void) +{ + timebasestruct_t real_time; + read_real_time(&real_time, TIMEBASE_SZ); + (void)time_base_to_time(&real_time, TIMEBASE_SZ); + _aix_baseline_epoch = real_time.tb_high; +} /* _MD_AixIntervalInit */ + +PRIntervalTime _MD_AixGetInterval(void) +{ + PRIntn rv; + PRUint64 temp; + timebasestruct_t real_time; + read_real_time(&real_time, TIMEBASE_SZ); + (void)time_base_to_time(&real_time, TIMEBASE_SZ); + /* tb_high is in seconds, tb_low in 10(-9)seconds */ + temp = 1000000000ULL * (PRUint64)(real_time.tb_high - _aix_baseline_epoch); + temp += (PRUint64)real_time.tb_low; /* everything's 10(-9) seconds */ + temp >>= 16; /* now it's something way different */ + return (PRIntervalTime)temp; +} /* _MD_AixGetInterval */ + +PRIntervalTime _MD_AixIntervalPerSec(void) +{ + return 1000000000ULL >> 16; /* that's 15258, I think */ +} /* _MD_AixIntervalPerSec */ + +#endif /* defined(AIX_TIMERS) */ + +#if !defined(PTHREADS_USER) + +#if defined(_PR_PTHREADS) + +/* + * AIX 4.3 has sched_yield(). AIX 4.2 has pthread_yield(). + * So we look up the appropriate function pointer at run time. + */ + +#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); + + _PT_aix_yield_fcn = (int(*)())dlsym(main_app_handle, "sched_yield"); + if (!_PT_aix_yield_fcn) { + _PT_aix_yield_fcn = (int(*)())dlsym(main_app_handle,"pthread_yield"); + PR_ASSERT(NULL != _PT_aix_yield_fcn); + } + 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 +} + +#else /* _PR_PTHREADS */ + +void _MD_EarlyInit(void) +{ +#if defined(AIX_TIMERS) + _MD_AixIntervalInit(); +#endif +} + +#endif /* _PR_PTHREADS */ + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +PR_IMPLEMENT(void) +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PR_IMPLEMENT(PRStatus) +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for AIX */ +PR_IMPLEMENT(void) +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for AIX."); +} + +PR_IMPLEMENT(PRStatus) +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for AIX."); +} +#endif /* _PR_PTHREADS */ +#endif /* PTHREADS_USER */ + +/* + * NSPR 2.0 overrides the system select() and poll() functions. + * On AIX 4.2, we use dlopen("/unix", RTLD_NOW) and dlsym() to get + * at the original system select() and poll() functions. + */ + +#if !defined(AIX_RENAME_SELECT) + +#include <sys/select.h> +#include <sys/poll.h> +#include <dlfcn.h> + +static int (*aix_select_fcn)() = NULL; +static int (*aix_poll_fcn)() = NULL; + +int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) +{ + int rv; + + if (!aix_select_fcn) { + void *aix_handle; + + aix_handle = dlopen("/unix", RTLD_NOW); + if (!aix_handle) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + aix_select_fcn = (int(*)())dlsym(aix_handle,"select"); + dlclose(aix_handle); + if (!aix_select_fcn) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + } + rv = (*aix_select_fcn)(width, r, w, e, t); + return rv; +} + +int _MD_POLL(void *listptr, unsigned long nfds, long timeout) +{ + int rv; + + if (!aix_poll_fcn) { + void *aix_handle; + + aix_handle = dlopen("/unix", RTLD_NOW); + if (!aix_handle) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + aix_poll_fcn = (int(*)())dlsym(aix_handle,"poll"); + dlclose(aix_handle); + if (!aix_poll_fcn) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + } + rv = (*aix_poll_fcn)(listptr, nfds, timeout); + return rv; +} + +#else + +/* + * In AIX versions prior to 4.2, we use the two-step rename/link trick. + * The binary must contain at least one "poll" symbol for linker's rename + * to work. So we must have this dummy function that references poll(). + */ +#include <sys/poll.h> +void _pr_aix_dummy() +{ + poll(0,0,0); +} + +#endif /* !defined(AIX_RENAME_SELECT) */ + +#ifdef _PR_HAVE_ATOMIC_CAS + +#include "pratom.h" + +#define _PR_AIX_ATOMIC_LOCK -1 + +PR_IMPLEMENT(void) +PR_StackPush(PRStack *stack, PRStackElem *stack_elem) +{ + PRStackElem *addr; + boolean_t locked = TRUE; + + /* Is it safe to cast a pointer to an int? */ + PR_ASSERT(sizeof(int) == sizeof(PRStackElem *)); + do { + while ((addr = stack->prstk_head.prstk_elem_next) == + (PRStackElem *)_PR_AIX_ATOMIC_LOCK) + ; + locked = _check_lock((atomic_p) &stack->prstk_head.prstk_elem_next, + (int) addr, _PR_AIX_ATOMIC_LOCK); + } while (locked == TRUE); + stack_elem->prstk_elem_next = addr; + _clear_lock((atomic_p)&stack->prstk_head.prstk_elem_next, (int)stack_elem); + return; +} + +PR_IMPLEMENT(PRStackElem *) +PR_StackPop(PRStack *stack) +{ + PRStackElem *element; + boolean_t locked = TRUE; + + /* Is it safe to cast a pointer to an int? */ + PR_ASSERT(sizeof(int) == sizeof(PRStackElem *)); + do { + while ((element = stack->prstk_head.prstk_elem_next) == + (PRStackElem *) _PR_AIX_ATOMIC_LOCK) + ; + locked = _check_lock((atomic_p) &stack->prstk_head.prstk_elem_next, + (int)element, _PR_AIX_ATOMIC_LOCK); + } while (locked == TRUE); + + if (element == NULL) { + _clear_lock((atomic_p) &stack->prstk_head.prstk_elem_next, NULL); + } else { + _clear_lock((atomic_p) &stack->prstk_head.prstk_elem_next, + (int) element->prstk_elem_next); + } + return element; +} + +#endif /* _PR_HAVE_ATOMIC_CAS */ diff --git a/nsprpub/pr/src/md/unix/aixwrap.c b/nsprpub/pr/src/md/unix/aixwrap.c new file mode 100644 index 0000000000..853825196b --- /dev/null +++ b/nsprpub/pr/src/md/unix/aixwrap.c @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: aixwrap.c + * Description: + * This file contains a single function, _MD_SELECT(), which simply + * invokes the select() function. This file is used in an ugly + * hack to override the system select() function on AIX releases + * prior to 4.2. (On AIX 4.2, we use a different mechanism to + * override select().) + */ + +#ifndef AIX_RENAME_SELECT +#error aixwrap.c should only be used on AIX 3.2 or 4.1 +#else + +#include <sys/select.h> +#include <sys/poll.h> + +int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) +{ + return select(width, r, w, e, t); +} + +int _MD_POLL(void *listptr, unsigned long nfds, long timeout) +{ + return poll(listptr, nfds, timeout); +} + +#endif /* AIX_RENAME_SELECT */ diff --git a/nsprpub/pr/src/md/unix/bsdi.c b/nsprpub/pr/src/md/unix/bsdi.c new file mode 100644 index 0000000000..e625003f73 --- /dev/null +++ b/nsprpub/pr/src/md/unix/bsdi.c @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <signal.h> + +void _MD_EarlyInit(void) +{ + /* + * Ignore FPE because coercion of a NaN to an int causes SIGFPE + * to be raised. + */ + struct sigaction act; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGFPE, &act, 0); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for BSDI */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for BSDI."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for BSDI."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/darwin.c b/nsprpub/pr/src/md/unix/darwin.c new file mode 100644 index 0000000000..6135e082ff --- /dev/null +++ b/nsprpub/pr/src/md/unix/darwin.c @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <mach/mach_time.h> + +void _MD_EarlyInit(void) +{ +} + +/* + * The multiplier (as a fraction) for converting the Mach absolute time + * unit to nanoseconds. + */ +static mach_timebase_info_data_t machTimebaseInfo; + +void _PR_Mach_IntervalInit(void) +{ + kern_return_t rv; + + rv = mach_timebase_info(&machTimebaseInfo); + PR_ASSERT(rv == KERN_SUCCESS); +} + +PRIntervalTime _PR_Mach_GetInterval(void) +{ + uint64_t time; + + /* + * mach_absolute_time returns the time in the Mach absolute time unit. + * Convert it to milliseconds. See Mac Technical Q&A QA1398. + */ + time = mach_absolute_time(); + time = time * machTimebaseInfo.numer / machTimebaseInfo.denom / + PR_NSEC_PER_MSEC; + return (PRIntervalTime)time; +} /* _PR_Mach_GetInterval */ + +PRIntervalTime _PR_Mach_TicksPerSecond(void) +{ + return 1000; +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#if !defined(_PR_PTHREADS) + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#if !defined(_PR_PTHREADS) +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Darwin */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Darwin."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Darwin."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ + +/* darwin.c */ + diff --git a/nsprpub/pr/src/md/unix/dgux.c b/nsprpub/pr/src/md/unix/dgux.c new file mode 100644 index 0000000000..a1b3602c5a --- /dev/null +++ b/nsprpub/pr/src/md/unix/dgux.c @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/* + * using only NSPR threads here + * + * Copied from the UnixWare implementation. Should be kept in sync + * with ../../../include/md/_dgux.h. + */ + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for DG/UX */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for DG/UX."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for DG/UX."); +} + diff --git a/nsprpub/pr/src/md/unix/freebsd.c b/nsprpub/pr/src/md/unix/freebsd.c new file mode 100644 index 0000000000..19af17a440 --- /dev/null +++ b/nsprpub/pr/src/md/unix/freebsd.c @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <signal.h> + +void _MD_EarlyInit(void) +{ + /* + * Ignore FPE because coercion of a NaN to an int causes SIGFPE + * to be raised. + */ + struct sigaction act; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGFPE, &act, 0); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) sigsetjmp(CONTEXT(t), 1); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for FreeBSD */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for FreeBSD."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for FreeBSD."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/hpux.c b/nsprpub/pr/src/md/unix/hpux.c new file mode 100644 index 0000000000..bf72567fd5 --- /dev/null +++ b/nsprpub/pr/src/md/unix/hpux.c @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include <setjmp.h> + +#if defined(HPUX_LW_TIMER) + +#include <machine/inline.h> +#include <machine/clock.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/pstat.h> + +int __lw_get_thread_times(int which, int64_t *sample, int64_t *time); + +static double msecond_per_itick; + +void _PR_HPUX_LW_IntervalInit(void) +{ + struct pst_processor psp; + int iticksperclktick, clk_tck; + int rv; + + rv = pstat_getprocessor(&psp, sizeof(psp), 1, 0); + PR_ASSERT(rv != -1); + + iticksperclktick = psp.psp_iticksperclktick; + clk_tck = sysconf(_SC_CLK_TCK); + msecond_per_itick = (1000.0)/(double)(iticksperclktick * clk_tck); +} + +PRIntervalTime _PR_HPUX_LW_GetInterval(void) +{ + int64_t time, sample; + + __lw_get_thread_times(1, &sample, &time); + /* + * Division is slower than float multiplication. + * return (time / iticks_per_msecond); + */ + return (time * msecond_per_itick); +} +#endif /* HPUX_LW_TIMER */ + +#if !defined(PTHREADS_USER) + +void _MD_EarlyInit(void) +{ +#ifndef _PR_PTHREADS + /* + * The following piece of code is taken from ns/nspr/src/md_HP-UX.c. + * In the comment for revision 1.6, dated 1995/09/11 23:33:34, + * robm says: + * This version has some problems which need to be addressed. + * First, intercept all system calls and prevent them from + * executing the library code which performs stack switches + * before normal system call invocation. In order for library + * calls which make system calls to work (like stdio), however, + * we must also allocate our own stack and switch the primordial + * stack to use it. This isn't so bad, except that I fudged the + * backtrace length when copying the old stack to the new one. + * + * This is the original comment of robm in the code: + * XXXrobm Horrific. To avoid a problem with HP's system call + * code, we allocate a new stack for the primordial thread and + * use it. However, we don't know how far back the original stack + * goes. We should create a routine that performs a backtrace and + * finds out just how much we need to copy. As a temporary measure, + * I just copy an arbitrary guess. + * + * In an email to servereng dated 2 Jan 1997, Mike Patnode (mikep) + * suggests that this only needs to be done for HP-UX 9. + */ +#ifdef HPUX9 +#define PIDOOMA_STACK_SIZE 524288 +#define BACKTRACE_SIZE 8192 + { + jmp_buf jb; + char *newstack; + char *oldstack; + + if(!setjmp(jb)) { + newstack = (char *) PR_MALLOC(PIDOOMA_STACK_SIZE); + oldstack = (char *) (*(((int *) jb) + 1) - BACKTRACE_SIZE); + memcpy(newstack, oldstack, BACKTRACE_SIZE); + *(((int *) jb) + 1) = (int) (newstack + BACKTRACE_SIZE); + longjmp(jb, 1); + } + } +#endif /* HPUX9 */ +#endif /* !_PR_PTHREADS */ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for HP-UX */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for HP-UX."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for HP-UX."); +} +#endif /* _PR_PTHREADS */ + +void +_MD_suspend_thread(PRThread *thread) +{ +#ifdef _PR_PTHREADS +#endif +} + +void +_MD_resume_thread(PRThread *thread) +{ +#ifdef _PR_PTHREADS +#endif +} +#endif /* PTHREADS_USER */ + +/* + * The HP version of strchr is buggy. It looks past the end of the + * string and causes a segmentation fault when our (NSPR) version + * of malloc is used. + * + * A better solution might be to put a cushion in our malloc just in + * case HP's version of strchr somehow gets used instead of this one. + */ +char * +strchr(const char *s, int c) +{ + char ch; + + if (!s) { + return NULL; + } + + ch = (char) c; + + while ((*s) && ((*s) != ch)) { + s++; + } + + if ((*s) == ch) { + return (char *) s; + } + + return NULL; +} + +/* + * Implemementation of memcmp in HP-UX (verified on releases A.09.03, + * A.09.07, and B.10.10) dumps core if called with: + * 1. First operand with address = 1(mod 4). + * 2. Size = 1(mod 4) + * 3. Last byte of the second operand is the last byte of the page and + * next page is not accessible(not mapped or protected) + * Thus, using the following naive version (tons of optimizations are + * possible;^) + */ + +int memcmp(const void *s1, const void *s2, size_t n) +{ + register unsigned char *p1 = (unsigned char *) s1, + *p2 = (unsigned char *) s2; + + while (n-- > 0) { + register int r = ((int) ((unsigned int) *p1)) + - ((int) ((unsigned int) *p2)); + if (r) { + return r; + } + p1++; p2++; + } + return 0; +} diff --git a/nsprpub/pr/src/md/unix/linux.c b/nsprpub/pr/src/md/unix/linux.c new file mode 100644 index 0000000000..6bfc7a8024 --- /dev/null +++ b/nsprpub/pr/src/md/unix/linux.c @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifdef _PR_PTHREADS + +extern void _MD_unix_terminate_waitpid_daemon(void); + +void _MD_CleanupBeforeExit(void) +{ + _MD_unix_terminate_waitpid_daemon(); +} + +#else /* ! _PR_PTHREADS */ + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + /* + * set the pointers to the stack-pointer and frame-pointer words in the + * context structure; this is for debugging use. + */ + thread->md.sp = _MD_GET_SP_PTR(thread); + thread->md.fp = _MD_GET_FP_PTR(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Linux */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Linux."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Linux."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/netbsd.c b/nsprpub/pr/src/md/unix/netbsd.c new file mode 100644 index 0000000000..aa3618a31a --- /dev/null +++ b/nsprpub/pr/src/md/unix/netbsd.c @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <signal.h> +#include <poll.h> +#include <sys/syscall.h> + +void _MD_EarlyInit(void) +{ + /* + * Ignore FPE because coercion of a NaN to an int causes SIGFPE + * to be raised. + */ + struct sigaction act; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGFPE, &act, 0); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) sigsetjmp(CONTEXT(t), 1); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for NetBSD */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for NetBSD."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for NetBSD."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/nto.c b/nsprpub/pr/src/md/unix/nto.c new file mode 100644 index 0000000000..8ab8b1eab8 --- /dev/null +++ b/nsprpub/pr/src/md/unix/nto.c @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <setjmp.h> + +/* Fake this out */ +int socketpair (int foo, int foo2, int foo3, int sv[2]) +{ + printf("error in socketpair\n"); + exit (-1); +} + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} diff --git a/nsprpub/pr/src/md/unix/objs.mk b/nsprpub/pr/src/md/unix/objs.mk new file mode 100644 index 0000000000..77eaa6d13b --- /dev/null +++ b/nsprpub/pr/src/md/unix/objs.mk @@ -0,0 +1,31 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This makefile appends to the variable OBJS the platform-dependent +# object modules that will be part of the nspr20 library. + +CSRCS = \ + unix.c \ + unix_errors.c \ + uxproces.c \ + uxrng.c \ + uxshm.c \ + uxwrap.c \ + $(NULL) + +ifneq ($(USE_PTHREADS),1) +CSRCS += uxpoll.c +endif + +ifeq ($(PTHREADS_USER),1) +CSRCS += pthreads_user.c +endif + +CSRCS += $(PR_MD_CSRCS) +ASFILES += $(PR_MD_ASFILES) + +OBJS += $(addprefix md/unix/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \ + $(addprefix md/unix/$(OBJDIR)/,$(ASFILES:.s=.$(OBJ_SUFFIX))) + diff --git a/nsprpub/pr/src/md/unix/openbsd.c b/nsprpub/pr/src/md/unix/openbsd.c new file mode 100644 index 0000000000..b7f0a29954 --- /dev/null +++ b/nsprpub/pr/src/md/unix/openbsd.c @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <signal.h> +#include <poll.h> +#include <sys/syscall.h> + +void _MD_EarlyInit(void) +{ + /* + * Ignore FPE because coercion of a NaN to an int causes SIGFPE + * to be raised. + */ + struct sigaction act; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGFPE, &act, 0); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) sigsetjmp(CONTEXT(t), 1); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for OpenBSD */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for OpenBSD."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OpenBSD."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/os_AIX.s b/nsprpub/pr/src/md/unix/os_AIX.s new file mode 100644 index 0000000000..0227cc47f8 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_AIX.s @@ -0,0 +1,91 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4 +.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9 +.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14 +.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19 +.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24 +.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29 +.set r30,30; .set r31,31 + + + .rename H.10.NO_SYMBOL{PR},"" + .rename H.18.longjmp{TC},"longjmp" + + .lglobl H.10.NO_SYMBOL{PR} + .globl .longjmp + .globl longjmp{DS} + .extern .sigcleanup + .extern .jmprestfpr + +# .text section + + .csect H.10.NO_SYMBOL{PR} +.longjmp: + mr r13,r3 + mr r14,r4 + stu SP,-56(SP) + bl .sigcleanup + l RTOC,0x14(SP) + cal SP,0x38(SP) + mr r3,r13 + mr r4,r14 + l r5,0x8(r3) + l SP,0xc(r3) + l r7,0xf8(r3) + st r7,0x0(SP) + l RTOC,0x10(r3) + bl .jmprestfpr +# 1 == cr0 in disassembly + cmpi 1,r4,0x0 + mtlr r5 + lm r13,0x14(r3) + l r5,0x60(r3) + mtcrf 0x38,r5 + mr r3,r4 + bne __L1 + lil r3,0x1 +__L1: + br + +# traceback table + .long 0x00000000 + .byte 0x00 # VERSION=0 + .byte 0x00 # LANG=TB_C + .byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1 + # INT_PROC=0,HAS_CTL=0,TOCLESS=0 + # FP_PRESENT=0,LOG_ABORT=0 + .byte 0x40 # INT_HNDL=0,NAME_PRESENT=1 + # USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND + # SAVES_CR=0,SAVES_LR=0 + .byte 0x80 # STORES_BC=1,FPR_SAVED=0 + .byte 0x00 # GPR_SAVED=0 + .byte 0x02 # FIXEDPARMS=2 + .byte 0x01 # FLOATPARMS=0,PARMSONSTK=1 + .long 0x00000000 # + .long 0x00000014 # TB_OFFSET + .short 7 # NAME_LEN + .byte "longjmp" + .byte 0 # padding + .byte 0 # padding + .byte 0 # padding +# End of traceback table + .long 0x00000000 # "\0\0\0\0" + +# .data section + + .toc # 0x00000038 +T.18.longjmp: + .tc H.18.longjmp{TC},longjmp{DS} + + .csect longjmp{DS} + .long .longjmp # "\0\0\0\0" + .long TOC{TC0} # "\0\0\0008" + .long 0x00000000 # "\0\0\0\0" +# End csect longjmp{DS} + +# .bss section diff --git a/nsprpub/pr/src/md/unix/os_BSD_386_2.s b/nsprpub/pr/src/md/unix/os_BSD_386_2.s new file mode 100644 index 0000000000..fa36599e73 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_BSD_386_2.s @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * os_BSD_386_2.s + * We need to define our own setjmp/longjmp on BSDI 2.x because libc's + * implementation does some sanity checking that defeats user level threads. + * This should no longer be necessary in BSDI 3.0. + */ + +.globl _setjmp +.align 2 +_setjmp: + movl 4(%esp),%eax + movl 0(%esp),%edx + movl %edx, 0(%eax) /* rta */ + movl %ebx, 4(%eax) + movl %esp, 8(%eax) + movl %ebp,12(%eax) + movl %esi,16(%eax) + movl %edi,20(%eax) + movl $0,%eax + ret + +.globl _longjmp +.align 2 +_longjmp: + movl 4(%esp),%edx + movl 8(%esp),%eax + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret diff --git a/nsprpub/pr/src/md/unix/os_Darwin.s b/nsprpub/pr/src/md/unix/os_Darwin.s new file mode 100644 index 0000000000..26a0b9226e --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Darwin.s @@ -0,0 +1,13 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifdef __i386__ +#include "os_Darwin_x86.s" +#elif defined(__x86_64__) +#include "os_Darwin_x86_64.s" +#elif defined(__ppc__) +#include "os_Darwin_ppc.s" +#endif diff --git a/nsprpub/pr/src/md/unix/os_Darwin_ppc.s b/nsprpub/pr/src/md/unix/os_Darwin_ppc.s new file mode 100644 index 0000000000..8479dec01f --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Darwin_ppc.s @@ -0,0 +1,68 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# Based on the programming examples in The PowerPC Architecture: +# A Specification for A New Family of RISC Processors, 2nd Ed., +# Book I, Section E.1, "Synchronization," pp. 249-256, May 1994. +# + +.text + +# +# PRInt32 __PR_DarwinPPC_AtomicIncrement(PRInt32 *val); +# + .align 2 + .globl __PR_DarwinPPC_AtomicIncrement + .private_extern __PR_DarwinPPC_AtomicIncrement +__PR_DarwinPPC_AtomicIncrement: + lwarx r4,0,r3 + addi r0,r4,1 + stwcx. r0,0,r3 + bne- __PR_DarwinPPC_AtomicIncrement + mr r3,r0 + blr + +# +# PRInt32 __PR_DarwinPPC_AtomicDecrement(PRInt32 *val); +# + .align 2 + .globl __PR_DarwinPPC_AtomicDecrement + .private_extern __PR_DarwinPPC_AtomicDecrement +__PR_DarwinPPC_AtomicDecrement: + lwarx r4,0,r3 + addi r0,r4,-1 + stwcx. r0,0,r3 + bne- __PR_DarwinPPC_AtomicDecrement + mr r3,r0 + blr + +# +# PRInt32 __PR_DarwinPPC_AtomicSet(PRInt32 *val, PRInt32 newval); +# + .align 2 + .globl __PR_DarwinPPC_AtomicSet + .private_extern __PR_DarwinPPC_AtomicSet +__PR_DarwinPPC_AtomicSet: + lwarx r5,0,r3 + stwcx. r4,0,r3 + bne- __PR_DarwinPPC_AtomicSet + mr r3,r5 + blr + +# +# PRInt32 __PR_DarwinPPC_AtomicAdd(PRInt32 *ptr, PRInt32 val); +# + .align 2 + .globl __PR_DarwinPPC_AtomicAdd + .private_extern __PR_DarwinPPC_AtomicAdd +__PR_DarwinPPC_AtomicAdd: + lwarx r5,0,r3 + add r0,r4,r5 + stwcx. r0,0,r3 + bne- __PR_DarwinPPC_AtomicAdd + mr r3,r0 + blr diff --git a/nsprpub/pr/src/md/unix/os_Darwin_x86.s b/nsprpub/pr/src/md/unix/os_Darwin_x86.s new file mode 100644 index 0000000000..fc6379f952 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Darwin_x86.s @@ -0,0 +1,80 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# Based on os_Linux_x86.s +# + +# +# PRInt32 __PR_Darwin_x86_AtomicIncrement(PRInt32 *val); +# +# Atomically increment the integer pointed to by 'val' and return +# the result of the increment. +# + .text + .globl __PR_Darwin_x86_AtomicIncrement + .private_extern __PR_Darwin_x86_AtomicIncrement + .align 4 +__PR_Darwin_x86_AtomicIncrement: + movl 4(%esp), %ecx + movl $1, %eax + lock + xaddl %eax, (%ecx) + incl %eax + ret + +# +# PRInt32 __PR_Darwin_x86_AtomicDecrement(PRInt32 *val); +# +# Atomically decrement the integer pointed to by 'val' and return +# the result of the decrement. +# + .text + .globl __PR_Darwin_x86_AtomicDecrement + .private_extern __PR_Darwin_x86_AtomicDecrement + .align 4 +__PR_Darwin_x86_AtomicDecrement: + movl 4(%esp), %ecx + movl $-1, %eax + lock + xaddl %eax, (%ecx) + decl %eax + ret + +# +# PRInt32 __PR_Darwin_x86_AtomicSet(PRInt32 *val, PRInt32 newval); +# +# Atomically set the integer pointed to by 'val' to the new +# value 'newval' and return the old value. +# + .text + .globl __PR_Darwin_x86_AtomicSet + .private_extern __PR_Darwin_x86_AtomicSet + .align 4 +__PR_Darwin_x86_AtomicSet: + movl 4(%esp), %ecx + movl 8(%esp), %eax + xchgl %eax, (%ecx) + ret + +# +# PRInt32 __PR_Darwin_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val); +# +# Atomically add 'val' to the integer pointed to by 'ptr' +# and return the result of the addition. +# + .text + .globl __PR_Darwin_x86_AtomicAdd + .private_extern __PR_Darwin_x86_AtomicAdd + .align 4 +__PR_Darwin_x86_AtomicAdd: + movl 4(%esp), %ecx + movl 8(%esp), %eax + movl %eax, %edx + lock + xaddl %eax, (%ecx) + addl %edx, %eax + ret diff --git a/nsprpub/pr/src/md/unix/os_Darwin_x86_64.s b/nsprpub/pr/src/md/unix/os_Darwin_x86_64.s new file mode 100644 index 0000000000..449aaa5db6 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Darwin_x86_64.s @@ -0,0 +1,67 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# PRInt32 __PR_Darwin_x86_64_AtomicIncrement(PRInt32 *val) +# +# Atomically increment the integer pointed to by 'val' and return +# the result of the increment. +# + .text + .globl __PR_Darwin_x86_64_AtomicIncrement + .private_extern __PR_Darwin_x86_64_AtomicIncrement + .align 4 +__PR_Darwin_x86_64_AtomicIncrement: + movl $1, %eax + lock + xaddl %eax, (%rdi) + incl %eax + ret + +# PRInt32 __PR_Darwin_x86_64_AtomicDecrement(PRInt32 *val) +# +# Atomically decrement the integer pointed to by 'val' and return +# the result of the decrement. +# + .text + .globl __PR_Darwin_x86_64_AtomicDecrement + .private_extern __PR_Darwin_x86_64_AtomicDecrement + .align 4 +__PR_Darwin_x86_64_AtomicDecrement: + movl $-1, %eax + lock + xaddl %eax, (%rdi) + decl %eax + ret + +# PRInt32 __PR_Darwin_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval) +# +# Atomically set the integer pointed to by 'val' to the new +# value 'newval' and return the old value. +# + .text + .globl __PR_Darwin_x86_64_AtomicSet + .private_extern __PR_Darwin_x86_64_AtomicSet + .align 4 +__PR_Darwin_x86_64_AtomicSet: + movl %esi, %eax + xchgl %eax, (%rdi) + ret + +# PRInt32 __PR_Darwin_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val) +# +# Atomically add 'val' to the integer pointed to by 'ptr' +# and return the result of the addition. +# + .text + .globl __PR_Darwin_x86_64_AtomicAdd + .private_extern __PR_Darwin_x86_64_AtomicAdd + .align 4 +__PR_Darwin_x86_64_AtomicAdd: + movl %esi, %eax + lock + xaddl %eax, (%rdi) + addl %esi, %eax + ret diff --git a/nsprpub/pr/src/md/unix/os_HPUX.s b/nsprpub/pr/src/md/unix/os_HPUX.s new file mode 100644 index 0000000000..f8ecabf9b0 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_HPUX.s @@ -0,0 +1,25 @@ +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifdef __LP64__ + .LEVEL 2.0W +#else + .LEVEL 1.1 +#endif + + .CODE ; equivalent to the following two lines +; .SPACE $TEXT$,SORT=8 +; .SUBSPA $CODE$,QUAD=0,ALIGN=4,ACCESS=0x2c,CODE_ONLY,SORT=24 + +ret_cr16 + .PROC + .CALLINFO FRAME=0, NO_CALLS + .EXPORT ret_cr16,ENTRY + .ENTRY + BV %r0(%rp) + .EXIT + MFCTL %cr16,%ret0 + .PROCEND + .END diff --git a/nsprpub/pr/src/md/unix/os_HPUX_ia64.s b/nsprpub/pr/src/md/unix/os_HPUX_ia64.s new file mode 100644 index 0000000000..302ae7687d --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_HPUX_ia64.s @@ -0,0 +1,80 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +.text + +// PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val) +// +// Atomically increment the integer pointed to by 'val' and return +// the result of the increment. +// + .align 16 + .global _PR_ia64_AtomicIncrement# + .proc _PR_ia64_AtomicIncrement# +_PR_ia64_AtomicIncrement: +#ifndef _LP64 + addp4 r32 = 0, r32 ;; +#endif + fetchadd4.acq r8 = [r32], 1 ;; + adds r8 = 1, r8 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicIncrement# + +// PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val) +// +// Atomically decrement the integer pointed to by 'val' and return +// the result of the decrement. +// + .align 16 + .global _PR_ia64_AtomicDecrement# + .proc _PR_ia64_AtomicDecrement# +_PR_ia64_AtomicDecrement: +#ifndef _LP64 + addp4 r32 = 0, r32 ;; +#endif + fetchadd4.rel r8 = [r32], -1 ;; + adds r8 = -1, r8 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicDecrement# + +// PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val) +// +// Atomically add 'val' to the integer pointed to by 'ptr' +// and return the result of the addition. +// + .align 16 + .global _PR_ia64_AtomicAdd# + .proc _PR_ia64_AtomicAdd# +_PR_ia64_AtomicAdd: +#ifndef _LP64 + addp4 r32 = 0, r32 ;; +#endif + ld4 r15 = [r32] ;; +.L3: + mov r14 = r15 + mov ar.ccv = r15 + add r8 = r15, r33 ;; + cmpxchg4.acq r15 = [r32], r8, ar.ccv ;; + cmp4.ne p6, p7 = r15, r14 + (p6) br.cond.dptk .L3 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicAdd# + +// PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval) +// +// Atomically set the integer pointed to by 'val' to the new +// value 'newval' and return the old value. +// + .align 16 + .global _PR_ia64_AtomicSet# + .proc _PR_ia64_AtomicSet# +_PR_ia64_AtomicSet: +#ifndef _LP64 + addp4 r32 = 0, r32 ;; +#endif + xchg4 r8 = [r32], r33 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicSet# diff --git a/nsprpub/pr/src/md/unix/os_Linux_ia64.s b/nsprpub/pr/src/md/unix/os_Linux_ia64.s new file mode 100644 index 0000000000..39b724ae47 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Linux_ia64.s @@ -0,0 +1,71 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +.text + +// PRInt32 _PR_ia64_AtomicIncrement(PRInt32 *val) +// +// Atomically increment the integer pointed to by 'val' and return +// the result of the increment. +// + .align 16 + .global _PR_ia64_AtomicIncrement# + .proc _PR_ia64_AtomicIncrement# +_PR_ia64_AtomicIncrement: + fetchadd4.acq r8 = [r32], 1 ;; + adds r8 = 1, r8 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicIncrement# + +// PRInt32 _PR_ia64_AtomicDecrement(PRInt32 *val) +// +// Atomically decrement the integer pointed to by 'val' and return +// the result of the decrement. +// + .align 16 + .global _PR_ia64_AtomicDecrement# + .proc _PR_ia64_AtomicDecrement# +_PR_ia64_AtomicDecrement: + fetchadd4.rel r8 = [r32], -1 ;; + adds r8 = -1, r8 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicDecrement# + +// PRInt32 _PR_ia64_AtomicAdd(PRInt32 *ptr, PRInt32 val) +// +// Atomically add 'val' to the integer pointed to by 'ptr' +// and return the result of the addition. +// + .align 16 + .global _PR_ia64_AtomicAdd# + .proc _PR_ia64_AtomicAdd# +_PR_ia64_AtomicAdd: + ld4 r15 = [r32] ;; +.L3: + mov r14 = r15 + mov ar.ccv = r15 + add r8 = r15, r33 ;; + cmpxchg4.acq r15 = [r32], r8, ar.ccv ;; + cmp4.ne p6, p7 = r15, r14 + (p6) br.cond.dptk .L3 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicAdd# + +// PRInt32 _PR_ia64_AtomicSet(PRInt32 *val, PRInt32 newval) +// +// Atomically set the integer pointed to by 'val' to the new +// value 'newval' and return the old value. +// + .align 16 + .global _PR_ia64_AtomicSet# + .proc _PR_ia64_AtomicSet# +_PR_ia64_AtomicSet: + xchg4 r8 = [r32], r33 + br.ret.sptk.many b0 + .endp _PR_ia64_AtomicSet# + +// Magic indicating no need for an executable stack +.section .note.GNU-stack, "", @progbits diff --git a/nsprpub/pr/src/md/unix/os_Linux_ppc.s b/nsprpub/pr/src/md/unix/os_Linux_ppc.s new file mode 100644 index 0000000000..76da33bffc --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Linux_ppc.s @@ -0,0 +1,75 @@ +# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# Based on the programming examples in The PowerPC Architecture: +# A Specification for A New Family of RISC Processors, 2nd Ed., +# Book I, Section E.1, "Synchronization," pp. 249-256, May 1994. +# + + .section ".text" + +# +# PRInt32 _PR_ppc_AtomicIncrement(PRInt32 *val); +# + .align 2 + .globl _PR_ppc_AtomicIncrement + .type _PR_ppc_AtomicIncrement,@function +_PR_ppc_AtomicIncrement: +.Lfd1: lwarx 4,0,3 + addi 0,4,1 + stwcx. 0,0,3 + bne- .Lfd1 + mr 3,0 + blr +.Lfe1: .size _PR_ppc_AtomicIncrement,.Lfe1-_PR_ppc_AtomicIncrement + +# +# PRInt32 _PR_ppc_AtomicDecrement(PRInt32 *val); +# + .align 2 + .globl _PR_ppc_AtomicDecrement + .type _PR_ppc_AtomicDecrement,@function +_PR_ppc_AtomicDecrement: +.Lfd2: lwarx 4,0,3 + addi 0,4,-1 + stwcx. 0,0,3 + bne- .Lfd2 + mr 3,0 + blr +.Lfe2: .size _PR_ppc_AtomicDecrement,.Lfe2-_PR_ppc_AtomicDecrement + +# +# PRInt32 _PR_ppc_AtomicSet(PRInt32 *val, PRInt32 newval); +# + .align 2 + .globl _PR_ppc_AtomicSet + .type _PR_ppc_AtomicSet,@function +_PR_ppc_AtomicSet: +.Lfd3: lwarx 5,0,3 + stwcx. 4,0,3 + bne- .Lfd3 + mr 3,5 + blr +.Lfe3: .size _PR_ppc_AtomicSet,.Lfe3-_PR_ppc_AtomicSet + +# +# PRInt32 _PR_ppc_AtomicAdd(PRInt32 *ptr, PRInt32 val); +# + .align 2 + .globl _PR_ppc_AtomicAdd + .type _PR_ppc_AtomicAdd,@function +_PR_ppc_AtomicAdd: +.Lfd4: lwarx 5,0,3 + add 0,4,5 + stwcx. 0,0,3 + bne- .Lfd4 + mr 3,0 + blr +.Lfe4: .size _PR_ppc_AtomicAdd,.Lfe4-_PR_ppc_AtomicAdd + +# Magic indicating no need for an executable stack +.section .note.GNU-stack, "", @progbits diff --git a/nsprpub/pr/src/md/unix/os_Linux_x86.s b/nsprpub/pr/src/md/unix/os_Linux_x86.s new file mode 100644 index 0000000000..83e10b4552 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Linux_x86.s @@ -0,0 +1,85 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// PRInt32 _PR_x86_AtomicIncrement(PRInt32 *val) +// +// Atomically increment the integer pointed to by 'val' and return +// the result of the increment. +// + .text + .globl _PR_x86_AtomicIncrement + .align 4 +_PR_x86_AtomicIncrement: + movl 4(%esp), %ecx + movl $1, %eax + lock + xaddl %eax, (%ecx) + incl %eax + ret + +// PRInt32 _PR_x86_AtomicDecrement(PRInt32 *val) +// +// Atomically decrement the integer pointed to by 'val' and return +// the result of the decrement. +// + .text + .globl _PR_x86_AtomicDecrement + .align 4 +_PR_x86_AtomicDecrement: + movl 4(%esp), %ecx + movl $-1, %eax + lock + xaddl %eax, (%ecx) + decl %eax + ret + +// PRInt32 _PR_x86_AtomicSet(PRInt32 *val, PRInt32 newval) +// +// Atomically set the integer pointed to by 'val' to the new +// value 'newval' and return the old value. +// +// An alternative implementation: +// .text +// .globl _PR_x86_AtomicSet +// .align 4 +//_PR_x86_AtomicSet: +// movl 4(%esp), %ecx +// movl 8(%esp), %edx +// movl (%ecx), %eax +//retry: +// lock +// cmpxchgl %edx, (%ecx) +// jne retry +// ret +// + .text + .globl _PR_x86_AtomicSet + .align 4 +_PR_x86_AtomicSet: + movl 4(%esp), %ecx + movl 8(%esp), %eax + xchgl %eax, (%ecx) + ret + +// PRInt32 _PR_x86_AtomicAdd(PRInt32 *ptr, PRInt32 val) +// +// Atomically add 'val' to the integer pointed to by 'ptr' +// and return the result of the addition. +// + .text + .globl _PR_x86_AtomicAdd + .align 4 +_PR_x86_AtomicAdd: + movl 4(%esp), %ecx + movl 8(%esp), %eax + movl %eax, %edx + lock + xaddl %eax, (%ecx) + addl %edx, %eax + ret + +// Magic indicating no need for an executable stack +.section .note.GNU-stack, "", @progbits diff --git a/nsprpub/pr/src/md/unix/os_Linux_x86_64.s b/nsprpub/pr/src/md/unix/os_Linux_x86_64.s new file mode 100644 index 0000000000..f30e75d538 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_Linux_x86_64.s @@ -0,0 +1,74 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// PRInt32 _PR_x86_64_AtomicIncrement(PRInt32 *val) +// +// Atomically increment the integer pointed to by 'val' and return +// the result of the increment. +// + .text + .globl _PR_x86_64_AtomicIncrement + .type _PR_x86_64_AtomicIncrement, @function + .align 4 +_PR_x86_64_AtomicIncrement: + movl $1, %eax + lock + xaddl %eax, (%rdi) + incl %eax + ret + .size _PR_x86_64_AtomicIncrement, .-_PR_x86_64_AtomicIncrement + +// PRInt32 _PR_x86_64_AtomicDecrement(PRInt32 *val) +// +// Atomically decrement the integer pointed to by 'val' and return +// the result of the decrement. +// + .text + .globl _PR_x86_64_AtomicDecrement + .type _PR_x86_64_AtomicDecrement, @function + .align 4 +_PR_x86_64_AtomicDecrement: + movl $-1, %eax + lock + xaddl %eax, (%rdi) + decl %eax + ret + .size _PR_x86_64_AtomicDecrement, .-_PR_x86_64_AtomicDecrement + +// PRInt32 _PR_x86_64_AtomicSet(PRInt32 *val, PRInt32 newval) +// +// Atomically set the integer pointed to by 'val' to the new +// value 'newval' and return the old value. +// + .text + .globl _PR_x86_64_AtomicSet + .type _PR_x86_64_AtomicSet, @function + .align 4 +_PR_x86_64_AtomicSet: + movl %esi, %eax + xchgl %eax, (%rdi) + ret + .size _PR_x86_64_AtomicSet, .-_PR_x86_64_AtomicSet + +// PRInt32 _PR_x86_64_AtomicAdd(PRInt32 *ptr, PRInt32 val) +// +// Atomically add 'val' to the integer pointed to by 'ptr' +// and return the result of the addition. +// + .text + .globl _PR_x86_64_AtomicAdd + .type _PR_x86_64_AtomicAdd, @function + .align 4 +_PR_x86_64_AtomicAdd: + movl %esi, %eax + lock + xaddl %eax, (%rdi) + addl %esi, %eax + ret + .size _PR_x86_64_AtomicAdd, .-_PR_x86_64_AtomicAdd + +// Magic indicating no need for an executable stack +.section .note.GNU-stack, "", @progbits diff --git a/nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s b/nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s new file mode 100644 index 0000000000..8bc75af88d --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_SunOS_sparcv9.s @@ -0,0 +1,173 @@ +! -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +! +! This Source Code Form is subject to the terms of the Mozilla Public +! License, v. 2.0. If a copy of the MPL was not distributed with this +! file, You can obtain one at http://mozilla.org/MPL/2.0/. + +! +! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc) +! using CAS (compare-and-swap) atomic instructions +! +! this MUST be compiled with an ultrasparc-aware assembler +! +! standard asm linkage macros; this module must be compiled +! with the -P option (use C preprocessor) + +#include <sys/asm_linkage.h> + +! ====================================================================== +! +! Perform the sequence a = a + 1 atomically with respect to other +! fetch-and-adds to location a in a wait-free fashion. +! +! usage : val = PR_AtomicIncrement(address) +! return: current value (you'd think this would be old val) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [local] - work register +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(_MD_AtomicIncrement) ! standard assembler/ELF prologue + +retryAI: + ld [%o0], %o2 ! set o2 to the current value + add %o2, 0x1, %o3 ! calc the new value + mov %o3, %o1 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAI ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o1, %o0 ! set the return code to the new value + + SET_SIZE(_MD_AtomicIncrement) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = a - 1 atomically with respect to other +! fetch-and-decs to location a in a wait-free fashion. +! +! usage : val = PR_AtomicDecrement(address) +! return: current value (you'd think this would be old val) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [local] - work register +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(_MD_AtomicDecrement) ! standard assembler/ELF prologue + +retryAD: + ld [%o0], %o2 ! set o2 to the current value + sub %o2, 0x1, %o3 ! calc the new value + mov %o3, %o1 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAD ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o1, %o0 ! set the return code to the new value + + SET_SIZE(_MD_AtomicDecrement) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = b atomically with respect to other +! fetch-and-stores to location a in a wait-free fashion. +! +! usage : old_val = PR_AtomicSet(address, newval) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [input] - the new value to set for [%o0] +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(_MD_AtomicSet) ! standard assembler/ELF prologue + +retryAS: + ld [%o0], %o2 ! set o2 to the current value + mov %o1, %o3 ! set up the new value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAS ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o3, %o0 ! set the return code to the prev value + + SET_SIZE(_MD_AtomicSet) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = a + b atomically with respect to other +! fetch-and-adds to location a in a wait-free fashion. +! +! usage : newval = PR_AtomicAdd(address, val) +! return: the value after addition +! + ENTRY(_MD_AtomicAdd) ! standard assembler/ELF prologue + +retryAA: + ld [%o0], %o2 ! set o2 to the current value + add %o2, %o1, %o3 ! calc the new value + mov %o3, %o4 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAA ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o4, %o0 ! set the return code to the new value + + SET_SIZE(_MD_AtomicAdd) ! standard assembler/ELF epilogue + +! +! end +! diff --git a/nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s b/nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s new file mode 100644 index 0000000000..32bf7884ac --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_SunOS_ultrasparc.s @@ -0,0 +1,173 @@ +! -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +! +! This Source Code Form is subject to the terms of the Mozilla Public +! License, v. 2.0. If a copy of the MPL was not distributed with this +! file, You can obtain one at http://mozilla.org/MPL/2.0/. + +! +! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc) +! using CAS (compare-and-swap) atomic instructions +! +! this MUST be compiled with an ultrasparc-aware assembler +! +! standard asm linkage macros; this module must be compiled +! with the -P option (use C preprocessor) + +#include <sys/asm_linkage.h> + +! ====================================================================== +! +! Perform the sequence a = a + 1 atomically with respect to other +! fetch-and-adds to location a in a wait-free fashion. +! +! usage : val = PR_AtomicIncrement(address) +! return: current value (you'd think this would be old val) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [local] - work register +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(PR_AtomicIncrement) ! standard assembler/ELF prologue + +retryAI: + ld [%o0], %o2 ! set o2 to the current value + add %o2, 0x1, %o3 ! calc the new value + mov %o3, %o1 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAI ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o1, %o0 ! set the return code to the new value + + SET_SIZE(PR_AtomicIncrement) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = a - 1 atomically with respect to other +! fetch-and-decs to location a in a wait-free fashion. +! +! usage : val = PR_AtomicDecrement(address) +! return: current value (you'd think this would be old val) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [local] - work register +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(PR_AtomicDecrement) ! standard assembler/ELF prologue + +retryAD: + ld [%o0], %o2 ! set o2 to the current value + sub %o2, 0x1, %o3 ! calc the new value + mov %o3, %o1 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAD ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o1, %o0 ! set the return code to the new value + + SET_SIZE(PR_AtomicDecrement) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = b atomically with respect to other +! fetch-and-stores to location a in a wait-free fashion. +! +! usage : old_val = PR_AtomicSet(address, newval) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [input] - the new value to set for [%o0] +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(PR_AtomicSet) ! standard assembler/ELF prologue + +retryAS: + ld [%o0], %o2 ! set o2 to the current value + mov %o1, %o3 ! set up the new value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAS ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o3, %o0 ! set the return code to the prev value + + SET_SIZE(PR_AtomicSet) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = a + b atomically with respect to other +! fetch-and-adds to location a in a wait-free fashion. +! +! usage : newval = PR_AtomicAdd(address, val) +! return: the value after addition +! + ENTRY(PR_AtomicAdd) ! standard assembler/ELF prologue + +retryAA: + ld [%o0], %o2 ! set o2 to the current value + add %o2, %o1, %o3 ! calc the new value + mov %o3, %o4 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAA ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o4, %o0 ! set the return code to the new value + + SET_SIZE(PR_AtomicAdd) ! standard assembler/ELF epilogue + +! +! end +! diff --git a/nsprpub/pr/src/md/unix/os_SunOS_x86.s b/nsprpub/pr/src/md/unix/os_SunOS_x86.s new file mode 100644 index 0000000000..dcd7535bf6 --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_SunOS_x86.s @@ -0,0 +1,126 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + + .text + + .globl getedi +getedi: + movl %edi,%eax + ret + .type getedi,@function + .size getedi,.-getedi + + .globl setedi +setedi: + movl 4(%esp),%edi + ret + .type setedi,@function + .size setedi,.-setedi + + .globl __MD_FlushRegisterWindows + .globl _MD_FlushRegisterWindows + +__MD_FlushRegisterWindows: +_MD_FlushRegisterWindows: + + ret + +// +// sol_getsp() +// +// Return the current sp (for debugging) +// + .globl sol_getsp +sol_getsp: + movl %esp, %eax + ret + +// +// sol_curthread() +// +// Return a unique identifier for the currently active thread. +// + .globl sol_curthread +sol_curthread: + movl %ecx, %eax + ret + +// PRInt32 _MD_AtomicIncrement(PRInt32 *val) +// +// Atomically increment the integer pointed to by 'val' and return +// the result of the increment. +// + .text + .globl _MD_AtomicIncrement + .align 4 +_MD_AtomicIncrement: + movl 4(%esp), %ecx + movl $1, %eax + lock + xaddl %eax, (%ecx) + incl %eax + ret + +// PRInt32 _MD_AtomicDecrement(PRInt32 *val) +// +// Atomically decrement the integer pointed to by 'val' and return +// the result of the decrement. +// + .text + .globl _MD_AtomicDecrement + .align 4 +_MD_AtomicDecrement: + movl 4(%esp), %ecx + movl $-1, %eax + lock + xaddl %eax, (%ecx) + decl %eax + ret + +// PRInt32 _MD_AtomicSet(PRInt32 *val, PRInt32 newval) +// +// Atomically set the integer pointed to by 'val' to the new +// value 'newval' and return the old value. +// +// An alternative implementation: +// .text +// .globl _MD_AtomicSet +// .align 4 +//_MD_AtomicSet: +// movl 4(%esp), %ecx +// movl 8(%esp), %edx +// movl (%ecx), %eax +//retry: +// lock +// cmpxchgl %edx, (%ecx) +// jne retry +// ret +// + .text + .globl _MD_AtomicSet + .align 4 +_MD_AtomicSet: + movl 4(%esp), %ecx + movl 8(%esp), %eax + xchgl %eax, (%ecx) + ret + +// PRInt32 _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val) +// +// Atomically add 'val' to the integer pointed to by 'ptr' +// and return the result of the addition. +// + .text + .globl _MD_AtomicAdd + .align 4 +_MD_AtomicAdd: + movl 4(%esp), %ecx + movl 8(%esp), %eax + movl %eax, %edx + lock + xaddl %eax, (%ecx) + addl %edx, %eax + ret diff --git a/nsprpub/pr/src/md/unix/os_SunOS_x86_64.s b/nsprpub/pr/src/md/unix/os_SunOS_x86_64.s new file mode 100644 index 0000000000..5e3df049fe --- /dev/null +++ b/nsprpub/pr/src/md/unix/os_SunOS_x86_64.s @@ -0,0 +1,63 @@ +// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// PRInt32 _MD_AtomicIncrement(PRInt32 *val) +// +// Atomically increment the integer pointed to by 'val' and return +// the result of the increment. +// + .text + .globl _MD_AtomicIncrement + .align 4 +_MD_AtomicIncrement: + movl $1, %eax + lock + xaddl %eax, (%rdi) + incl %eax + ret + +// PRInt32 _MD_AtomicDecrement(PRInt32 *val) +// +// Atomically decrement the integer pointed to by 'val' and return +// the result of the decrement. +// + .text + .globl _MD_AtomicDecrement + .align 4 +_MD_AtomicDecrement: + movl $-1, %eax + lock + xaddl %eax, (%rdi) + decl %eax + ret + +// PRInt32 _MD_AtomicSet(PRInt32 *val, PRInt32 newval) +// +// Atomically set the integer pointed to by 'val' to the new +// value 'newval' and return the old value. +// + .text + .globl _MD_AtomicSet + .align 4 +_MD_AtomicSet: + movl %esi, %eax + xchgl %eax, (%rdi) + ret + +// PRInt32 _MD_AtomicAdd(PRInt32 *ptr, PRInt32 val) +// +// Atomically add 'val' to the integer pointed to by 'ptr' +// and return the result of the addition. +// + .text + .globl _MD_AtomicAdd + .align 4 +_MD_AtomicAdd: + movl %esi, %eax + lock + xaddl %eax, (%rdi) + addl %esi, %eax + ret diff --git a/nsprpub/pr/src/md/unix/pthreads_user.c b/nsprpub/pr/src/md/unix/pthreads_user.c new file mode 100644 index 0000000000..525c1576ff --- /dev/null +++ b/nsprpub/pr/src/md/unix/pthreads_user.c @@ -0,0 +1,464 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <pthread.h> + + +sigset_t ints_off; +pthread_mutex_t _pr_heapLock; +pthread_key_t current_thread_key; +pthread_key_t current_cpu_key; +pthread_key_t last_thread_key; +pthread_key_t intsoff_key; + + +PRInt32 _pr_md_pthreads_created, _pr_md_pthreads_failed; +PRInt32 _pr_md_pthreads = 1; + +void _MD_EarlyInit(void) +{ + extern PRInt32 _nspr_noclock; + + if (pthread_key_create(¤t_thread_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + if (pthread_key_create(¤t_cpu_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + if (pthread_key_create(&last_thread_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + if (pthread_key_create(&intsoff_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + + sigemptyset(&ints_off); + sigaddset(&ints_off, SIGALRM); + sigaddset(&ints_off, SIGIO); + sigaddset(&ints_off, SIGCLD); + + /* + * disable clock interrupts + */ + _nspr_noclock = 1; + +} + +void _MD_InitLocks() +{ + if (pthread_mutex_init(&_pr_heapLock, NULL) != 0) { + perror("pthread_mutex_init failed"); + exit(1); + } +} + +PR_IMPLEMENT(void) _MD_FREE_LOCK(struct _MDLock *lockp) +{ + PRIntn _is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(_is); + } + pthread_mutex_destroy(&lockp->mutex); + if (me && !_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(_is); + } +} + + + +PR_IMPLEMENT(PRStatus) _MD_NEW_LOCK(struct _MDLock *lockp) +{ + PRStatus rv; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + rv = pthread_mutex_init(&lockp->mutex, NULL); + if (me && !_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + return (rv == 0) ? PR_SUCCESS : PR_FAILURE; +} + + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +PR_IMPLEMENT(void) +_MD_SetPriority(_MDThread *thread, PRThreadPriority newPri) +{ + /* + * XXX - to be implemented + */ + return; +} + +PR_IMPLEMENT(PRStatus) _MD_InitThread(struct PRThread *thread) +{ + struct sigaction sigact; + + if (thread->flags & _PR_GLOBAL_SCOPE) { + thread->md.pthread = pthread_self(); +#if 0 + /* + * set up SIGUSR1 handler; this is used to save state + * during PR_SuspendAll + */ + sigact.sa_handler = save_context_and_block; + sigact.sa_flags = SA_RESTART; + /* + * Must mask clock interrupts + */ + sigact.sa_mask = timer_set; + sigaction(SIGUSR1, &sigact, 0); +#endif + } + + return PR_SUCCESS; +} + +PR_IMPLEMENT(void) _MD_ExitThread(struct PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + _MD_CLEAN_THREAD(thread); + _MD_SET_CURRENT_THREAD(NULL); + } +} + +PR_IMPLEMENT(void) _MD_CleanThread(struct PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + pthread_mutex_destroy(&thread->md.pthread_mutex); + pthread_cond_destroy(&thread->md.pthread_cond); + } +} + +PR_IMPLEMENT(void) _MD_SuspendThread(struct PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + _PR_IS_GCABLE_THREAD(thread)); +#if 0 + thread->md.suspending_id = getpid(); + rv = kill(thread->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); +#endif +} + +PR_IMPLEMENT(void) _MD_ResumeThread(struct PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + _PR_IS_GCABLE_THREAD(thread)); +#if 0 + rv = unblockproc(thread->md.id); +#endif +} + +PR_IMPLEMENT(void) _MD_SuspendCPU(struct _PRCPU *thread) +{ + PRInt32 rv; + +#if 0 + cpu->md.suspending_id = getpid(); + rv = kill(cpu->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); +#endif +} + +PR_IMPLEMENT(void) _MD_ResumeCPU(struct _PRCPU *thread) +{ +#if 0 + unblockproc(cpu->md.id); +#endif +} + + +#define PT_NANOPERMICRO 1000UL +#define PT_BILLION 1000000000UL + +PR_IMPLEMENT(PRStatus) +_pt_wait(PRThread *thread, PRIntervalTime timeout) +{ + int rv; + struct timeval now; + struct timespec tmo; + PRUint32 ticks = PR_TicksPerSecond(); + + + if (timeout != PR_INTERVAL_NO_TIMEOUT) { + tmo.tv_sec = timeout / ticks; + tmo.tv_nsec = timeout - (tmo.tv_sec * ticks); + tmo.tv_nsec = PR_IntervalToMicroseconds(PT_NANOPERMICRO * + tmo.tv_nsec); + + /* pthreads wants this in absolute time, off we go ... */ + (void)GETTIMEOFDAY(&now); + /* that one's usecs, this one's nsecs - grrrr! */ + tmo.tv_sec += now.tv_sec; + tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); + tmo.tv_sec += tmo.tv_nsec / PT_BILLION; + tmo.tv_nsec %= PT_BILLION; + } + + pthread_mutex_lock(&thread->md.pthread_mutex); + thread->md.wait--; + if (thread->md.wait < 0) { + if (timeout != PR_INTERVAL_NO_TIMEOUT) { + rv = pthread_cond_timedwait(&thread->md.pthread_cond, + &thread->md.pthread_mutex, &tmo); + } + else + rv = pthread_cond_wait(&thread->md.pthread_cond, + &thread->md.pthread_mutex); + if (rv != 0) { + thread->md.wait++; + } + } else { + rv = 0; + } + pthread_mutex_unlock(&thread->md.pthread_mutex); + + return (rv == 0) ? PR_SUCCESS : PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_MD_wait(PRThread *thread, PRIntervalTime ticks) +{ + if ( thread->flags & _PR_GLOBAL_SCOPE ) { + _MD_CHECK_FOR_EXIT(); + if (_pt_wait(thread, ticks) == PR_FAILURE) { + _MD_CHECK_FOR_EXIT(); + /* + * wait timed out + */ + _PR_THREAD_LOCK(thread); + if (thread->wait.cvar) { + /* + * The thread will remove itself from the waitQ + * of the cvar in _PR_WaitCondVar + */ + thread->wait.cvar = NULL; + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + } else { + _pt_wait(thread, PR_INTERVAL_NO_TIMEOUT); + _PR_THREAD_UNLOCK(thread); + } + } + } else { + _PR_MD_SWITCH_CONTEXT(thread); + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +_MD_WakeupWaiter(PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 pid, rv; + PRIntn is; + + PR_ASSERT(_pr_md_idle_cpus >= 0); + if (thread == NULL) { + if (_pr_md_idle_cpus) { + _MD_Wakeup_CPUs(); + } + } else if (!_PR_IS_NATIVE_THREAD(thread)) { + /* + * If the thread is on my cpu's runq there is no need to + * wakeup any cpus + */ + if (!_PR_IS_NATIVE_THREAD(me)) { + if (me->cpu != thread->cpu) { + if (_pr_md_idle_cpus) { + _MD_Wakeup_CPUs(); + } + } + } else { + if (_pr_md_idle_cpus) { + _MD_Wakeup_CPUs(); + } + } + } else { + PR_ASSERT(_PR_IS_NATIVE_THREAD(thread)); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + + pthread_mutex_lock(&thread->md.pthread_mutex); + thread->md.wait++; + rv = pthread_cond_signal(&thread->md.pthread_cond); + PR_ASSERT(rv == 0); + pthread_mutex_unlock(&thread->md.pthread_mutex); + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + } + return PR_SUCCESS; +} + +/* These functions should not be called for AIX */ +PR_IMPLEMENT(void) +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for AIX."); +} + +PR_IMPLEMENT(PRStatus) +_MD_CreateThread( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PRIntn is; + int rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + pthread_attr_t attr; + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + + if (pthread_mutex_init(&thread->md.pthread_mutex, NULL) != 0) { + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + return PR_FAILURE; + } + + if (pthread_cond_init(&thread->md.pthread_cond, NULL) != 0) { + pthread_mutex_destroy(&thread->md.pthread_mutex); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + return PR_FAILURE; + } + thread->flags |= _PR_GLOBAL_SCOPE; + + pthread_attr_init(&attr); /* initialize attr with default attributes */ + if (pthread_attr_setstacksize(&attr, (size_t) stackSize) != 0) { + pthread_mutex_destroy(&thread->md.pthread_mutex); + pthread_cond_destroy(&thread->md.pthread_cond); + pthread_attr_destroy(&attr); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + return PR_FAILURE; + } + + thread->md.wait = 0; + rv = pthread_create(&thread->md.pthread, &attr, start, (void *)thread); + if (0 == rv) { + _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_created); + _MD_ATOMIC_INCREMENT(&_pr_md_pthreads); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + return PR_SUCCESS; + } else { + pthread_mutex_destroy(&thread->md.pthread_mutex); + pthread_cond_destroy(&thread->md.pthread_cond); + pthread_attr_destroy(&attr); + _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_failed); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, rv); + return PR_FAILURE; + } +} + +PR_IMPLEMENT(void) +_MD_InitRunningCPU(struct _PRCPU *cpu) +{ + extern int _pr_md_pipefd[2]; + + _MD_unix_init_running_cpu(cpu); + cpu->md.pthread = pthread_self(); + if (_pr_md_pipefd[0] >= 0) { + _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0]; +#ifndef _PR_USE_POLL + FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu)); +#endif + } +} + + +void +_MD_CleanupBeforeExit(void) +{ +#if 0 + extern PRInt32 _pr_cpus_exit; + + _pr_irix_exit_now = 1; + if (_pr_numCPU > 1) { + /* + * Set a global flag, and wakeup all cpus which will notice the flag + * and exit. + */ + _pr_cpus_exit = getpid(); + _MD_Wakeup_CPUs(); + while(_pr_numCPU > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _pr_numCPU--; + } + } + /* + * cause global threads on the recycle list to exit + */ + _PR_DEADQ_LOCK; + if (_PR_NUM_DEADNATIVE != 0) { + PRThread *thread; + PRCList *ptr; + + ptr = _PR_DEADNATIVEQ.next; + while( ptr != &_PR_DEADNATIVEQ ) { + thread = _PR_THREAD_PTR(ptr); + _MD_CVAR_POST_SEM(thread); + ptr = ptr->next; + } + } + _PR_DEADQ_UNLOCK; + while(_PR_NUM_DEADNATIVE > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _PR_DEC_DEADNATIVE; + } +#endif +} diff --git a/nsprpub/pr/src/md/unix/qnx.c b/nsprpub/pr/src/md/unix/qnx.c new file mode 100644 index 0000000000..aef97a25a0 --- /dev/null +++ b/nsprpub/pr/src/md/unix/qnx.c @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Unixware */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Unixware."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRUintn priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware."); + return PR_FAILURE; +} diff --git a/nsprpub/pr/src/md/unix/riscos.c b/nsprpub/pr/src/md/unix/riscos.c new file mode 100644 index 0000000000..a041188753 --- /dev/null +++ b/nsprpub/pr/src/md/unix/riscos.c @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifdef _PR_PTHREADS + +void _MD_CleanupBeforeExit(void) +{ +} + +#else /* ! _PR_PTHREADS */ + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + /* + * set the pointers to the stack-pointer and frame-pointer words in the + * context structure; this is for debugging use. + */ + thread->md.sp = _MD_GET_SP_PTR(thread); + thread->md.fp = _MD_GET_FP_PTR(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for RISC OS */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for RISC OS."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for RISC OS."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/scoos.c b/nsprpub/pr/src/md/unix/scoos.c new file mode 100644 index 0000000000..43b256d82b --- /dev/null +++ b/nsprpub/pr/src/md/unix/scoos.c @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * SCO ODT 5.0 - originally created by mikep + */ +#include "primpl.h" + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +#ifdef ALARMS_BREAK_TCP /* I don't think they do */ + +PRInt32 _MD_connect(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _connect(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); +} + +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _accept(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); + return(rv); +} +#endif + +/* + * These are also implemented in pratom.c using NSPR locks. Any reason + * this might be better or worse? If you like this better, define + * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h + */ +#ifdef _PR_HAVE_ATOMIC_OPS +/* Atomic operations */ +#include <stdio.h> +static FILE *_uw_semf; + +void +_MD_INIT_ATOMIC(void) +{ + /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */ + if ((_uw_semf = tmpfile()) == NULL) { + PR_ASSERT(0); + } + + return; +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)++; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) +{ + flockfile(_uw_semf); + (*ptr) += val; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)--; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + flockfile(_uw_semf); + *val = newval; + unflockfile(_uw_semf); +} +#endif + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for SCO */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for SCO."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SCO."); +} diff --git a/nsprpub/pr/src/md/unix/solaris.c b/nsprpub/pr/src/md/unix/solaris.c new file mode 100644 index 0000000000..383f49d044 --- /dev/null +++ b/nsprpub/pr/src/md/unix/solaris.c @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + + +extern PRBool suspendAllOn; +extern PRThread *suspendAllThread; + +extern void _MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri); + +PRIntervalTime _MD_Solaris_TicksPerSecond(void) +{ + /* + * Ticks have a 10-microsecond resolution. So there are + * 100000 ticks per second. + */ + return 100000UL; +} + +/* Interval timers, implemented using gethrtime() */ + +PRIntervalTime _MD_Solaris_GetInterval(void) +{ + union { + hrtime_t hrt; /* hrtime_t is a 64-bit (long long) integer */ + PRInt64 pr64; + } time; + PRInt64 resolution; + PRIntervalTime ticks; + + time.hrt = gethrtime(); /* in nanoseconds */ + /* + * Convert from nanoseconds to ticks. A tick's resolution is + * 10 microseconds, or 10000 nanoseconds. + */ + LL_I2L(resolution, 10000); + LL_DIV(time.pr64, time.pr64, resolution); + LL_L2UI(ticks, time.pr64); + return ticks; +} + +#ifdef _PR_PTHREADS +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np) +{ + *np = 0; + return NULL; +} +#endif /* _PR_PTHREADS */ + +#if defined(_PR_LOCAL_THREADS_ONLY) + +void _MD_EarlyInit(void) +{ +} + +void _MD_SolarisInit() +{ + _PR_UnixInit(); +} + +void +_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + PR_ASSERT((thread == NULL) || (!(thread->flags & _PR_GLOBAL_SCOPE))); + return PR_SUCCESS; +} + +/* These functions should not be called for Solaris */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Solaris"); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Solaris"); + return(PR_FAILURE); +} + +#ifdef USE_SETJMP +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} +#else +PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np) +{ + if (isCurrent) { + (void) getcontext(CONTEXT(t)); + } + *np = NGREG; + return (PRWord*) &t->md.context.uc_mcontext.gregs[0]; +} +#endif /* USE_SETJMP */ + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +#ifndef _PR_PTHREADS +#if defined(i386) && defined(SOLARIS2_4) +/* + * Because clock_gettime() on Solaris/x86 2.4 always generates a + * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(), + * which is implemented using gettimeofday(). + */ + +int +_pr_solx86_clock_gettime(clockid_t clock_id, struct timespec *tp) +{ + struct timeval tv; + + if (clock_id != CLOCK_REALTIME) { + errno = EINVAL; + return -1; + } + + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; +} +#endif /* i386 && SOLARIS2_4 */ +#endif /* _PR_PTHREADS */ diff --git a/nsprpub/pr/src/md/unix/unix.c b/nsprpub/pr/src/md/unix/unix.c new file mode 100644 index 0000000000..70bb8e8744 --- /dev/null +++ b/nsprpub/pr/src/md/unix/unix.c @@ -0,0 +1,3744 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <sys/utsname.h> + +#ifdef _PR_POLL_AVAILABLE +#include <poll.h> +#endif + +#if defined(ANDROID) +#include <android/api-level.h> +#endif + +/* To get FIONREAD */ +#if defined(UNIXWARE) +#include <sys/filio.h> +#endif + +#if defined(NTO) +#include <sys/statvfs.h> +#endif + +/* + * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or + * PRInt32* pointer to a _PRSockLen_t* pointer. + */ +#if defined(HAVE_SOCKLEN_T) \ + || (defined(__GLIBC__) && __GLIBC__ >= 2) +#define _PRSockLen_t socklen_t +#elif defined(HPUX) || defined(SOLARIS) \ + || defined(AIX4_1) || defined(LINUX) \ + || defined(BSDI) || defined(SCO) \ + || defined(DARWIN) \ + || defined(QNX) +#define _PRSockLen_t int +#elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD) || defined(UNIXWARE) \ + || defined(NTO) || defined(RISCOS) +#define _PRSockLen_t size_t +#else +#error "Cannot determine architecture" +#endif + +/* +** Global lock variable used to bracket calls into rusty libraries that +** aren't thread safe (like libc, libX, etc). +*/ +static PRLock *_pr_unix_rename_lock = NULL; +static PRMonitor *_pr_Xfe_mon = NULL; + +static PRInt64 minus_one; + +sigset_t timer_set; + +#if !defined(_PR_PTHREADS) + +static sigset_t empty_set; + +#ifdef SOLARIS +#include <sys/file.h> +#include <sys/filio.h> +#endif + +#ifndef PIPE_BUF +#define PIPE_BUF 512 +#endif + +/* + * _nspr_noclock - if set clock interrupts are disabled + */ +int _nspr_noclock = 1; + +/* + * There is an assertion in this code that NSPR's definition of PRIOVec + * is bit compatible with UNIX' definition of a struct iovec. This is + * applicable to the 'writev()' operations where the types are casually + * cast to avoid warnings. + */ + +int _pr_md_pipefd[2] = { -1, -1 }; +static char _pr_md_pipebuf[PIPE_BUF]; +static PRInt32 local_io_wait(PRInt32 osfd, PRInt32 wait_flag, + PRIntervalTime timeout); + +_PRInterruptTable _pr_interruptTable[] = { + { + "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, + }, + { + 0 + } +}; + +void _MD_unix_init_running_cpu(_PRCPU *cpu) +{ + PR_INIT_CLIST(&(cpu->md.md_unix.ioQ)); + cpu->md.md_unix.ioq_max_osfd = -1; + cpu->md.md_unix.ioq_timeout = PR_INTERVAL_NO_TIMEOUT; +} + +PRStatus _MD_open_dir(_MDDir *d, const char *name) +{ + int err; + + d->d = opendir(name); + if (!d->d) { + err = _MD_ERRNO(); + _PR_MD_MAP_OPENDIR_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PRInt32 _MD_close_dir(_MDDir *d) +{ + int rv = 0, err; + + if (d->d) { + rv = closedir(d->d); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_CLOSEDIR_ERROR(err); + } + } + return rv; +} + +char * _MD_read_dir(_MDDir *d, PRIntn flags) +{ + struct dirent *de; + int err; + + for (;;) { + /* + * XXX: readdir() is not MT-safe. There is an MT-safe version + * readdir_r() on some systems. + */ + _MD_ERRNO() = 0; + de = readdir(d->d); + if (!de) { + err = _MD_ERRNO(); + _PR_MD_MAP_READDIR_ERROR(err); + return 0; + } + if ((flags & PR_SKIP_DOT) && + (de->d_name[0] == '.') && (de->d_name[1] == 0)) { + continue; + } + if ((flags & PR_SKIP_DOT_DOT) && + (de->d_name[0] == '.') && (de->d_name[1] == '.') && + (de->d_name[2] == 0)) { + continue; + } + if ((flags & PR_SKIP_HIDDEN) && (de->d_name[0] == '.')) { + continue; + } + break; + } + return de->d_name; +} + +PRInt32 _MD_delete(const char *name) +{ + PRInt32 rv, err; +#ifdef UNIXWARE + sigset_t set, oset; +#endif + +#ifdef UNIXWARE + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, &oset); +#endif + rv = unlink(name); +#ifdef UNIXWARE + sigprocmask(SIG_SETMASK, &oset, NULL); +#endif + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_UNLINK_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_rename(const char *from, const char *to) +{ + PRInt32 rv = -1, err; + + /* + ** This is trying to enforce the semantics of WINDOZE' rename + ** operation. That means one is not allowed to rename over top + ** of an existing file. Holding a lock across these two function + ** and the open function is known to be a bad idea, but .... + */ + if (NULL != _pr_unix_rename_lock) { + PR_Lock(_pr_unix_rename_lock); + } + if (0 == access(to, F_OK)) { + PR_SetError(PR_FILE_EXISTS_ERROR, 0); + } + else + { + rv = rename(from, to); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_RENAME_ERROR(err); + } + } + if (NULL != _pr_unix_rename_lock) { + PR_Unlock(_pr_unix_rename_lock); + } + return rv; +} + +PRInt32 _MD_access(const char *name, PRAccessHow how) +{ + PRInt32 rv, err; + int amode; + + switch (how) { + case PR_ACCESS_WRITE_OK: + amode = W_OK; + break; + case PR_ACCESS_READ_OK: + amode = R_OK; + break; + case PR_ACCESS_EXISTS: + amode = F_OK; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = -1; + goto done; + } + rv = access(name, amode); + + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_ACCESS_ERROR(err); + } + +done: + return(rv); +} + +PRInt32 _MD_mkdir(const char *name, PRIntn mode) +{ + int rv, err; + + /* + ** This lock is used to enforce rename semantics as described + ** in PR_Rename. Look there for more fun details. + */ + if (NULL !=_pr_unix_rename_lock) { + PR_Lock(_pr_unix_rename_lock); + } + rv = mkdir(name, mode); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_MKDIR_ERROR(err); + } + if (NULL !=_pr_unix_rename_lock) { + PR_Unlock(_pr_unix_rename_lock); + } + return rv; +} + +PRInt32 _MD_rmdir(const char *name) +{ + int rv, err; + + rv = rmdir(name); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_RMDIR_ERROR(err); + } + return rv; +} + +PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 rv, err; +#ifndef _PR_USE_POLL + fd_set rd; +#else + struct pollfd pfd; +#endif /* _PR_USE_POLL */ + PRInt32 osfd = fd->secret->md.osfd; + +#ifndef _PR_USE_POLL + FD_ZERO(&rd); + FD_SET(osfd, &rd); +#else + pfd.fd = osfd; + pfd.events = POLLIN; +#endif /* _PR_USE_POLL */ + while ((rv = read(osfd,buf,amount)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, + PR_INTERVAL_NO_TIMEOUT)) < 0) { + goto done; + } + } else { +#ifndef _PR_USE_POLL + while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL)) + == -1 && (err = _MD_ERRNO()) == EINTR) { + /* retry _MD_SELECT() if it is interrupted */ + } +#else /* _PR_USE_POLL */ + while ((rv = _MD_POLL(&pfd, 1, -1)) + == -1 && (err = _MD_ERRNO()) == EINTR) { + /* retry _MD_POLL() if it is interrupted */ + } +#endif /* _PR_USE_POLL */ + if (rv == -1) { + break; + } + } + if (_PR_PENDING_INTERRUPT(me)) { + break; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + _PR_MD_MAP_READ_ERROR(err); + } + } +done: + return(rv); +} + +PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 rv, err; +#ifndef _PR_USE_POLL + fd_set wd; +#else + struct pollfd pfd; +#endif /* _PR_USE_POLL */ + PRInt32 osfd = fd->secret->md.osfd; + +#ifndef _PR_USE_POLL + FD_ZERO(&wd); + FD_SET(osfd, &wd); +#else + pfd.fd = osfd; + pfd.events = POLLOUT; +#endif /* _PR_USE_POLL */ + while ((rv = write(osfd,buf,amount)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, + PR_INTERVAL_NO_TIMEOUT)) < 0) { + goto done; + } + } else { +#ifndef _PR_USE_POLL + while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL)) + == -1 && (err = _MD_ERRNO()) == EINTR) { + /* retry _MD_SELECT() if it is interrupted */ + } +#else /* _PR_USE_POLL */ + while ((rv = _MD_POLL(&pfd, 1, -1)) + == -1 && (err = _MD_ERRNO()) == EINTR) { + /* retry _MD_POLL() if it is interrupted */ + } +#endif /* _PR_USE_POLL */ + if (rv == -1) { + break; + } + } + if (_PR_PENDING_INTERRUPT(me)) { + break; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + _PR_MD_MAP_WRITE_ERROR(err); + } + } +done: + return(rv); +} + +PRInt32 _MD_fsync(PRFileDesc *fd) +{ + PRInt32 rv, err; + + rv = fsync(fd->secret->md.osfd); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_FSYNC_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_close(PRInt32 osfd) +{ + PRInt32 rv, err; + + rv = close(osfd); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_CLOSE_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRInt32 osfd, err; + + osfd = socket(domain, type, proto); + + if (osfd == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_SOCKET_ERROR(err); + return(osfd); + } + + return(osfd); +} + +PRInt32 _MD_socketavailable(PRFileDesc *fd) +{ + PRInt32 result; + + if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) { + _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO()); + return -1; + } + return result; +} + +PRInt64 _MD_socketavailable64(PRFileDesc *fd) +{ + PRInt64 result; + LL_I2L(result, _MD_socketavailable(fd)); + return result; +} /* _MD_socketavailable64 */ + +#define READ_FD 1 +#define WRITE_FD 2 + +/* + * socket_io_wait -- + * + * wait for socket i/o, periodically checking for interrupt + * + * The first implementation uses select(), for platforms without + * poll(). The second (preferred) implementation uses poll(). + */ + +#ifndef _PR_USE_POLL + +static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type, + PRIntervalTime timeout) +{ + PRInt32 rv = -1; + struct timeval tv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRInt32 syserror; + fd_set rd_wr; + + switch (timeout) { + case PR_INTERVAL_NO_WAIT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_INTERVAL_NO_TIMEOUT: + /* + * This is a special case of the 'default' case below. + * Please see the comments there. + */ + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + FD_ZERO(&rd_wr); + do { + FD_SET(osfd, &rd_wr); + if (fd_type == READ_FD) { + rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv); + } + else { + rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv); + } + if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { + _PR_MD_MAP_SELECT_ERROR(syserror); + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = timeout; + FD_ZERO(&rd_wr); + do { + /* + * We block in _MD_SELECT for at most + * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, + * so that there is an upper limit on the delay + * before the interrupt bit is checked. + */ + wait_for_remaining = PR_TRUE; + tv.tv_sec = PR_IntervalToSeconds(remaining); + if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) { + wait_for_remaining = PR_FALSE; + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + } else { + tv.tv_usec = PR_IntervalToMicroseconds( + remaining - + PR_SecondsToInterval(tv.tv_sec)); + } + FD_SET(osfd, &rd_wr); + if (fd_type == READ_FD) { + rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv); + } + else { + rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv); + } + /* + * we don't consider EINTR a real error + */ + if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { + _PR_MD_MAP_SELECT_ERROR(syserror); + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + /* + * We loop again if _MD_SELECT timed out or got interrupted + * by a signal, and the timeout deadline has not passed yet. + */ + if (rv == 0 || (rv == -1 && syserror == EINTR)) { + /* + * If _MD_SELECT timed out, we know how much time + * we spent in blocking, so we can avoid a + * PR_IntervalNow() call. + */ + if (rv == 0) { + if (wait_for_remaining) { + now += remaining; + } else { + now += PR_SecondsToInterval(tv.tv_sec) + + PR_MicrosecondsToInterval(tv.tv_usec); + } + } else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= timeout) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } else { + remaining = timeout - elapsed; + } + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + } + return(rv); +} + +#else /* _PR_USE_POLL */ + +static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type, + PRIntervalTime timeout) +{ + PRInt32 rv = -1; + int msecs; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRInt32 syserror; + struct pollfd pfd; + + switch (timeout) { + case PR_INTERVAL_NO_WAIT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_INTERVAL_NO_TIMEOUT: + /* + * This is a special case of the 'default' case below. + * Please see the comments there. + */ + msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000; + pfd.fd = osfd; + if (fd_type == READ_FD) { + pfd.events = POLLIN; + } else { + pfd.events = POLLOUT; + } + do { + rv = _MD_POLL(&pfd, 1, msecs); + if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { + _PR_MD_MAP_POLL_ERROR(syserror); + break; + } + /* + * If POLLERR is set, don't process it; retry the operation + */ + if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) { + rv = -1; + _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents); + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = timeout; + pfd.fd = osfd; + if (fd_type == READ_FD) { + pfd.events = POLLIN; + } else { + pfd.events = POLLOUT; + } + do { + /* + * We block in _MD_POLL for at most + * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, + * so that there is an upper limit on the delay + * before the interrupt bit is checked. + */ + wait_for_remaining = PR_TRUE; + msecs = PR_IntervalToMilliseconds(remaining); + if (msecs > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) { + wait_for_remaining = PR_FALSE; + msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000; + } + rv = _MD_POLL(&pfd, 1, msecs); + /* + * we don't consider EINTR a real error + */ + if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { + _PR_MD_MAP_POLL_ERROR(syserror); + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + /* + * If POLLERR is set, don't process it; retry the operation + */ + if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) { + rv = -1; + _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents); + break; + } + /* + * We loop again if _MD_POLL timed out or got interrupted + * by a signal, and the timeout deadline has not passed yet. + */ + if (rv == 0 || (rv == -1 && syserror == EINTR)) { + /* + * If _MD_POLL timed out, we know how much time + * we spent in blocking, so we can avoid a + * PR_IntervalNow() call. + */ + if (rv == 0) { + if (wait_for_remaining) { + now += remaining; + } else { + now += PR_MillisecondsToInterval(msecs); + } + } else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= timeout) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } else { + remaining = timeout - elapsed; + } + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + } + return(rv); +} + +#endif /* _PR_USE_POLL */ + +static PRInt32 local_io_wait( + PRInt32 osfd, + PRInt32 wait_flag, + PRIntervalTime timeout) +{ + _PRUnixPollDesc pd; + PRInt32 rv; + + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("waiting to %s on osfd=%d", + (wait_flag == _PR_UNIX_POLL_READ) ? "read" : "write", + osfd)); + + if (timeout == PR_INTERVAL_NO_WAIT) { + return 0; + } + + pd.osfd = osfd; + pd.in_flags = wait_flag; + pd.out_flags = 0; + + rv = _PR_WaitForMultipleFDs(&pd, 1, timeout); + + if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + } + return rv; +} + + +PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRInt32 flags, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + /* + * Many OS's (Solaris, Unixware) have a broken recv which won't read + * from socketpairs. As long as we don't use flags on socketpairs, this + * is a decent fix. - mikep + */ +#if defined(UNIXWARE) || defined(SOLARIS) + while ((rv = read(osfd,buf,amount)) == -1) { +#else + while ((rv = recv(osfd,buf,amount,flags)) == -1) { +#endif + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd,_PR_UNIX_POLL_READ,timeout)) < 0) { + goto done; + } + } else { + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { + goto done; + } + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_RECV_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, + PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((*addrlen = PR_NETADDR_SIZE(addr)), + ((rv = recvfrom(osfd, buf, amount, flags, + (struct sockaddr *) addr, (_PRSockLen_t *)addrlen)) == -1)) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) { + goto done; + } + } else { + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { + goto done; + } + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_RECVFROM_ERROR(err); + } +done: +#ifdef _PR_HAVE_SOCKADDR_LEN + if (rv != -1) { + /* ignore the sa_len field of struct sockaddr */ + if (addr) { + addr->raw.family = ((struct sockaddr *) addr)->sa_family; + } + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ + return(rv); +} + +PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRInt32 flags, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); +#if defined(SOLARIS) + PRInt32 tmp_amount = amount; +#endif + + /* + * On pre-2.6 Solaris, send() is much slower than write(). + * On 2.6 and beyond, with in-kernel sockets, send() and + * write() are fairly equivalent in performance. + */ +#if defined(SOLARIS) + PR_ASSERT(0 == flags); + while ((rv = write(osfd,buf,tmp_amount)) == -1) { +#else + while ((rv = send(osfd,buf,amount,flags)) == -1) { +#endif + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { + goto done; + } + } else { + if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) { + goto done; + } + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { +#if defined(SOLARIS) + /* + * The write system call has been reported to return the ERANGE + * error on occasion. Try to write in smaller chunks to workaround + * this bug. + */ + if (err == ERANGE) { + if (tmp_amount > 1) { + tmp_amount = tmp_amount/2; /* half the bytes */ + continue; + } + } +#endif + break; + } + } + /* + * optimization; if bytes sent is less than "amount" call + * select before returning. This is because it is likely that + * the next send() call will return EWOULDBLOCK. + */ + if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) + && (timeout != PR_INTERVAL_NO_WAIT)) { + if (_PR_IS_NATIVE_THREAD(me)) { + if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) { + rv = -1; + goto done; + } + } else { + if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) { + rv = -1; + goto done; + } + } + } + if (rv < 0) { + _PR_MD_MAP_SEND_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _MD_sendto( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); +#ifdef _PR_HAVE_SOCKADDR_LEN + PRNetAddr addrCopy; + + addrCopy = *addr; + ((struct sockaddr *) &addrCopy)->sa_len = addrlen; + ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; + + while ((rv = sendto(osfd, buf, amount, flags, + (struct sockaddr *) &addrCopy, addrlen)) == -1) { +#else + while ((rv = sendto(osfd, buf, amount, flags, + (struct sockaddr *) addr, addrlen)) == -1) { +#endif + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { + goto done; + } + } else { + if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) { + goto done; + } + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_SENDTO_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _MD_writev( + PRFileDesc *fd, const PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 index, amount = 0; + PRInt32 osfd = fd->secret->md.osfd; + + /* + * Calculate the total number of bytes to be sent; needed for + * optimization later. + * We could avoid this if this number was passed in; but it is + * probably not a big deal because iov_size is usually small (less than + * 3) + */ + if (!fd->secret->nonblocking) { + for (index=0; index<iov_size; index++) { + amount += iov[index].iov_len; + } + } + + while ((rv = writev(osfd, (const struct iovec*)iov, iov_size)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { + goto done; + } + } else { + if ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) { + goto done; + } + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + /* + * optimization; if bytes sent is less than "amount" call + * select before returning. This is because it is likely that + * the next writev() call will return EWOULDBLOCK. + */ + if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) + && (timeout != PR_INTERVAL_NO_WAIT)) { + if (_PR_IS_NATIVE_THREAD(me)) { + if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) { + rv = -1; + goto done; + } + } else { + if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) { + rv = -1; + goto done; + } + } + } + if (rv < 0) { + _PR_MD_MAP_WRITEV_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = accept(osfd, (struct sockaddr *) addr, + (_PRSockLen_t *)addrlen)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == ECONNABORTED)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) { + goto done; + } + } else { + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) { + goto done; + } + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) { + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_ACCEPT_ERROR(err); + } +done: +#ifdef _PR_HAVE_SOCKADDR_LEN + if (rv != -1) { + /* ignore the sa_len field of struct sockaddr */ + if (addr) { + addr->raw.family = ((struct sockaddr *) addr)->sa_family; + } + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ + return(rv); +} + +extern int _connect (int s, const struct sockaddr *name, int namelen); +PRInt32 _MD_connect( + PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 osfd = fd->secret->md.osfd; +#ifdef _PR_HAVE_SOCKADDR_LEN + PRNetAddr addrCopy; + + addrCopy = *addr; + ((struct sockaddr *) &addrCopy)->sa_len = addrlen; + ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; +#endif + + /* + * We initiate the connection setup by making a nonblocking connect() + * call. If the connect() call fails, there are two cases we handle + * specially: + * 1. The connect() call was interrupted by a signal. In this case + * we simply retry connect(). + * 2. The NSPR socket is nonblocking and connect() fails with + * EINPROGRESS. We first wait until the socket becomes writable. + * Then we try to find out whether the connection setup succeeded + * or failed. + */ + +retry: +#ifdef _PR_HAVE_SOCKADDR_LEN + if ((rv = connect(osfd, (struct sockaddr *)&addrCopy, addrlen)) == -1) { +#else + if ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) { +#endif + err = _MD_ERRNO(); + + if (err == EINTR) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + goto retry; + } + + if (!fd->secret->nonblocking && (err == EINPROGRESS)) { + if (!_PR_IS_NATIVE_THREAD(me)) { + + if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) { + return -1; + } + } else { + /* + * socket_io_wait() may return -1 or 1. + */ + + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if (rv == -1) { + return -1; + } + } + + PR_ASSERT(rv == 1); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } + return 0; + } + + _PR_MD_MAP_CONNECT_ERROR(err); + } + + return rv; +} /* _MD_connect */ + +PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) +{ + PRInt32 rv, err; +#ifdef _PR_HAVE_SOCKADDR_LEN + PRNetAddr addrCopy; + + addrCopy = *addr; + ((struct sockaddr *) &addrCopy)->sa_len = addrlen; + ((struct sockaddr *) &addrCopy)->sa_family = addr->raw.family; + rv = bind(fd->secret->md.osfd, (struct sockaddr *) &addrCopy, (int )addrlen); +#else + rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen); +#endif + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_BIND_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 rv, err; + + rv = listen(fd->secret->md.osfd, backlog); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_LISTEN_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how) +{ + PRInt32 rv, err; + + rv = shutdown(fd->secret->md.osfd, how); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SHUTDOWN_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_socketpair(int af, int type, int flags, + PRInt32 *osfd) +{ + PRInt32 rv, err; + + rv = socketpair(af, type, flags, osfd); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SOCKETPAIR_ERROR(err); + } + return rv; +} + +PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen) +{ + PRInt32 rv, err; + + rv = getsockname(fd->secret->md.osfd, + (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); +#ifdef _PR_HAVE_SOCKADDR_LEN + if (rv == 0) { + /* ignore the sa_len field of struct sockaddr */ + if (addr) { + addr->raw.family = ((struct sockaddr *) addr)->sa_family; + } + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_GETSOCKNAME_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen) +{ + PRInt32 rv, err; + + rv = getpeername(fd->secret->md.osfd, + (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); +#ifdef _PR_HAVE_SOCKADDR_LEN + if (rv == 0) { + /* ignore the sa_len field of struct sockaddr */ + if (addr) { + addr->raw.family = ((struct sockaddr *) addr)->sa_family; + } + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_GETPEERNAME_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, + PRInt32 optname, char* optval, PRInt32* optlen) +{ + PRInt32 rv, err; + + rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (_PRSockLen_t *)optlen); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_GETSOCKOPT_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, + PRInt32 optname, const char* optval, PRInt32 optlen) +{ + PRInt32 rv, err; + + rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_set_fd_inheritable(PRFileDesc *fd, PRBool inheritable) +{ + int rv; + + rv = fcntl(fd->secret->md.osfd, F_SETFD, inheritable ? 0 : FD_CLOEXEC); + if (-1 == rv) { + PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +void _MD_init_fd_inheritable(PRFileDesc *fd, PRBool imported) +{ + if (imported) { + fd->secret->inheritable = _PR_TRI_UNKNOWN; + } else { + /* By default, a Unix fd is not closed on exec. */ +#ifdef DEBUG + { + int flags = fcntl(fd->secret->md.osfd, F_GETFD, 0); + PR_ASSERT(0 == flags); + } +#endif + fd->secret->inheritable = _PR_TRI_TRUE; + } +} + +/************************************************************************/ +#if !defined(_PR_USE_POLL) + +/* +** Scan through io queue and find any bad fd's that triggered the error +** from _MD_SELECT +*/ +static void FindBadFDs(void) +{ + PRCList *q; + PRThread *me = _MD_CURRENT_THREAD(); + + PR_ASSERT(!_PR_IS_NATIVE_THREAD(me)); + q = (_PR_IOQ(me->cpu)).next; + _PR_IOQ_MAX_OSFD(me->cpu) = -1; + _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT; + while (q != &_PR_IOQ(me->cpu)) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + PRBool notify = PR_FALSE; + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + PRInt32 pq_max_osfd = -1; + + q = q->next; + for (; pds < epds; pds++) { + PRInt32 osfd = pds->osfd; + pds->out_flags = 0; + PR_ASSERT(osfd >= 0 || pds->in_flags == 0); + if (pds->in_flags == 0) { + continue; /* skip this fd */ + } + if (fcntl(osfd, F_GETFL, 0) == -1) { + /* Found a bad descriptor, remove it from the fd_sets. */ + PR_LOG(_pr_io_lm, PR_LOG_MAX, + ("file descriptor %d is bad", osfd)); + pds->out_flags = _PR_UNIX_POLL_NVAL; + notify = PR_TRUE; + } + if (osfd > pq_max_osfd) { + pq_max_osfd = osfd; + } + } + + if (notify) { + PRIntn pri; + PR_REMOVE_LINK(&pq->links); + pq->on_ioq = PR_FALSE; + + /* + * Decrement the count of descriptors for each desciptor/event + * because this I/O request is being removed from the + * ioq + */ + pds = pq->pds; + for (; pds < epds; pds++) { + PRInt32 osfd = pds->osfd; + PRInt16 in_flags = pds->in_flags; + PR_ASSERT(osfd >= 0 || in_flags == 0); + if (in_flags & _PR_UNIX_POLL_READ) { + if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu)); + } + } + if (in_flags & _PR_UNIX_POLL_WRITE) { + if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu)); + } + } + if (in_flags & _PR_UNIX_POLL_EXCEPT) { + if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); + } + } + } + + _PR_THREAD_LOCK(pq->thr); + if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { + _PRCPU *cpu = pq->thr->cpu; + _PR_SLEEPQ_LOCK(pq->thr->cpu); + _PR_DEL_SLEEPQ(pq->thr, PR_TRUE); + _PR_SLEEPQ_UNLOCK(pq->thr->cpu); + + if (pq->thr->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; + * a Resume operation on the thread + * will move it to the runQ + */ + pq->thr->state = _PR_SUSPENDED; + _PR_MISCQ_LOCK(pq->thr->cpu); + _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu); + _PR_MISCQ_UNLOCK(pq->thr->cpu); + } else { + pri = pq->thr->priority; + pq->thr->state = _PR_RUNNABLE; + + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(pq->thr, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + } + } + _PR_THREAD_UNLOCK(pq->thr); + } else { + if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) { + _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout; + } + if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) { + _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd; + } + } + } + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) { + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + } + } +} +#endif /* !defined(_PR_USE_POLL) */ + +/************************************************************************/ + +/* +** Called by the scheduler when there is nothing to do. This means that +** all threads are blocked on some monitor somewhere. +** +** Note: this code doesn't release the scheduler lock. +*/ +/* +** Pause the current CPU. longjmp to the cpu's pause stack +** +** This must be called with the scheduler locked +*/ +void _MD_PauseCPU(PRIntervalTime ticks) +{ + PRThread *me = _MD_CURRENT_THREAD(); +#ifdef _PR_USE_POLL + int timeout; + struct pollfd *pollfds; /* an array of pollfd structures */ + struct pollfd *pollfdPtr; /* a pointer that steps through the array */ + unsigned long npollfds; /* number of pollfd structures in array */ + unsigned long pollfds_size; + int nfd; /* to hold the return value of poll() */ +#else + struct timeval timeout, *tvp; + fd_set r, w, e; + fd_set *rp, *wp, *ep; + PRInt32 max_osfd, nfd; +#endif /* _PR_USE_POLL */ + PRInt32 rv; + PRCList *q; + PRUint32 min_timeout; + sigset_t oldset; + + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + + _PR_MD_IOQ_LOCK(); + +#ifdef _PR_USE_POLL + /* Build up the pollfd structure array to wait on */ + + /* Find out how many pollfd structures are needed */ + npollfds = _PR_IOQ_OSFD_CNT(me->cpu); + PR_ASSERT(npollfds >= 0); + + /* + * We use a pipe to wake up a native thread. An fd is needed + * for the pipe and we poll it for reading. + */ + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + npollfds++; + } + + /* + * if the cpu's pollfd array is not big enough, release it and allocate a new one + */ + if (npollfds > _PR_IOQ_POLLFDS_SIZE(me->cpu)) { + if (_PR_IOQ_POLLFDS(me->cpu) != NULL) { + PR_DELETE(_PR_IOQ_POLLFDS(me->cpu)); + } + pollfds_size = PR_MAX(_PR_IOQ_MIN_POLLFDS_SIZE(me->cpu), npollfds); + pollfds = (struct pollfd *) PR_MALLOC(pollfds_size * sizeof(struct pollfd)); + _PR_IOQ_POLLFDS(me->cpu) = pollfds; + _PR_IOQ_POLLFDS_SIZE(me->cpu) = pollfds_size; + } else { + pollfds = _PR_IOQ_POLLFDS(me->cpu); + } + pollfdPtr = pollfds; + + /* + * If we need to poll the pipe for waking up a native thread, + * the pipe's fd is the first element in the pollfds array. + */ + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + pollfdPtr->fd = _pr_md_pipefd[0]; + pollfdPtr->events = POLLIN; + pollfdPtr++; + } + + min_timeout = PR_INTERVAL_NO_TIMEOUT; + for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + + if (pq->timeout < min_timeout) { + min_timeout = pq->timeout; + } + for (; pds < epds; pds++, pollfdPtr++) { + /* + * Assert that the pollfdPtr pointer does not go + * beyond the end of the pollfds array + */ + PR_ASSERT(pollfdPtr < pollfds + npollfds); + pollfdPtr->fd = pds->osfd; + /* direct copy of poll flags */ + pollfdPtr->events = pds->in_flags; + } + } + _PR_IOQ_TIMEOUT(me->cpu) = min_timeout; +#else + /* + * assigment of fd_sets + */ + r = _PR_FD_READ_SET(me->cpu); + w = _PR_FD_WRITE_SET(me->cpu); + e = _PR_FD_EXCEPTION_SET(me->cpu); + + rp = &r; + wp = &w; + ep = &e; + + max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1; + min_timeout = _PR_IOQ_TIMEOUT(me->cpu); +#endif /* _PR_USE_POLL */ + /* + ** Compute the minimum timeout value: make it the smaller of the + ** timeouts specified by the i/o pollers or the timeout of the first + ** sleeping thread. + */ + q = _PR_SLEEPQ(me->cpu).next; + + if (q != &_PR_SLEEPQ(me->cpu)) { + PRThread *t = _PR_THREAD_PTR(q); + + if (t->sleep < min_timeout) { + min_timeout = t->sleep; + } + } + if (min_timeout > ticks) { + min_timeout = ticks; + } + +#ifdef _PR_USE_POLL + if (min_timeout == PR_INTERVAL_NO_TIMEOUT) { + timeout = -1; + } + else { + timeout = PR_IntervalToMilliseconds(min_timeout); + } +#else + if (min_timeout == PR_INTERVAL_NO_TIMEOUT) { + tvp = NULL; + } else { + timeout.tv_sec = PR_IntervalToSeconds(min_timeout); + timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout) + % PR_USEC_PER_SEC; + tvp = &timeout; + } +#endif /* _PR_USE_POLL */ + + _PR_MD_IOQ_UNLOCK(); + _MD_CHECK_FOR_EXIT(); + /* + * check for i/o operations + */ +#ifndef _PR_NO_CLOCK_TIMER + /* + * Disable the clock interrupts while we are in select, if clock interrupts + * are enabled. Otherwise, when the select/poll calls are interrupted, the + * timer value starts ticking from zero again when the system call is restarted. + */ + if (!_nspr_noclock) { + PR_ASSERT(sigismember(&timer_set, SIGALRM)); + } + sigprocmask(SIG_BLOCK, &timer_set, &oldset); +#endif /* !_PR_NO_CLOCK_TIMER */ + +#ifndef _PR_USE_POLL + PR_ASSERT(FD_ISSET(_pr_md_pipefd[0],rp)); + nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp); +#else + nfd = _MD_POLL(pollfds, npollfds, timeout); +#endif /* !_PR_USE_POLL */ + +#ifndef _PR_NO_CLOCK_TIMER + if (!_nspr_noclock) { + sigprocmask(SIG_SETMASK, &oldset, 0); + } +#endif /* !_PR_NO_CLOCK_TIMER */ + + _MD_CHECK_FOR_EXIT(); + + _PR_MD_primordial_cpu(); + + _PR_MD_IOQ_LOCK(); + /* + ** Notify monitors that are associated with the selected descriptors. + */ +#ifdef _PR_USE_POLL + if (nfd > 0) { + pollfdPtr = pollfds; + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + /* + * Assert that the pipe is the first element in the + * pollfds array. + */ + PR_ASSERT(pollfds[0].fd == _pr_md_pipefd[0]); + if ((pollfds[0].revents & POLLIN) && (nfd == 1)) { + /* + * woken up by another thread; read all the data + * in the pipe to empty the pipe + */ + while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, + PIPE_BUF)) == PIPE_BUF) { + } + PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN))); + } + pollfdPtr++; + } + for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + PRBool notify = PR_FALSE; + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + + for (; pds < epds; pds++, pollfdPtr++) { + /* + * Assert that the pollfdPtr pointer does not go beyond + * the end of the pollfds array. + */ + PR_ASSERT(pollfdPtr < pollfds + npollfds); + /* + * Assert that the fd's in the pollfds array (stepped + * through by pollfdPtr) are in the same order as + * the fd's in _PR_IOQ() (stepped through by q and pds). + * This is how the pollfds array was created earlier. + */ + PR_ASSERT(pollfdPtr->fd == pds->osfd); + pds->out_flags = pollfdPtr->revents; + /* Negative fd's are ignored by poll() */ + if (pds->osfd >= 0 && pds->out_flags) { + notify = PR_TRUE; + } + } + if (notify) { + PRIntn pri; + PRThread *thred; + + PR_REMOVE_LINK(&pq->links); + pq->on_ioq = PR_FALSE; + + thred = pq->thr; + _PR_THREAD_LOCK(thred); + if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { + _PRCPU *cpu = pq->thr->cpu; + _PR_SLEEPQ_LOCK(pq->thr->cpu); + _PR_DEL_SLEEPQ(pq->thr, PR_TRUE); + _PR_SLEEPQ_UNLOCK(pq->thr->cpu); + + if (pq->thr->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; + * a Resume operation on the thread + * will move it to the runQ + */ + pq->thr->state = _PR_SUSPENDED; + _PR_MISCQ_LOCK(pq->thr->cpu); + _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu); + _PR_MISCQ_UNLOCK(pq->thr->cpu); + } else { + pri = pq->thr->priority; + pq->thr->state = _PR_RUNNABLE; + + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(pq->thr, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + if (_pr_md_idle_cpus > 1) { + _PR_MD_WAKEUP_WAITER(thred); + } + } + } + _PR_THREAD_UNLOCK(thred); + _PR_IOQ_OSFD_CNT(me->cpu) -= pq->npds; + PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0); + } + } + } else if (nfd == -1) { + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno)); + } + +#else + if (nfd > 0) { + q = _PR_IOQ(me->cpu).next; + _PR_IOQ_MAX_OSFD(me->cpu) = -1; + _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT; + while (q != &_PR_IOQ(me->cpu)) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + PRBool notify = PR_FALSE; + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + PRInt32 pq_max_osfd = -1; + + q = q->next; + for (; pds < epds; pds++) { + PRInt32 osfd = pds->osfd; + PRInt16 in_flags = pds->in_flags; + PRInt16 out_flags = 0; + PR_ASSERT(osfd >= 0 || in_flags == 0); + if ((in_flags & _PR_UNIX_POLL_READ) && FD_ISSET(osfd, rp)) { + out_flags |= _PR_UNIX_POLL_READ; + } + if ((in_flags & _PR_UNIX_POLL_WRITE) && FD_ISSET(osfd, wp)) { + out_flags |= _PR_UNIX_POLL_WRITE; + } + if ((in_flags & _PR_UNIX_POLL_EXCEPT) && FD_ISSET(osfd, ep)) { + out_flags |= _PR_UNIX_POLL_EXCEPT; + } + pds->out_flags = out_flags; + if (out_flags) { + notify = PR_TRUE; + } + if (osfd > pq_max_osfd) { + pq_max_osfd = osfd; + } + } + if (notify == PR_TRUE) { + PRIntn pri; + PRThread *thred; + + PR_REMOVE_LINK(&pq->links); + pq->on_ioq = PR_FALSE; + + /* + * Decrement the count of descriptors for each desciptor/event + * because this I/O request is being removed from the + * ioq + */ + pds = pq->pds; + for (; pds < epds; pds++) { + PRInt32 osfd = pds->osfd; + PRInt16 in_flags = pds->in_flags; + PR_ASSERT(osfd >= 0 || in_flags == 0); + if (in_flags & _PR_UNIX_POLL_READ) { + if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu)); + } + } + if (in_flags & _PR_UNIX_POLL_WRITE) { + if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu)); + } + } + if (in_flags & _PR_UNIX_POLL_EXCEPT) { + if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); + } + } + } + + /* + * Because this thread can run on a different cpu right + * after being added to the run queue, do not dereference + * pq + */ + thred = pq->thr; + _PR_THREAD_LOCK(thred); + if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { + _PRCPU *cpu = thred->cpu; + _PR_SLEEPQ_LOCK(pq->thr->cpu); + _PR_DEL_SLEEPQ(pq->thr, PR_TRUE); + _PR_SLEEPQ_UNLOCK(pq->thr->cpu); + + if (pq->thr->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; + * a Resume operation on the thread + * will move it to the runQ + */ + pq->thr->state = _PR_SUSPENDED; + _PR_MISCQ_LOCK(pq->thr->cpu); + _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu); + _PR_MISCQ_UNLOCK(pq->thr->cpu); + } else { + pri = pq->thr->priority; + pq->thr->state = _PR_RUNNABLE; + + pq->thr->cpu = cpu; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(pq->thr, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + if (_pr_md_idle_cpus > 1) { + _PR_MD_WAKEUP_WAITER(thred); + } + } + } + _PR_THREAD_UNLOCK(thred); + } else { + if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) { + _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout; + } + if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) { + _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd; + } + } + } + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + if ((FD_ISSET(_pr_md_pipefd[0], rp)) && (nfd == 1)) { + /* + * woken up by another thread; read all the data + * in the pipe to empty the pipe + */ + while ((rv = + read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) + == PIPE_BUF) { + } + PR_ASSERT((rv > 0) || + ((rv == -1) && (errno == EAGAIN))); + } + if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) { + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + } + } + } else if (nfd < 0) { + if (errno == EBADF) { + FindBadFDs(); + } else { + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d", + errno)); + } + } else { + PR_ASSERT(nfd == 0); + /* + * compute the new value of _PR_IOQ_TIMEOUT + */ + q = _PR_IOQ(me->cpu).next; + _PR_IOQ_MAX_OSFD(me->cpu) = -1; + _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT; + while (q != &_PR_IOQ(me->cpu)) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + PRInt32 pq_max_osfd = -1; + + q = q->next; + for (; pds < epds; pds++) { + if (pds->osfd > pq_max_osfd) { + pq_max_osfd = pds->osfd; + } + } + if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) { + _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout; + } + if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) { + _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd; + } + } + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) { + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + } + } + } +#endif /* _PR_USE_POLL */ + _PR_MD_IOQ_UNLOCK(); +} + +void _MD_Wakeup_CPUs() +{ + PRInt32 rv, data; + + data = 0; + rv = write(_pr_md_pipefd[1], &data, 1); + + while ((rv < 0) && (errno == EAGAIN)) { + /* + * pipe full, read all data in pipe to empty it + */ + while ((rv = + read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) + == PIPE_BUF) { + } + PR_ASSERT((rv > 0) || + ((rv == -1) && (errno == EAGAIN))); + rv = write(_pr_md_pipefd[1], &data, 1); + } +} + + +void _MD_InitCPUS() +{ + PRInt32 rv, flags; + PRThread *me = _MD_CURRENT_THREAD(); + + rv = pipe(_pr_md_pipefd); + PR_ASSERT(rv == 0); + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; +#ifndef _PR_USE_POLL + FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu)); +#endif + + flags = fcntl(_pr_md_pipefd[0], F_GETFL, 0); + fcntl(_pr_md_pipefd[0], F_SETFL, flags | O_NONBLOCK); + flags = fcntl(_pr_md_pipefd[1], F_GETFL, 0); + fcntl(_pr_md_pipefd[1], F_SETFL, flags | O_NONBLOCK); +} + +/* +** Unix SIGALRM (clock) signal handler +*/ +static void ClockInterruptHandler() +{ + int olderrno; + PRUintn pri; + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + PRThread *me = _MD_CURRENT_THREAD(); + +#ifdef SOLARIS + if (!me || _PR_IS_NATIVE_THREAD(me)) { + _pr_primordialCPU->u.missed[_pr_primordialCPU->where] |= _PR_MISSED_CLOCK; + return; + } +#endif + + if (_PR_MD_GET_INTSOFF() != 0) { + cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK; + return; + } + _PR_MD_SET_INTSOFF(1); + + olderrno = errno; + _PR_ClockInterrupt(); + errno = olderrno; + + /* + ** If the interrupt wants a resched or if some other thread at + ** the same priority needs the cpu, reschedule. + */ + pri = me->priority; + if ((cpu->u.missed[3] || (_PR_RUNQREADYMASK(me->cpu) >> pri))) { +#ifdef _PR_NO_PREEMPT + cpu->resched = PR_TRUE; + if (pr_interruptSwitchHook) { + (*pr_interruptSwitchHook)(pr_interruptSwitchHookArg); + } +#else /* _PR_NO_PREEMPT */ + /* + ** Re-enable unix interrupts (so that we can use + ** setjmp/longjmp for context switching without having to + ** worry about the signal state) + */ + sigprocmask(SIG_SETMASK, &empty_set, 0); + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock caused context switch")); + + if(!(me->flags & _PR_IDLE_THREAD)) { + _PR_THREAD_LOCK(me); + me->state = _PR_RUNNABLE; + me->cpu = cpu; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(me, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(me); + } else { + me->state = _PR_RUNNABLE; + } + _MD_SWITCH_CONTEXT(me); + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock back from context switch")); +#endif /* _PR_NO_PREEMPT */ + } + /* + * Because this thread could be running on a different cpu after + * a context switch the current cpu should be accessed and the + * value of the 'cpu' variable should not be used. + */ + _PR_MD_SET_INTSOFF(0); +} + +/* + * On HP-UX 9, we have to use the sigvector() interface to restart + * interrupted system calls, because sigaction() does not have the + * SA_RESTART flag. + */ + +#ifdef HPUX9 +static void HPUX9_ClockInterruptHandler( + int sig, + int code, + struct sigcontext *scp) +{ + ClockInterruptHandler(); + scp->sc_syscall_action = SIG_RESTART; +} +#endif /* HPUX9 */ + +/* # of milliseconds per clock tick that we will use */ +#define MSEC_PER_TICK 50 + + +void _MD_StartInterrupts() +{ + char *eval; + + if ((eval = getenv("NSPR_NOCLOCK")) != NULL) { + if (atoi(eval) == 0) { + _nspr_noclock = 0; + } + else { + _nspr_noclock = 1; + } + } + +#ifndef _PR_NO_CLOCK_TIMER + if (!_nspr_noclock) { + _MD_EnableClockInterrupts(); + } +#endif +} + +void _MD_StopInterrupts() +{ + sigprocmask(SIG_BLOCK, &timer_set, 0); +} + +void _MD_EnableClockInterrupts() +{ + struct itimerval itval; + extern PRUintn _pr_numCPU; +#ifdef HPUX9 + struct sigvec vec; + + vec.sv_handler = (void (*)()) HPUX9_ClockInterruptHandler; + vec.sv_mask = 0; + vec.sv_flags = 0; + sigvector(SIGALRM, &vec, 0); +#else + struct sigaction vtact; + + vtact.sa_handler = (void (*)()) ClockInterruptHandler; + sigemptyset(&vtact.sa_mask); + vtact.sa_flags = SA_RESTART; + sigaction(SIGALRM, &vtact, 0); +#endif /* HPUX9 */ + + PR_ASSERT(_pr_numCPU == 1); + itval.it_interval.tv_sec = 0; + itval.it_interval.tv_usec = MSEC_PER_TICK * PR_USEC_PER_MSEC; + itval.it_value = itval.it_interval; + setitimer(ITIMER_REAL, &itval, 0); +} + +void _MD_DisableClockInterrupts() +{ + struct itimerval itval; + extern PRUintn _pr_numCPU; + + PR_ASSERT(_pr_numCPU == 1); + itval.it_interval.tv_sec = 0; + itval.it_interval.tv_usec = 0; + itval.it_value = itval.it_interval; + setitimer(ITIMER_REAL, &itval, 0); +} + +void _MD_BlockClockInterrupts() +{ + sigprocmask(SIG_BLOCK, &timer_set, 0); +} + +void _MD_UnblockClockInterrupts() +{ + sigprocmask(SIG_UNBLOCK, &timer_set, 0); +} + +void _MD_MakeNonblock(PRFileDesc *fd) +{ + PRInt32 osfd = fd->secret->md.osfd; + int flags; + + if (osfd <= 2) { + /* Don't mess around with stdin, stdout or stderr */ + return; + } + flags = fcntl(osfd, F_GETFL, 0); + + /* + * Use O_NONBLOCK (POSIX-style non-blocking I/O) whenever possible. + * On SunOS 4, we must use FNDELAY (BSD-style non-blocking I/O), + * otherwise connect() still blocks and can be interrupted by SIGALRM. + */ + + fcntl(osfd, F_SETFL, flags | O_NONBLOCK); +} + +PRInt32 _MD_open(const char *name, PRIntn flags, PRIntn mode) +{ + PRInt32 osflags; + PRInt32 rv, err; + + if (flags & PR_RDWR) { + osflags = O_RDWR; + } else if (flags & PR_WRONLY) { + osflags = O_WRONLY; + } else { + osflags = O_RDONLY; + } + + if (flags & PR_EXCL) { + osflags |= O_EXCL; + } + if (flags & PR_APPEND) { + osflags |= O_APPEND; + } + if (flags & PR_TRUNCATE) { + osflags |= O_TRUNC; + } + if (flags & PR_SYNC) { +#if defined(O_SYNC) + osflags |= O_SYNC; +#elif defined(O_FSYNC) + osflags |= O_FSYNC; +#else +#error "Neither O_SYNC nor O_FSYNC is defined on this platform" +#endif + } + + /* + ** On creations we hold the 'create' lock in order to enforce + ** the semantics of PR_Rename. (see the latter for more details) + */ + if (flags & PR_CREATE_FILE) + { + osflags |= O_CREAT; + if (NULL !=_pr_unix_rename_lock) { + PR_Lock(_pr_unix_rename_lock); + } + } + +#if defined(ANDROID) + osflags |= O_LARGEFILE; +#endif + + rv = _md_iovector._open64(name, osflags, mode); + + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_OPEN_ERROR(err); + } + + if ((flags & PR_CREATE_FILE) && (NULL !=_pr_unix_rename_lock)) { + PR_Unlock(_pr_unix_rename_lock); + } + return rv; +} + +PRIntervalTime intr_timeout_ticks; + +#if defined(SOLARIS) +static void sigsegvhandler() { + fprintf(stderr,"Received SIGSEGV\n"); + fflush(stderr); + pause(); +} + +static void sigaborthandler() { + fprintf(stderr,"Received SIGABRT\n"); + fflush(stderr); + pause(); +} + +static void sigbushandler() { + fprintf(stderr,"Received SIGBUS\n"); + fflush(stderr); + pause(); +} +#endif /* SOLARIS */ + +#endif /* !defined(_PR_PTHREADS) */ + +void _MD_query_fd_inheritable(PRFileDesc *fd) +{ + int flags; + + PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); + flags = fcntl(fd->secret->md.osfd, F_GETFD, 0); + PR_ASSERT(-1 != flags); + fd->secret->inheritable = (flags & FD_CLOEXEC) ? + _PR_TRI_FALSE : _PR_TRI_TRUE; +} + +PROffset32 _MD_lseek(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) +{ + PROffset32 rv, where; + + switch (whence) { + case PR_SEEK_SET: + where = SEEK_SET; + break; + case PR_SEEK_CUR: + where = SEEK_CUR; + break; + case PR_SEEK_END: + where = SEEK_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = -1; + goto done; + } + rv = lseek(fd->secret->md.osfd,offset,where); + if (rv == -1) + { + PRInt32 syserr = _MD_ERRNO(); + _PR_MD_MAP_LSEEK_ERROR(syserr); + } +done: + return(rv); +} + +PROffset64 _MD_lseek64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) +{ + PRInt32 where; + PROffset64 rv; + + switch (whence) + { + case PR_SEEK_SET: + where = SEEK_SET; + break; + case PR_SEEK_CUR: + where = SEEK_CUR; + break; + case PR_SEEK_END: + where = SEEK_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = minus_one; + goto done; + } + rv = _md_iovector._lseek64(fd->secret->md.osfd, offset, where); + if (LL_EQ(rv, minus_one)) + { + PRInt32 syserr = _MD_ERRNO(); + _PR_MD_MAP_LSEEK_ERROR(syserr); + } +done: + return rv; +} /* _MD_lseek64 */ + +/* +** _MD_set_fileinfo_times -- +** Set the modifyTime and creationTime of the PRFileInfo +** structure using the values in struct stat. +** +** _MD_set_fileinfo64_times -- +** Set the modifyTime and creationTime of the PRFileInfo64 +** structure using the values in _MDStat64. +*/ + +#if defined(_PR_STAT_HAS_ST_ATIM) +/* +** struct stat has st_atim, st_mtim, and st_ctim fields of +** type timestruc_t. +*/ +static void _MD_set_fileinfo_times( + const struct stat *sb, + PRFileInfo *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtim.tv_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtim.tv_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctim.tv_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctim.tv_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} + +static void _MD_set_fileinfo64_times( + const _MDStat64 *sb, + PRFileInfo64 *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtim.tv_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtim.tv_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctim.tv_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctim.tv_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} +#elif defined(_PR_STAT_HAS_ST_ATIM_UNION) +/* +** The st_atim, st_mtim, and st_ctim fields in struct stat are +** unions with a st__tim union member of type timestruc_t. +*/ +static void _MD_set_fileinfo_times( + const struct stat *sb, + PRFileInfo *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} + +static void _MD_set_fileinfo64_times( + const _MDStat64 *sb, + PRFileInfo64 *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} +#elif defined(_PR_STAT_HAS_ST_ATIMESPEC) +/* +** struct stat has st_atimespec, st_mtimespec, and st_ctimespec +** fields of type struct timespec. +*/ +#if defined(_PR_TIMESPEC_HAS_TS_SEC) +static void _MD_set_fileinfo_times( + const struct stat *sb, + PRFileInfo *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} + +static void _MD_set_fileinfo64_times( + const _MDStat64 *sb, + PRFileInfo64 *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} +#else /* _PR_TIMESPEC_HAS_TS_SEC */ +/* +** The POSIX timespec structure has tv_sec and tv_nsec. +*/ +static void _MD_set_fileinfo_times( + const struct stat *sb, + PRFileInfo *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} + +static void _MD_set_fileinfo64_times( + const _MDStat64 *sb, + PRFileInfo64 *info) +{ + PRInt64 us, s2us; + + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec); + LL_MUL(info->modifyTime, info->modifyTime, s2us); + LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000); + LL_ADD(info->modifyTime, info->modifyTime, us); + LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec); + LL_MUL(info->creationTime, info->creationTime, s2us); + LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000); + LL_ADD(info->creationTime, info->creationTime, us); +} +#endif /* _PR_TIMESPEC_HAS_TS_SEC */ +#elif defined(_PR_STAT_HAS_ONLY_ST_ATIME) +/* +** struct stat only has st_atime, st_mtime, and st_ctime fields +** of type time_t. +*/ +static void _MD_set_fileinfo_times( + const struct stat *sb, + PRFileInfo *info) +{ + PRInt64 s, s2us; + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, sb->st_mtime); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb->st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; +} + +static void _MD_set_fileinfo64_times( + const _MDStat64 *sb, + PRFileInfo64 *info) +{ + PRInt64 s, s2us; + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, sb->st_mtime); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb->st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; +} +#else +#error "I don't know yet" +#endif + +static int _MD_convert_stat_to_fileinfo( + const struct stat *sb, + PRFileInfo *info) +{ + if (S_IFREG & sb->st_mode) { + info->type = PR_FILE_FILE; + } + else if (S_IFDIR & sb->st_mode) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_OTHER; + } + +#if defined(_PR_HAVE_LARGE_OFF_T) + if (0x7fffffffL < sb->st_size) + { + PR_SetError(PR_FILE_TOO_BIG_ERROR, 0); + return -1; + } +#endif /* defined(_PR_HAVE_LARGE_OFF_T) */ + info->size = sb->st_size; + + _MD_set_fileinfo_times(sb, info); + return 0; +} /* _MD_convert_stat_to_fileinfo */ + +static int _MD_convert_stat64_to_fileinfo64( + const _MDStat64 *sb, + PRFileInfo64 *info) +{ + if (S_IFREG & sb->st_mode) { + info->type = PR_FILE_FILE; + } + else if (S_IFDIR & sb->st_mode) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_OTHER; + } + + LL_I2L(info->size, sb->st_size); + + _MD_set_fileinfo64_times(sb, info); + return 0; +} /* _MD_convert_stat64_to_fileinfo64 */ + +PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info) +{ + PRInt32 rv; + struct stat sb; + + rv = stat(fn, &sb); + if (rv < 0) { + _PR_MD_MAP_STAT_ERROR(_MD_ERRNO()); + } + else if (NULL != info) { + rv = _MD_convert_stat_to_fileinfo(&sb, info); + } + return rv; +} + +PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info) +{ + _MDStat64 sb; + PRInt32 rv = _md_iovector._stat64(fn, &sb); + if (rv < 0) { + _PR_MD_MAP_STAT_ERROR(_MD_ERRNO()); + } + else if (NULL != info) { + rv = _MD_convert_stat64_to_fileinfo64(&sb, info); + } + return rv; +} + +PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info) +{ + struct stat sb; + PRInt32 rv = fstat(fd->secret->md.osfd, &sb); + if (rv < 0) { + _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO()); + } + else if (NULL != info) { + rv = _MD_convert_stat_to_fileinfo(&sb, info); + } + return rv; +} + +PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info) +{ + _MDStat64 sb; + PRInt32 rv = _md_iovector._fstat64(fd->secret->md.osfd, &sb); + if (rv < 0) { + _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO()); + } + else if (NULL != info) { + rv = _MD_convert_stat64_to_fileinfo64(&sb, info); + } + return rv; +} + +/* + * _md_iovector._open64 must be initialized to 'open' so that _PR_InitLog can + * open the log file during NSPR initialization, before _md_iovector is + * initialized by _PR_MD_FINAL_INIT. This means the log file cannot be a + * large file on some platforms. + */ +struct _MD_IOVector _md_iovector = { open }; + +/* +** These implementations are to emulate large file routines on systems that +** don't have them. Their goal is to check in case overflow occurs. Otherwise +** they will just operate as normal using 32-bit file routines. +** +** The checking might be pre- or post-op, depending on the semantics. +*/ + +#if defined(SOLARIS2_5) + +static PRIntn _MD_solaris25_fstat64(PRIntn osfd, _MDStat64 *buf) +{ + PRInt32 rv; + struct stat sb; + + rv = fstat(osfd, &sb); + if (rv >= 0) + { + /* + ** I'm only copying the fields that are immediately needed. + ** If somebody else calls this function, some of the fields + ** may not be defined. + */ + (void)memset(buf, 0, sizeof(_MDStat64)); + buf->st_mode = sb.st_mode; + buf->st_ctim = sb.st_ctim; + buf->st_mtim = sb.st_mtim; + buf->st_size = sb.st_size; + } + return rv; +} /* _MD_solaris25_fstat64 */ + +static PRIntn _MD_solaris25_stat64(const char *fn, _MDStat64 *buf) +{ + PRInt32 rv; + struct stat sb; + + rv = stat(fn, &sb); + if (rv >= 0) + { + /* + ** I'm only copying the fields that are immediately needed. + ** If somebody else calls this function, some of the fields + ** may not be defined. + */ + (void)memset(buf, 0, sizeof(_MDStat64)); + buf->st_mode = sb.st_mode; + buf->st_ctim = sb.st_ctim; + buf->st_mtim = sb.st_mtim; + buf->st_size = sb.st_size; + } + return rv; +} /* _MD_solaris25_stat64 */ +#endif /* defined(SOLARIS2_5) */ + +#if defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) + +static PROffset64 _MD_Unix_lseek64(PRIntn osfd, PROffset64 offset, PRIntn whence) +{ + PRUint64 maxoff; + PROffset64 rv = minus_one; + LL_I2L(maxoff, 0x7fffffff); + if (LL_CMP(offset, <=, maxoff)) + { + off_t off; + LL_L2I(off, offset); + LL_I2L(rv, lseek(osfd, off, whence)); + } + else { + errno = EFBIG; /* we can't go there */ + } + return rv; +} /* _MD_Unix_lseek64 */ + +static void* _MD_Unix_mmap64( + void *addr, PRSize len, PRIntn prot, PRIntn flags, + PRIntn fildes, PRInt64 offset) +{ + PR_SetError(PR_FILE_TOO_BIG_ERROR, 0); + return NULL; +} /* _MD_Unix_mmap64 */ +#endif /* defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) */ + +/* NDK non-unified headers for API < 21 don't have mmap64. However, + * NDK unified headers do provide mmap64 for all API versions when building + * with clang. Therefore, we should provide mmap64 here for API < 21 if we're + * not using clang or if we're using non-unified headers. We check for + * non-unified headers by the lack of __ANDROID_API_L__ macro. */ +#if defined(ANDROID) && __ANDROID_API__ < 21 && \ + (!defined(__clang__) || !defined(__ANDROID_API_L__)) +PR_IMPORT(void) *__mmap2(void *, size_t, int, int, int, size_t); + +#define ANDROID_PAGE_SIZE 4096 + +static void * +mmap64(void *addr, size_t len, int prot, int flags, int fd, loff_t offset) +{ + if (offset & (ANDROID_PAGE_SIZE - 1)) { + errno = EINVAL; + return MAP_FAILED; + } + return __mmap2(addr, len, prot, flags, fd, offset / ANDROID_PAGE_SIZE); +} +#endif + +static void _PR_InitIOV(void) +{ +#if defined(SOLARIS2_5) + PRLibrary *lib; + void *open64_func; + + open64_func = PR_FindSymbolAndLibrary("open64", &lib); + if (NULL != open64_func) + { + PR_ASSERT(NULL != lib); + _md_iovector._open64 = (_MD_Open64)open64_func; + _md_iovector._mmap64 = (_MD_Mmap64)PR_FindSymbol(lib, "mmap64"); + _md_iovector._fstat64 = (_MD_Fstat64)PR_FindSymbol(lib, "fstat64"); + _md_iovector._stat64 = (_MD_Stat64)PR_FindSymbol(lib, "stat64"); + _md_iovector._lseek64 = (_MD_Lseek64)PR_FindSymbol(lib, "lseek64"); + (void)PR_UnloadLibrary(lib); + } + else + { + _md_iovector._open64 = open; + _md_iovector._mmap64 = _MD_Unix_mmap64; + _md_iovector._fstat64 = _MD_solaris25_fstat64; + _md_iovector._stat64 = _MD_solaris25_stat64; + _md_iovector._lseek64 = _MD_Unix_lseek64; + } +#elif defined(_PR_NO_LARGE_FILES) + _md_iovector._open64 = open; + _md_iovector._mmap64 = _MD_Unix_mmap64; + _md_iovector._fstat64 = fstat; + _md_iovector._stat64 = stat; + _md_iovector._lseek64 = _MD_Unix_lseek64; +#elif defined(_PR_HAVE_OFF64_T) +#if (defined(ANDROID) && __ANDROID_API__ < 21) + /* + * Android < 21 doesn't have open64. We pass the O_LARGEFILE flag to open + * in _MD_open. + */ + _md_iovector._open64 = open; +#else + _md_iovector._open64 = open64; +#endif + _md_iovector._mmap64 = mmap64; +#if (defined(ANDROID) && __ANDROID_API__ < 21) + /* Same as the open64 case for Android. */ + _md_iovector._fstat64 = (_MD_Fstat64)fstat; + _md_iovector._stat64 = (_MD_Stat64)stat; +#else + _md_iovector._fstat64 = fstat64; + _md_iovector._stat64 = stat64; +#endif + _md_iovector._lseek64 = lseek64; +#elif defined(_PR_HAVE_LARGE_OFF_T) + _md_iovector._open64 = open; + _md_iovector._mmap64 = mmap; + _md_iovector._fstat64 = fstat; + _md_iovector._stat64 = stat; + _md_iovector._lseek64 = lseek; +#else +#error "I don't know yet" +#endif + LL_I2L(minus_one, -1); +} /* _PR_InitIOV */ + +void _PR_UnixInit(void) +{ + struct sigaction sigact; + int rv; + + sigemptyset(&timer_set); + +#if !defined(_PR_PTHREADS) + + sigaddset(&timer_set, SIGALRM); + sigemptyset(&empty_set); + intr_timeout_ticks = + PR_SecondsToInterval(_PR_INTERRUPT_CHECK_INTERVAL_SECS); + +#if defined(SOLARIS) + + if (getenv("NSPR_SIGSEGV_HANDLE")) { + sigact.sa_handler = sigsegvhandler; + sigact.sa_flags = 0; + sigact.sa_mask = timer_set; + sigaction(SIGSEGV, &sigact, 0); + } + + if (getenv("NSPR_SIGABRT_HANDLE")) { + sigact.sa_handler = sigaborthandler; + sigact.sa_flags = 0; + sigact.sa_mask = timer_set; + sigaction(SIGABRT, &sigact, 0); + } + + if (getenv("NSPR_SIGBUS_HANDLE")) { + sigact.sa_handler = sigbushandler; + sigact.sa_flags = 0; + sigact.sa_mask = timer_set; + sigaction(SIGBUS, &sigact, 0); + } + +#endif +#endif /* !defined(_PR_PTHREADS) */ + + sigact.sa_handler = SIG_IGN; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + rv = sigaction(SIGPIPE, &sigact, 0); + PR_ASSERT(0 == rv); + + _pr_unix_rename_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_unix_rename_lock); + _pr_Xfe_mon = PR_NewMonitor(); + PR_ASSERT(NULL != _pr_Xfe_mon); + + _PR_InitIOV(); /* one last hack */ +} + +void _PR_UnixCleanup(void) +{ + if (_pr_unix_rename_lock) { + PR_DestroyLock(_pr_unix_rename_lock); + _pr_unix_rename_lock = NULL; + } + if (_pr_Xfe_mon) { + PR_DestroyMonitor(_pr_Xfe_mon); + _pr_Xfe_mon = NULL; + } +} + +#if !defined(_PR_PTHREADS) + +/* + * Variables used by the GC code, initialized in _MD_InitSegs(). + */ +static PRInt32 _pr_zero_fd = -1; +static PRLock *_pr_md_lock = NULL; + +/* + * _MD_InitSegs -- + * + * This is Unix's version of _PR_MD_INIT_SEGS(), which is + * called by _PR_InitSegs(), which in turn is called by + * PR_Init(). + */ +void _MD_InitSegs(void) +{ +#ifdef DEBUG + /* + ** Disable using mmap(2) if NSPR_NO_MMAP is set + */ + if (getenv("NSPR_NO_MMAP")) { + _pr_zero_fd = -2; + return; + } +#endif + _pr_zero_fd = open("/dev/zero",O_RDWR, 0); + /* Prevent the fd from being inherited by child processes */ + fcntl(_pr_zero_fd, F_SETFD, FD_CLOEXEC); + _pr_md_lock = PR_NewLock(); +} + +PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr) +{ + static char *lastaddr = (char*) _PR_STACK_VMBASE; + PRStatus retval = PR_SUCCESS; + int prot; + void *rv; + + PR_ASSERT(seg != 0); + PR_ASSERT(size != 0); + + PR_Lock(_pr_md_lock); + if (_pr_zero_fd < 0) { +from_heap: + seg->vaddr = PR_MALLOC(size); + if (!seg->vaddr) { + retval = PR_FAILURE; + } + else { + seg->size = size; + } + goto exit; + } + + prot = PROT_READ|PROT_WRITE; + /* + * On Alpha Linux, the user-level thread stack needs + * to be made executable because longjmp/signal seem + * to put machine instructions on the stack. + */ +#if defined(LINUX) && defined(__alpha) + prot |= PROT_EXEC; +#endif + rv = mmap((vaddr != 0) ? vaddr : lastaddr, size, prot, + _MD_MMAP_FLAGS, + _pr_zero_fd, 0); + if (rv == (void*)-1) { + goto from_heap; + } + lastaddr += size; + seg->vaddr = rv; + seg->size = size; + seg->flags = _PR_SEG_VM; + +exit: + PR_Unlock(_pr_md_lock); + return retval; +} + +void _MD_FreeSegment(PRSegment *seg) +{ + if (seg->flags & _PR_SEG_VM) { + (void) munmap(seg->vaddr, seg->size); + } + else { + PR_DELETE(seg->vaddr); + } +} + +#endif /* _PR_PTHREADS */ + +/* + *----------------------------------------------------------------------- + * + * PR_Now -- + * + * Returns the current time in microseconds since the epoch. + * The epoch is midnight January 1, 1970 GMT. + * The implementation is machine dependent. This is the Unix + * implementation. + * Cf. time_t time(time_t *tp) + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(PRTime) +PR_Now(void) +{ + struct timeval tv; + PRInt64 s, us, s2us; + + GETTIMEOFDAY(&tv); + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, tv.tv_sec); + LL_I2L(us, tv.tv_usec); + LL_MUL(s, s, s2us); + LL_ADD(s, s, us); + return s; +} + +#if defined(_MD_INTERVAL_USE_GTOD) +/* + * This version of interval times is based on the time of day + * capability offered by the system. This isn't valid for two reasons: + * 1) The time of day is neither linear nor montonically increasing + * 2) The units here are milliseconds. That's not appropriate for our use. + */ +PRIntervalTime _PR_UNIX_GetInterval() +{ + struct timeval time; + PRIntervalTime ticks; + + (void)GETTIMEOFDAY(&time); /* fallicy of course */ + ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; /* that's in milliseconds */ + ticks += (PRUint32)time.tv_usec / PR_USEC_PER_MSEC; /* so's that */ + return ticks; +} /* _PR_UNIX_GetInterval */ + +PRIntervalTime _PR_UNIX_TicksPerSecond() +{ + return 1000; /* this needs some work :) */ +} +#endif + +#if defined(_PR_HAVE_CLOCK_MONOTONIC) +PRIntervalTime _PR_UNIX_GetInterval2() +{ + struct timespec time; + PRIntervalTime ticks; + + if (clock_gettime(CLOCK_MONOTONIC, &time) != 0) { + fprintf(stderr, "clock_gettime failed: %d\n", errno); + abort(); + } + + ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; + ticks += (PRUint32)time.tv_nsec / PR_NSEC_PER_MSEC; + return ticks; +} + +PRIntervalTime _PR_UNIX_TicksPerSecond2() +{ + return 1000; +} +#endif + +#if !defined(_PR_PTHREADS) +/* + * Wait for I/O on multiple descriptors. + * + * Return 0 if timed out, return -1 if interrupted, + * else return the number of ready descriptors. + */ +PRInt32 _PR_WaitForMultipleFDs( + _PRUnixPollDesc *unixpds, + PRInt32 pdcnt, + PRIntervalTime timeout) +{ + PRPollQueue pq; + PRIntn is; + PRInt32 rv; + _PRCPU *io_cpu; + _PRUnixPollDesc *unixpd, *eunixpd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + + pq.pds = unixpds; + pq.npds = pdcnt; + + _PR_INTSOFF(is); + _PR_MD_IOQ_LOCK(); + _PR_THREAD_LOCK(me); + + pq.thr = me; + io_cpu = me->cpu; + pq.on_ioq = PR_TRUE; + pq.timeout = timeout; + _PR_ADD_TO_IOQ(pq, me->cpu); + +#if !defined(_PR_USE_POLL) + eunixpd = unixpds + pdcnt; + for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { + PRInt32 osfd = unixpd->osfd; + if (unixpd->in_flags & _PR_UNIX_POLL_READ) { + FD_SET(osfd, &_PR_FD_READ_SET(me->cpu)); + _PR_FD_READ_CNT(me->cpu)[osfd]++; + } + if (unixpd->in_flags & _PR_UNIX_POLL_WRITE) { + FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu)); + (_PR_FD_WRITE_CNT(me->cpu))[osfd]++; + } + if (unixpd->in_flags & _PR_UNIX_POLL_EXCEPT) { + FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); + (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++; + } + if (osfd > _PR_IOQ_MAX_OSFD(me->cpu)) { + _PR_IOQ_MAX_OSFD(me->cpu) = osfd; + } + } +#endif /* !defined(_PR_USE_POLL) */ + + if (_PR_IOQ_TIMEOUT(me->cpu) > timeout) { + _PR_IOQ_TIMEOUT(me->cpu) = timeout; + } + + _PR_IOQ_OSFD_CNT(me->cpu) += pdcnt; + + _PR_SLEEPQ_LOCK(me->cpu); + _PR_ADD_SLEEPQ(me, timeout); + me->state = _PR_IO_WAIT; + me->io_pending = PR_TRUE; + me->io_suspended = PR_FALSE; + _PR_SLEEPQ_UNLOCK(me->cpu); + _PR_THREAD_UNLOCK(me); + _PR_MD_IOQ_UNLOCK(); + + _PR_MD_WAIT(me, timeout); + + me->io_pending = PR_FALSE; + me->io_suspended = PR_FALSE; + + /* + * This thread should run on the same cpu on which it was blocked; when + * the IO request times out the fd sets and fd counts for the + * cpu are updated below. + */ + PR_ASSERT(me->cpu == io_cpu); + + /* + ** If we timed out the pollq might still be on the ioq. Remove it + ** before continuing. + */ + if (pq.on_ioq) { + _PR_MD_IOQ_LOCK(); + /* + * Need to check pq.on_ioq again + */ + if (pq.on_ioq) { + PR_REMOVE_LINK(&pq.links); +#ifndef _PR_USE_POLL + eunixpd = unixpds + pdcnt; + for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { + PRInt32 osfd = unixpd->osfd; + PRInt16 in_flags = unixpd->in_flags; + + if (in_flags & _PR_UNIX_POLL_READ) { + if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu)); + } + } + if (in_flags & _PR_UNIX_POLL_WRITE) { + if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu)); + } + } + if (in_flags & _PR_UNIX_POLL_EXCEPT) { + if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) { + FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu)); + } + } + } +#endif /* _PR_USE_POLL */ + PR_ASSERT(pq.npds == pdcnt); + _PR_IOQ_OSFD_CNT(me->cpu) -= pdcnt; + PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0); + } + _PR_MD_IOQ_UNLOCK(); + } + /* XXX Should we use _PR_FAST_INTSON or _PR_INTSON? */ + if (1 == pdcnt) { + _PR_FAST_INTSON(is); + } else { + _PR_INTSON(is); + } + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + + rv = 0; + if (pq.on_ioq == PR_FALSE) { + /* Count the number of ready descriptors */ + while (--pdcnt >= 0) { + if (unixpds->out_flags != 0) { + rv++; + } + unixpds++; + } + } + + return rv; +} + +/* + * Unblock threads waiting for I/O + * used when interrupting threads + * + * NOTE: The thread lock should held when this function is called. + * On return, the thread lock is released. + */ +void _PR_Unblock_IO_Wait(PRThread *thr) +{ + int pri = thr->priority; + _PRCPU *cpu = thr->cpu; + + /* + * GLOBAL threads wakeup periodically to check for interrupt + */ + if (_PR_IS_NATIVE_THREAD(thr)) { + _PR_THREAD_UNLOCK(thr); + return; + } + + PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ)); + _PR_SLEEPQ_LOCK(cpu); + _PR_DEL_SLEEPQ(thr, PR_TRUE); + _PR_SLEEPQ_UNLOCK(cpu); + + PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD)); + thr->state = _PR_RUNNABLE; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thr, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(thr); + _PR_MD_WAKEUP_WAITER(thr); +} +#endif /* !defined(_PR_PTHREADS) */ + +/* + * When a nonblocking connect has completed, determine whether it + * succeeded or failed, and if it failed, what the error code is. + * + * The function returns the error code. An error code of 0 means + * that the nonblocking connect succeeded. + */ + +int _MD_unix_get_nonblocking_connect_error(int osfd) +{ +#if defined(NTO) + /* Neutrino does not support the SO_ERROR socket option */ + PRInt32 rv; + PRNetAddr addr; + _PRSockLen_t addrlen = sizeof(addr); + + /* Test to see if we are using the Tiny TCP/IP Stack or the Full one. */ + struct statvfs superblock; + rv = fstatvfs(osfd, &superblock); + if (rv == 0) { + if (strcmp(superblock.f_basetype, "ttcpip") == 0) { + /* Using the Tiny Stack! */ + rv = getpeername(osfd, (struct sockaddr *) &addr, + (_PRSockLen_t *) &addrlen); + if (rv == -1) { + int errno_copy = errno; /* make a copy so I don't + * accidentally reset */ + + if (errno_copy == ENOTCONN) { + struct stat StatInfo; + rv = fstat(osfd, &StatInfo); + if (rv == 0) { + time_t current_time = time(NULL); + + /* + * this is a real hack, can't explain why it + * works it just does + */ + if (abs(current_time - StatInfo.st_atime) < 5) { + return ECONNREFUSED; + } else { + return ETIMEDOUT; + } + } else { + return ECONNREFUSED; + } + } else { + return errno_copy; + } + } else { + /* No Error */ + return 0; + } + } else { + /* Have the FULL Stack which supports SO_ERROR */ + /* Hasn't been written yet, never been tested! */ + /* Jerry.Kirk@Nexwarecorp.com */ + + int err; + _PRSockLen_t optlen = sizeof(err); + + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, + (char *) &err, &optlen) == -1) { + return errno; + } else { + return err; + } + } + } else { + return ECONNREFUSED; + } +#elif defined(UNIXWARE) + /* + * getsockopt() fails with EPIPE, so use getmsg() instead. + */ + + int rv; + int flags = 0; + rv = getmsg(osfd, NULL, NULL, &flags); + PR_ASSERT(-1 == rv || 0 == rv); + if (-1 == rv && errno != EAGAIN && errno != EWOULDBLOCK) { + return errno; + } + return 0; /* no error */ +#else + int err; + _PRSockLen_t optlen = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) == -1) { + return errno; + } + return err; + +#endif +} + +/************************************************************************/ + +/* +** Special hacks for xlib. Xlib/Xt/Xm is not re-entrant nor is it thread +** safe. Unfortunately, neither is mozilla. To make these programs work +** in a pre-emptive threaded environment, we need to use a lock. +*/ + +void _PR_XLock(void) +{ + PR_EnterMonitor(_pr_Xfe_mon); +} + +void _PR_XUnlock(void) +{ + PR_ExitMonitor(_pr_Xfe_mon); +} + +PRBool _PR_XIsLocked(void) +{ + return (PR_InMonitor(_pr_Xfe_mon)) ? PR_TRUE : PR_FALSE; +} + +#if defined(HAVE_FCNTL_FILE_LOCKING) + +PRStatus +_MD_LockFile(PRInt32 f) +{ + PRInt32 rv; + struct flock arg; + + arg.l_type = F_WRLCK; + arg.l_whence = SEEK_SET; + arg.l_start = 0; + arg.l_len = 0; /* until EOF */ + rv = fcntl(f, F_SETLKW, &arg); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus +_MD_TLockFile(PRInt32 f) +{ + PRInt32 rv; + struct flock arg; + + arg.l_type = F_WRLCK; + arg.l_whence = SEEK_SET; + arg.l_start = 0; + arg.l_len = 0; /* until EOF */ + rv = fcntl(f, F_SETLK, &arg); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus +_MD_UnlockFile(PRInt32 f) +{ + PRInt32 rv; + struct flock arg; + + arg.l_type = F_UNLCK; + arg.l_whence = SEEK_SET; + arg.l_start = 0; + arg.l_len = 0; /* until EOF */ + rv = fcntl(f, F_SETLK, &arg); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +#elif defined(HAVE_BSD_FLOCK) + +#include <sys/file.h> + +PRStatus +_MD_LockFile(PRInt32 f) +{ + PRInt32 rv; + rv = flock(f, LOCK_EX); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus +_MD_TLockFile(PRInt32 f) +{ + PRInt32 rv; + rv = flock(f, LOCK_EX|LOCK_NB); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus +_MD_UnlockFile(PRInt32 f) +{ + PRInt32 rv; + rv = flock(f, LOCK_UN); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} +#else + +PRStatus +_MD_LockFile(PRInt32 f) +{ + PRInt32 rv; + rv = lockf(f, F_LOCK, 0); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus +_MD_TLockFile(PRInt32 f) +{ + PRInt32 rv; + rv = lockf(f, F_TLOCK, 0); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus +_MD_UnlockFile(PRInt32 f) +{ + PRInt32 rv; + rv = lockf(f, F_ULOCK, 0); + if (rv == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} +#endif + +PRStatus _MD_gethostname(char *name, PRUint32 namelen) +{ + PRIntn rv; + + rv = gethostname(name, namelen); + if (0 == rv) { + return PR_SUCCESS; + } + _PR_MD_MAP_GETHOSTNAME_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PRStatus _MD_getsysinfo(PRSysInfo cmd, char *name, PRUint32 namelen) +{ + struct utsname info; + + PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) || + (cmd == PR_SI_RELEASE_BUILD)); + + if (uname(&info) == -1) { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + if (PR_SI_SYSNAME == cmd) { + (void)PR_snprintf(name, namelen, info.sysname); + } + else if (PR_SI_RELEASE == cmd) { + (void)PR_snprintf(name, namelen, info.release); + } + else if (PR_SI_RELEASE_BUILD == cmd) { + (void)PR_snprintf(name, namelen, info.version); + } + else { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +/* + ******************************************************************* + * + * Memory-mapped files + * + ******************************************************************* + */ + +PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) +{ + PRFileInfo info; + PRUint32 sz; + + LL_L2UI(sz, size); + if (sz) { + if (PR_GetOpenFileInfo(fmap->fd, &info) == PR_FAILURE) { + return PR_FAILURE; + } + if (sz > info.size) { + /* + * Need to extend the file + */ + if (fmap->prot != PR_PROT_READWRITE) { + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0); + return PR_FAILURE; + } + if (PR_Seek(fmap->fd, sz - 1, PR_SEEK_SET) == -1) { + return PR_FAILURE; + } + if (PR_Write(fmap->fd, "", 1) != 1) { + return PR_FAILURE; + } + } + } + if (fmap->prot == PR_PROT_READONLY) { + fmap->md.prot = PROT_READ; +#if defined(DARWIN) || defined(ANDROID) + /* + * This is needed on OS X because its implementation of + * POSIX shared memory returns an error for MAP_PRIVATE, even + * when the mapping is read-only. + * + * And this is needed on Android, because mapping ashmem with + * MAP_PRIVATE creates a mapping of zeroed memory instead of + * the shm contents. + */ + fmap->md.flags = MAP_SHARED; +#else + fmap->md.flags = MAP_PRIVATE; +#endif + } else if (fmap->prot == PR_PROT_READWRITE) { + fmap->md.prot = PROT_READ | PROT_WRITE; + fmap->md.flags = MAP_SHARED; + } else { + PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY); + fmap->md.prot = PROT_READ | PROT_WRITE; + fmap->md.flags = MAP_PRIVATE; + } + return PR_SUCCESS; +} + +void * _MD_MemMap( + PRFileMap *fmap, + PRInt64 offset, + PRUint32 len) +{ + PRInt32 off; + void *addr; + + LL_L2I(off, offset); + if ((addr = mmap(0, len, fmap->md.prot, fmap->md.flags, + fmap->fd->secret->md.osfd, off)) == (void *) -1) { + _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); + addr = NULL; + } + return addr; +} + +PRStatus _MD_MemUnmap(void *addr, PRUint32 len) +{ + if (munmap(addr, len) == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; +} + +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; +} + +PRStatus _MD_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len) +{ + /* msync(..., MS_SYNC) alone is sufficient to flush modified data to disk + * synchronously. It is not necessary to call fsync. */ + if (msync(addr, len, MS_SYNC) == 0) { + return PR_SUCCESS; + } + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; +} + +#if defined(_PR_NEED_FAKE_POLL) + +/* + * Some platforms don't have poll(). For easier porting of code + * that calls poll(), we emulate poll() using select(). + */ + +int poll(struct pollfd *filedes, unsigned long nfds, int timeout) +{ + int i; + int rv; + int maxfd; + fd_set rd, wr, ex; + struct timeval tv, *tvp; + + if (timeout < 0 && timeout != -1) { + errno = EINVAL; + return -1; + } + + if (timeout == -1) { + tvp = NULL; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + tvp = &tv; + } + + maxfd = -1; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + for (i = 0; i < nfds; i++) { + int osfd = filedes[i].fd; + int events = filedes[i].events; + PRBool fdHasEvent = PR_FALSE; + + PR_ASSERT(osfd < FD_SETSIZE); + if (osfd < 0 || osfd >= FD_SETSIZE) { + continue; /* Skip this osfd. */ + } + + /* + * Map the poll events to the select fd_sets. + * POLLIN, POLLRDNORM ===> readable + * POLLOUT, POLLWRNORM ===> writable + * POLLPRI, POLLRDBAND ===> exception + * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) + * are ignored. + * + * The output events POLLERR and POLLHUP are never turned on. + * POLLNVAL may be turned on. + */ + + if (events & (POLLIN | POLLRDNORM)) { + FD_SET(osfd, &rd); + fdHasEvent = PR_TRUE; + } + if (events & (POLLOUT | POLLWRNORM)) { + FD_SET(osfd, &wr); + fdHasEvent = PR_TRUE; + } + if (events & (POLLPRI | POLLRDBAND)) { + FD_SET(osfd, &ex); + fdHasEvent = PR_TRUE; + } + if (fdHasEvent && osfd > maxfd) { + maxfd = osfd; + } + } + + rv = select(maxfd + 1, &rd, &wr, &ex, tvp); + + /* Compute poll results */ + if (rv > 0) { + rv = 0; + for (i = 0; i < nfds; i++) { + PRBool fdHasEvent = PR_FALSE; + + filedes[i].revents = 0; + if (filedes[i].fd < 0) { + continue; + } + if (filedes[i].fd >= FD_SETSIZE) { + filedes[i].revents |= POLLNVAL; + continue; + } + if (FD_ISSET(filedes[i].fd, &rd)) { + if (filedes[i].events & POLLIN) { + filedes[i].revents |= POLLIN; + } + if (filedes[i].events & POLLRDNORM) { + filedes[i].revents |= POLLRDNORM; + } + fdHasEvent = PR_TRUE; + } + if (FD_ISSET(filedes[i].fd, &wr)) { + if (filedes[i].events & POLLOUT) { + filedes[i].revents |= POLLOUT; + } + if (filedes[i].events & POLLWRNORM) { + filedes[i].revents |= POLLWRNORM; + } + fdHasEvent = PR_TRUE; + } + if (FD_ISSET(filedes[i].fd, &ex)) { + if (filedes[i].events & POLLPRI) { + filedes[i].revents |= POLLPRI; + } + if (filedes[i].events & POLLRDBAND) { + filedes[i].revents |= POLLRDBAND; + } + fdHasEvent = PR_TRUE; + } + if (fdHasEvent) { + rv++; + } + } + PR_ASSERT(rv > 0); + } else if (rv == -1 && errno == EBADF) { + rv = 0; + for (i = 0; i < nfds; i++) { + filedes[i].revents = 0; + if (filedes[i].fd < 0) { + continue; + } + if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) { + filedes[i].revents = POLLNVAL; + rv++; + } + } + PR_ASSERT(rv > 0); + } + PR_ASSERT(-1 != timeout || rv != 0); + + return rv; +} +#endif /* _PR_NEED_FAKE_POLL */ diff --git a/nsprpub/pr/src/md/unix/unix_errors.c b/nsprpub/pr/src/md/unix/unix_errors.c new file mode 100644 index 0000000000..1d87659699 --- /dev/null +++ b/nsprpub/pr/src/md/unix/unix_errors.c @@ -0,0 +1,833 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#if defined(_PR_POLL_AVAILABLE) +#include <poll.h> +#endif +#include <errno.h> + +void _MD_unix_map_default_error(int err) +{ + PRErrorCode prError; + + switch (err ) { + case EACCES: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case EADDRINUSE: + prError = PR_ADDRESS_IN_USE_ERROR; + break; + case EADDRNOTAVAIL: + prError = PR_ADDRESS_NOT_AVAILABLE_ERROR; + break; + case EAFNOSUPPORT: + prError = PR_ADDRESS_NOT_SUPPORTED_ERROR; + break; + case EAGAIN: + prError = PR_WOULD_BLOCK_ERROR; + break; + /* + * On QNX and Neutrino, EALREADY is defined as EBUSY. + */ +#if EALREADY != EBUSY + case EALREADY: + prError = PR_ALREADY_INITIATED_ERROR; + break; +#endif + case EBADF: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; +#ifdef EBADMSG + case EBADMSG: + prError = PR_IO_ERROR; + break; +#endif + case EBUSY: + prError = PR_FILESYSTEM_MOUNTED_ERROR; + break; + case ECONNABORTED: + prError = PR_CONNECT_ABORTED_ERROR; + break; + case ECONNREFUSED: + prError = PR_CONNECT_REFUSED_ERROR; + break; + case ECONNRESET: + prError = PR_CONNECT_RESET_ERROR; + break; + case EDEADLK: + prError = PR_DEADLOCK_ERROR; + break; +#ifdef EDIRCORRUPTED + case EDIRCORRUPTED: + prError = PR_DIRECTORY_CORRUPTED_ERROR; + break; +#endif +#ifdef EDQUOT + case EDQUOT: + prError = PR_NO_DEVICE_SPACE_ERROR; + break; +#endif + case EEXIST: + prError = PR_FILE_EXISTS_ERROR; + break; + case EFAULT: + prError = PR_ACCESS_FAULT_ERROR; + break; + case EFBIG: + prError = PR_FILE_TOO_BIG_ERROR; + break; + case EHOSTUNREACH: + case EHOSTDOWN: + prError = PR_HOST_UNREACHABLE_ERROR; + break; + case EINPROGRESS: + prError = PR_IN_PROGRESS_ERROR; + break; + case EINTR: + prError = PR_PENDING_INTERRUPT_ERROR; + break; + case EINVAL: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case EIO: + prError = PR_IO_ERROR; + break; + case EISCONN: + prError = PR_IS_CONNECTED_ERROR; + break; + case EISDIR: + prError = PR_IS_DIRECTORY_ERROR; + break; + case ELOOP: + prError = PR_LOOP_ERROR; + break; + case EMFILE: + prError = PR_PROC_DESC_TABLE_FULL_ERROR; + break; + case EMLINK: + prError = PR_MAX_DIRECTORY_ENTRIES_ERROR; + break; + case EMSGSIZE: + prError = PR_INVALID_ARGUMENT_ERROR; + break; +#ifdef EMULTIHOP + case EMULTIHOP: + prError = PR_REMOTE_FILE_ERROR; + break; +#endif + case ENAMETOOLONG: + prError = PR_NAME_TOO_LONG_ERROR; + break; + case ENETUNREACH: + prError = PR_NETWORK_UNREACHABLE_ERROR; + break; + case ENFILE: + prError = PR_SYS_DESC_TABLE_FULL_ERROR; + break; + /* + * On SCO OpenServer 5, ENOBUFS is defined as ENOSR. + */ +#if defined(ENOBUFS) && (ENOBUFS != ENOSR) + case ENOBUFS: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; +#endif + case ENODEV: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ENOENT: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ENOLCK: + prError = PR_FILE_IS_LOCKED_ERROR; + break; +#ifdef ENOLINK + case ENOLINK: + prError = PR_REMOTE_FILE_ERROR; + break; +#endif + case ENOMEM: + prError = PR_OUT_OF_MEMORY_ERROR; + break; + case ENOPROTOOPT: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case ENOSPC: + prError = PR_NO_DEVICE_SPACE_ERROR; + break; +#ifdef ENOSR + case ENOSR: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; +#endif + case ENOSYS: + prError = PR_NOT_IMPLEMENTED_ERROR; + break; + case ENOTCONN: + prError = PR_NOT_CONNECTED_ERROR; + break; + case ENOTDIR: + prError = PR_NOT_DIRECTORY_ERROR; + break; + case ENOTSOCK: + prError = PR_NOT_SOCKET_ERROR; + break; + case ENXIO: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case EOPNOTSUPP: + prError = PR_NOT_TCP_SOCKET_ERROR; + break; +#ifdef EOVERFLOW + case EOVERFLOW: + prError = PR_BUFFER_OVERFLOW_ERROR; + break; +#endif + case EPERM: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case EPIPE: + prError = PR_CONNECT_RESET_ERROR; + break; +#ifdef EPROTO + case EPROTO: + prError = PR_IO_ERROR; + break; +#endif + case EPROTONOSUPPORT: + prError = PR_PROTOCOL_NOT_SUPPORTED_ERROR; + break; + case EPROTOTYPE: + prError = PR_ADDRESS_NOT_SUPPORTED_ERROR; + break; + case ERANGE: + prError = PR_INVALID_METHOD_ERROR; + break; + case EROFS: + prError = PR_READ_ONLY_FILESYSTEM_ERROR; + break; + case ESPIPE: + prError = PR_INVALID_METHOD_ERROR; + break; + case ETIMEDOUT: + prError = PR_IO_TIMEOUT_ERROR; + break; +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: + prError = PR_WOULD_BLOCK_ERROR; + break; +#endif + case EXDEV: + prError = PR_NOT_SAME_DEVICE_ERROR; + break; + default: + prError = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_opendir_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_closedir_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_readdir_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case 0: + case ENOENT: + prError = PR_NO_MORE_FILES_ERROR; + break; +#ifdef EOVERFLOW + case EOVERFLOW: + prError = PR_IO_ERROR; + break; +#endif + case EINVAL: + prError = PR_IO_ERROR; + break; + case ENXIO: + prError = PR_IO_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_unlink_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EPERM: + prError = PR_IS_DIRECTORY_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_stat_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_fstat_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_rename_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EEXIST: + prError = PR_DIRECTORY_NOT_EMPTY_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_access_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_mkdir_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_rmdir_error(int err) +{ + PRErrorCode prError; + + switch (err) { + /* + * On AIX 4.3, ENOTEMPTY is defined as EEXIST. + */ +#if ENOTEMPTY != EEXIST + case ENOTEMPTY: + prError = PR_DIRECTORY_NOT_EMPTY_ERROR; + break; +#endif + case EEXIST: + prError = PR_DIRECTORY_NOT_EMPTY_ERROR; + break; + case EINVAL: + prError = PR_DIRECTORY_NOT_EMPTY_ERROR; + break; + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_read_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_INVALID_METHOD_ERROR; + break; + case ENXIO: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_write_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_INVALID_METHOD_ERROR; + break; + case ENXIO: + prError = PR_INVALID_METHOD_ERROR; + break; + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_lseek_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_fsync_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + case EINVAL: + prError = PR_INVALID_METHOD_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_close_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_socket_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_socketavailable_error(int err) +{ + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); +} + +void _MD_unix_map_recv_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_recvfrom_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_send_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_sendto_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_writev_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_accept_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ENODEV: + prError = PR_NOT_TCP_SOCKET_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_connect_error(int err) +{ + PRErrorCode prError; + + switch (err) { +#if defined(UNIXWARE) + /* + * On some platforms, if we connect to a port on the local host + * (the loopback address) that no process is listening on, we get + * EIO instead of ECONNREFUSED. + */ + case EIO: + prError = PR_CONNECT_REFUSED_ERROR; + break; +#endif + case ENXIO: + prError = PR_IO_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_bind_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_SOCKET_ADDRESS_IS_BOUND_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_listen_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_shutdown_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_socketpair_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_getsockname_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_getpeername_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_getsockopt_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_BUFFER_OVERFLOW_ERROR; + break; + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_setsockopt_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_BUFFER_OVERFLOW_ERROR; + break; + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_open_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EAGAIN: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case EBUSY: + prError = PR_IO_ERROR; + break; + case ENODEV: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ENOMEM: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; +#ifdef EOVERFLOW + case EOVERFLOW: + prError = PR_FILE_TOO_BIG_ERROR; + break; +#endif + case ETIMEDOUT: + prError = PR_REMOTE_FILE_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_mmap_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EAGAIN: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case EMFILE: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case ENODEV: + prError = PR_OPERATION_NOT_SUPPORTED_ERROR; + break; + case ENXIO: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_gethostname_error(int err) +{ + _MD_unix_map_default_error(err); +} + +void _MD_unix_map_select_error(int err) +{ + _MD_unix_map_default_error(err); +} + +#if defined(_PR_POLL_AVAILABLE) || defined(_PR_NEED_FAKE_POLL) +void _MD_unix_map_poll_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EAGAIN: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_poll_revents_error(int err) +{ + if (err & POLLNVAL) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF); + } + else if (err & POLLHUP) { + PR_SetError(PR_CONNECT_RESET_ERROR, EPIPE); + } + else if (err & POLLERR) { + PR_SetError(PR_IO_ERROR, EIO); + } + else { + PR_SetError(PR_UNKNOWN_ERROR, err); + } +} +#endif /* _PR_POLL_AVAILABLE || _PR_NEED_FAKE_POLL */ + + +void _MD_unix_map_flock_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EINVAL: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; + case EWOULDBLOCK: + prError = PR_FILE_IS_LOCKED_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_unix_map_lockf_error(int err) +{ + PRErrorCode prError; + + switch (err) { + case EACCES: + prError = PR_FILE_IS_LOCKED_ERROR; + break; + case EDEADLK: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + default: + _MD_unix_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +#ifdef AIX +void _MD_aix_map_sendfile_error(int err) +{ + _MD_unix_map_default_error(err); +} +#endif /* AIX */ + +#ifdef HPUX11 +void _MD_hpux_map_sendfile_error(int err) +{ + _MD_unix_map_default_error(err); +} +#endif /* HPUX11 */ + +#ifdef SOLARIS +void _MD_solaris_map_sendfile_error(int err) +{ + PRErrorCode prError; + + switch (err) { + /* + * Solaris defines a 0 return value for sendfile to mean end-of-file. + */ + case 0: + prError = PR_END_OF_FILE_ERROR; + break; + + default: + _MD_unix_map_default_error(err) ; + return; + } + PR_SetError(prError, err); +} +#endif /* SOLARIS */ + +#ifdef LINUX +void _MD_linux_map_sendfile_error(int err) +{ + _MD_unix_map_default_error(err) ; +} +#endif /* LINUX */ diff --git a/nsprpub/pr/src/md/unix/unixware.c b/nsprpub/pr/src/md/unix/unixware.c new file mode 100644 index 0000000000..d6958558b2 --- /dev/null +++ b/nsprpub/pr/src/md/unix/unixware.c @@ -0,0 +1,559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#if !defined (USE_SVR4_THREADS) + +/* + * using only NSPR threads here + */ + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +#ifdef ALARMS_BREAK_TCP /* I don't think they do */ + +PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _connect(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); +} + +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _accept(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); + return(rv); +} +#endif + +/* + * These are also implemented in pratom.c using NSPR locks. Any reason + * this might be better or worse? If you like this better, define + * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h + */ +#ifdef _PR_HAVE_ATOMIC_OPS +/* Atomic operations */ +#include <stdio.h> +static FILE *_uw_semf; + +void +_MD_INIT_ATOMIC(void) +{ + /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */ + if ((_uw_semf = tmpfile()) == NULL) { + PR_ASSERT(0); + } + + return; +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)++; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) +{ + flockfile(_uw_semf); + (*ptr) += val; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)--; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + flockfile(_uw_semf); + *val = newval; + unflockfile(_uw_semf); +} +#endif + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Unixware */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Unixware."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware."); +} + +#else /* USE_SVR4_THREADS */ + +/* NOTE: + * SPARC v9 (Ultras) do have an atomic test-and-set operation. But + * SPARC v8 doesn't. We should detect in the init if we are running on + * v8 or v9, and then use assembly where we can. + */ + +#include <thread.h> +#include <synch.h> + +static mutex_t _unixware_atomic = DEFAULTMUTEX; + +#define TEST_THEN_ADD(where, inc) \ + if (mutex_lock(&_unixware_atomic) != 0)\ + PR_ASSERT(0);\ + *where += inc;\ + if (mutex_unlock(&_unixware_atomic) != 0)\ + PR_ASSERT(0); + +#define TEST_THEN_SET(where, val) \ + if (mutex_lock(&_unixware_atomic) != 0)\ + PR_ASSERT(0);\ + *where = val;\ + if (mutex_unlock(&_unixware_atomic) != 0)\ + PR_ASSERT(0); + +void +_MD_INIT_ATOMIC(void) +{ +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + TEST_THEN_ADD(val, 1); +} + +void +_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) +{ + TEST_THEN_ADD(ptr, val); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + TEST_THEN_ADD(val, 0xffffffff); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + TEST_THEN_SET(val, newval); +} + +#include <signal.h> +#include <errno.h> +#include <fcntl.h> + +#include <sys/lwp.h> +#include <sys/procfs.h> +#include <sys/syscall.h> + + +THREAD_KEY_T threadid_key; +THREAD_KEY_T cpuid_key; +THREAD_KEY_T last_thread_key; +static sigset_t set, oldset; + +void _MD_EarlyInit(void) +{ + THR_KEYCREATE(&threadid_key, NULL); + THR_KEYCREATE(&cpuid_key, NULL); + THR_KEYCREATE(&last_thread_key, NULL); + sigemptyset(&set); + sigaddset(&set, SIGALRM); +} + +PRStatus _MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + long flags; + + /* mask out SIGALRM for native thread creation */ + thr_sigsetmask(SIG_BLOCK, &set, &oldset); + + flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/ + : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/); + if (_PR_IS_GCABLE_THREAD(thread) || + (scope == PR_GLOBAL_BOUND_THREAD)) { + flags |= THR_BOUND; + } + + if (thr_create(NULL, thread->stack->stackSize, + (void *(*)(void *)) start, (void *) thread, + flags, + &thread->md.handle)) { + thr_sigsetmask(SIG_SETMASK, &oldset, NULL); + return PR_FAILURE; + } + + + /* When the thread starts running, then the lwpid is set to the right + * value. Until then we want to mark this as 'uninit' so that + * its register state is initialized properly for GC */ + + thread->md.lwpid = -1; + thr_sigsetmask(SIG_SETMASK, &oldset, NULL); + _MD_NEW_SEM(&thread->md.waiter_sem, 0); + + if ((scope == PR_GLOBAL_THREAD) || (scope == PR_GLOBAL_BOUND_THREAD)) { + thread->flags |= _PR_GLOBAL_SCOPE; + } + + /* + ** Set the thread priority. This will also place the thread on + ** the runQ. + ** + ** Force PR_SetThreadPriority to set the priority by + ** setting thread->priority to 100. + */ + { + int pri; + pri = thread->priority; + thread->priority = 100; + PR_SetThreadPriority( thread, pri ); + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("(0X%x)[Start]: on to runq at priority %d", + thread, thread->priority)); + } + + /* Activate the thread */ + if (thr_continue( thread->md.handle ) ) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +void _MD_cleanup_thread(PRThread *thread) +{ + thread_t hdl; + PRMonitor *mon; + + hdl = thread->md.handle; + + /* + ** First, suspend the thread (unless it's the active one) + ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to + ** prevent both of us modifying the thread structure at the same time. + */ + if ( thread != _PR_MD_CURRENT_THREAD() ) { + thr_suspend(hdl); + } + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("(0X%x)[DestroyThread]\n", thread)); + + _MD_DESTROY_SEM(&thread->md.waiter_sem); +} + +void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri) +{ + if(thr_setprio((thread_t)md_thread->handle, newPri)) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("_PR_SetThreadPriority: can't set thread priority\n")); + } +} + +void _MD_WAIT_CV( + struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout) +{ + struct timespec tt; + PRUint32 msec; + int rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + msec = PR_IntervalToMilliseconds(timeout); + + GETTIME (&tt); + + tt.tv_sec += msec / PR_MSEC_PER_SEC; + tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC; + /* Check for nsec overflow - otherwise we'll get an EINVAL */ + if (tt.tv_nsec >= PR_NSEC_PER_SEC) { + tt.tv_sec++; + tt.tv_nsec -= PR_NSEC_PER_SEC; + } + me->md.sp = unixware_getsp(); + + + /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason + * hence ignore EINTR for now */ + + COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt); +} + +void _MD_lock(struct _MDLock *md_lock) +{ + mutex_lock(&md_lock->lock); +} + +void _MD_unlock(struct _MDLock *md_lock) +{ + mutex_unlock(&((md_lock)->lock)); +} + + +PRThread *_pr_current_thread_tls() +{ + PRThread *ret; + + thr_getspecific(threadid_key, (void **)&ret); + return ret; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + _MD_WAIT_SEM(&thread->md.waiter_sem); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread == NULL) { + return PR_SUCCESS; + } + _MD_POST_SEM(&thread->md.waiter_sem); + return PR_SUCCESS; +} + +_PRCPU *_pr_current_cpu_tls() +{ + _PRCPU *ret; + + thr_getspecific(cpuid_key, (void **)&ret); + return ret; +} + +PRThread *_pr_last_thread_tls() +{ + PRThread *ret; + + thr_getspecific(last_thread_key, (void **)&ret); + return ret; +} + +_MDLock _pr_ioq_lock; + +void _MD_INIT_IO (void) +{ + _MD_NEW_LOCK(&_pr_ioq_lock); +} + +PRStatus _MD_InitializeThread(PRThread *thread) +{ + if (!_PR_IS_NATIVE_THREAD(thread)) { + return; + } + /* prime the sp; substract 4 so we don't hit the assert that + * curr sp > base_stack + */ + thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long); + thread->md.lwpid = _lwp_self(); + thread->md.handle = THR_SELF(); + + /* all threads on Solaris are global threads from NSPR's perspective + * since all of them are mapped to Solaris threads. + */ + thread->flags |= _PR_GLOBAL_SCOPE; + + /* For primordial/attached thread, we don't create an underlying native thread. + * So, _MD_CREATE_THREAD() does not get called. We need to do initialization + * like allocating thread's synchronization variables and set the underlying + * native thread's priority. + */ + if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { + _MD_NEW_SEM(&thread->md.waiter_sem, 0); + _MD_SET_PRIORITY(&(thread->md), thread->priority); + } + return PR_SUCCESS; +} + +static sigset_t old_mask; /* store away original gc thread sigmask */ +static int gcprio; /* store away original gc thread priority */ +static lwpid_t *all_lwps=NULL; /* list of lwps that we suspended */ +static int num_lwps ; +static int suspendAllOn = 0; + +#define VALID_SP(sp, bottom, top) \ + (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top))) + +void unixware_preempt_off() +{ + sigset_t set; + (void)sigfillset(&set); + sigprocmask (SIG_SETMASK, &set, &old_mask); +} + +void unixware_preempt_on() +{ + sigprocmask (SIG_SETMASK, &old_mask, NULL); +} + +void _MD_Begin_SuspendAll() +{ + unixware_preempt_off(); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n")); + /* run at highest prio so I cannot be preempted */ + thr_getprio(thr_self(), &gcprio); + thr_setprio(thr_self(), 0x7fffffff); + suspendAllOn = 1; +} + +void _MD_End_SuspendAll() +{ +} + +void _MD_End_ResumeAll() +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n")); + thr_setprio(thr_self(), gcprio); + unixware_preempt_on(); + suspendAllOn = 0; +} + +void _MD_Suspend(PRThread *thr) +{ + int lwp_fd, result; + int lwp_main_proc_fd = 0; + + thr_suspend(thr->md.handle); + if (!_PR_IS_GCABLE_THREAD(thr)) { + return; + } + /* XXX Primordial thread can't be bound to an lwp, hence there is no + * way we can assume that we can get the lwp status for primordial + * thread reliably. Hence we skip this for primordial thread, hoping + * that the SP is saved during lock and cond. wait. + * XXX - Again this is concern only for java interpreter, not for the + * server, 'cause primordial thread in the server does not do java work + */ + if (thr->flags & _PR_PRIMORDIAL) { + return; + } + + /* if the thread is not started yet then don't do anything */ + if (!suspendAllOn || thr->md.lwpid == -1) { + return; + } + +} +void _MD_Resume(PRThread *thr) +{ + if (!_PR_IS_GCABLE_THREAD(thr) || !suspendAllOn) { + /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend + * during that time we can't call any thread lib or libc calls. Hence + * make sure that no resume is requested for Non gcable thread + * during suspendAllOn */ + PR_ASSERT(!suspendAllOn); + thr_continue(thr->md.handle); + return; + } + if (thr->md.lwpid == -1) { + return; + } + + if ( _lwp_continue(thr->md.lwpid) < 0) { + PR_ASSERT(0); /* ARGH, we are hosed! */ + } +} + + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) getcontext(CONTEXT(t)); /* XXX tune me: set md_IRIX.c */ + } + *np = NGREG; + if (t->md.lwpid == -1) { + memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord)); + } + return (PRWord*) &t->md.context.uc_mcontext.gregs[0]; +} + +int +_pr_unixware_clock_gettime (struct timespec *tp) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; +} + + +#endif /* USE_SVR4_THREADS */ diff --git a/nsprpub/pr/src/md/unix/uxpoll.c b/nsprpub/pr/src/md/unix/uxpoll.c new file mode 100644 index 0000000000..d01121cbf1 --- /dev/null +++ b/nsprpub/pr/src/md/unix/uxpoll.c @@ -0,0 +1,722 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(_PR_PTHREADS) + +#error "This file should not be compiled" + +#else /* defined(_PR_PTHREADS) */ + +#include "primpl.h" + +#include <sys/time.h> + +#include <fcntl.h> +#ifdef _PR_USE_POLL +#include <poll.h> +#endif + +#if defined(_PR_USE_POLL) +static PRInt32 NativeThreadPoll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + /* + * This function is mostly duplicated from ptio.s's PR_Poll(). + */ + PRInt32 ready = 0; + /* + * For restarting poll() if it is interrupted by a signal. + * We use these variables to figure out how much time has + * elapsed and how much of the timeout still remains. + */ + PRIntn index, msecs; + struct pollfd *syspoll = NULL; + PRIntervalTime start, elapsed, remaining; + + syspoll = (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd)); + if (NULL == syspoll) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + for (index = 0; index < npds; ++index) + { + PRFileDesc *bottom; + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (pds[index].in_flags & PR_POLL_READ) + { + in_flags_read = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_WRITE, + &out_flags_read); + } + if (pds[index].in_flags & PR_POLL_WRITE) + { + in_flags_write = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_READ, + &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one is ready right now */ + if (0 == ready) + { + /* + * We will return without calling the system + * poll function. So zero the out_flags + * fields of all the poll descriptors before + * this one. + */ + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; + pds[index].out_flags = out_flags_read | out_flags_write; + } + else + { + pds[index].out_flags = 0; /* pre-condition */ + /* now locate the NSPR layer at the bottom of the stack */ + bottom = PR_GetIdentitiesLayer(pds[index].fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); /* what to do about that? */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + syspoll[index].fd = bottom->secret->md.osfd; + syspoll[index].events = 0; /* pre-condition */ + if (in_flags_read & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_READ; + syspoll[index].events |= POLLIN; + } + if (in_flags_read & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_WRITE; + syspoll[index].events |= POLLOUT; + } + if (in_flags_write & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_READ; + syspoll[index].events |= POLLIN; + } + if (in_flags_write & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_WRITE; + syspoll[index].events |= POLLOUT; + } + if (pds[index].in_flags & PR_POLL_EXCEPT) { + syspoll[index].events |= POLLPRI; + } + } + } + else + { + if (0 == ready) + { + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pds[index].out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + /* make poll() ignore this entry */ + syspoll[index].fd = -1; + syspoll[index].events = 0; + pds[index].out_flags = 0; + } + } + + if (0 == ready) + { + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: msecs = 0; break; + case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break; + default: + msecs = PR_IntervalToMilliseconds(timeout); + start = PR_IntervalNow(); + } + +retry: + ready = _MD_POLL(syspoll, npds, msecs); + if (-1 == ready) + { + PRIntn oserror = errno; + + if (EINTR == oserror) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else if (timeout == PR_INTERVAL_NO_WAIT) { + ready = 0; + } + else + { + elapsed = (PRIntervalTime)(PR_IntervalNow() - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + msecs = PR_IntervalToMilliseconds(remaining); + goto retry; + } + } + } + else { + _PR_MD_MAP_POLL_ERROR(oserror); + } + } + else if (ready > 0) + { + for (index = 0; index < npds; ++index) + { + PRInt16 out_flags = 0; + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (0 != syspoll[index].revents) + { + /* + ** Set up the out_flags so that it contains the + ** bits that the highest layer thinks are nice + ** to have. Then the client of that layer will + ** call the appropriate I/O function and maybe + ** the protocol will make progress. + */ + if (syspoll[index].revents & POLLIN) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_READ) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_READ) + { + out_flags |= PR_POLL_WRITE; + } + } + if (syspoll[index].revents & POLLOUT) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_WRITE) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_WRITE) + { + out_flags |= PR_POLL_WRITE; + } + } + if (syspoll[index].revents & POLLPRI) { + out_flags |= PR_POLL_EXCEPT; + } + if (syspoll[index].revents & POLLERR) { + out_flags |= PR_POLL_ERR; + } + if (syspoll[index].revents & POLLNVAL) { + out_flags |= PR_POLL_NVAL; + } + if (syspoll[index].revents & POLLHUP) { + out_flags |= PR_POLL_HUP; + } + } + } + pds[index].out_flags = out_flags; + } + } + } + + PR_DELETE(syspoll); + return ready; + +} /* NativeThreadPoll */ +#endif /* defined(_PR_USE_POLL) */ + +#if !defined(_PR_USE_POLL) +static PRInt32 NativeThreadSelect( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + /* + * This code is almost a duplicate of w32poll.c's _PR_MD_PR_POLL(). + */ + fd_set rd, wt, ex; + PRFileDesc *bottom; + PRPollDesc *pd, *epd; + PRInt32 maxfd = -1, ready, err; + PRIntervalTime remaining, elapsed, start; + + struct timeval tv, *tvp = NULL; + + FD_ZERO(&rd); + FD_ZERO(&wt); + FD_ZERO(&ex); + + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + if (pd->in_flags & PR_POLL_READ) + { + in_flags_read = (pd->fd->methods->poll)( + pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read); + } + if (pd->in_flags & PR_POLL_WRITE) + { + in_flags_write = (pd->fd->methods->poll)( + pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one's ready right now */ + if (0 == ready) + { + /* + * We will have to return without calling the + * system poll/select function. So zero the + * out_flags fields of all the poll descriptors + * before this one. + */ + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; + pd->out_flags = out_flags_read | out_flags_write; + } + else + { + pd->out_flags = 0; /* pre-condition */ + + /* make sure this is an NSPR supported stack */ + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); /* what to do about that? */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + PRInt32 osfd = bottom->secret->md.osfd; + if (osfd > maxfd) { + maxfd = osfd; + } + if (in_flags_read & PR_POLL_READ) + { + pd->out_flags |= _PR_POLL_READ_SYS_READ; + FD_SET(osfd, &rd); + } + if (in_flags_read & PR_POLL_WRITE) + { + pd->out_flags |= _PR_POLL_READ_SYS_WRITE; + FD_SET(osfd, &wt); + } + if (in_flags_write & PR_POLL_READ) + { + pd->out_flags |= _PR_POLL_WRITE_SYS_READ; + FD_SET(osfd, &rd); + } + if (in_flags_write & PR_POLL_WRITE) + { + pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE; + FD_SET(osfd, &wt); + } + if (pd->in_flags & PR_POLL_EXCEPT) { + FD_SET(osfd, &ex); + } + } + } + else + { + if (0 == ready) + { + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pd->out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + pd->out_flags = 0; + } + } + + if (0 != ready) { + return ready; /* no need to block */ + } + + remaining = timeout; + start = PR_IntervalNow(); + +retry: + if (timeout != PR_INTERVAL_NO_TIMEOUT) + { + PRInt32 ticksPerSecond = PR_TicksPerSecond(); + tv.tv_sec = remaining / ticksPerSecond; + tv.tv_usec = PR_IntervalToMicroseconds( remaining % ticksPerSecond ); + tvp = &tv; + } + + ready = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp); + + if (ready == -1 && errno == EINTR) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else + { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + goto retry; + } + } + } + + /* + ** Now to unravel the select sets back into the client's poll + ** descriptor list. Is this possibly an area for pissing away + ** a few cycles or what? + */ + if (ready > 0) + { + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRInt16 out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + PRInt32 osfd; + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + + osfd = bottom->secret->md.osfd; + + if (FD_ISSET(osfd, &rd)) + { + if (pd->out_flags & _PR_POLL_READ_SYS_READ) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(osfd, &wt)) + { + if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(osfd, &ex)) { + out_flags |= PR_POLL_EXCEPT; + } + } + pd->out_flags = out_flags; + if (out_flags) { + ready++; + } + } + PR_ASSERT(ready > 0); + } + else if (ready < 0) + { + err = _MD_ERRNO(); + if (err == EBADF) + { + /* Find the bad fds */ + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + pd->out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + if (fcntl(bottom->secret->md.osfd, F_GETFL, 0) == -1) + { + pd->out_flags = PR_POLL_NVAL; + ready++; + } + } + } + PR_ASSERT(ready > 0); + } + else { + _PR_MD_MAP_SELECT_ERROR(err); + } + } + + return ready; +} /* NativeThreadSelect */ +#endif /* !defined(_PR_USE_POLL) */ + +static PRInt32 LocalThreads( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRPollDesc *pd, *epd; + PRInt32 ready, pdcnt; + _PRUnixPollDesc *unixpds, *unixpd; + + /* + * XXX + * PRPollDesc has a PRFileDesc field, fd, while the IOQ + * is a list of PRPollQueue structures, each of which contains + * a _PRUnixPollDesc. A _PRUnixPollDesc struct contains + * the OS file descriptor, osfd, and not a PRFileDesc. + * So, we have allocate memory for _PRUnixPollDesc structures, + * copy the flags information from the pds list and have pq + * point to this list of _PRUnixPollDesc structures. + * + * It would be better if the memory allocation can be avoided. + */ + + unixpd = unixpds = (_PRUnixPollDesc*) + PR_MALLOC(npds * sizeof(_PRUnixPollDesc)); + if (NULL == unixpds) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + + ready = 0; + for (pdcnt = 0, pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRFileDesc *bottom; + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + if (pd->in_flags & PR_POLL_READ) + { + in_flags_read = (pd->fd->methods->poll)( + pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read); + } + if (pd->in_flags & PR_POLL_WRITE) + { + in_flags_write = (pd->fd->methods->poll)( + pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one's ready right now */ + if (0 == ready) + { + /* + * We will have to return without calling the + * system poll/select function. So zero the + * out_flags fields of all the poll descriptors + * before this one. + */ + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; + pd->out_flags = out_flags_read | out_flags_write; + } + else + { + pd->out_flags = 0; /* pre-condition */ + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); /* what to do about that? */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + unixpd->osfd = bottom->secret->md.osfd; + unixpd->in_flags = 0; + if (in_flags_read & PR_POLL_READ) + { + unixpd->in_flags |= _PR_UNIX_POLL_READ; + pd->out_flags |= _PR_POLL_READ_SYS_READ; + } + if (in_flags_read & PR_POLL_WRITE) + { + unixpd->in_flags |= _PR_UNIX_POLL_WRITE; + pd->out_flags |= _PR_POLL_READ_SYS_WRITE; + } + if (in_flags_write & PR_POLL_READ) + { + unixpd->in_flags |= _PR_UNIX_POLL_READ; + pd->out_flags |= _PR_POLL_WRITE_SYS_READ; + } + if (in_flags_write & PR_POLL_WRITE) + { + unixpd->in_flags |= _PR_UNIX_POLL_WRITE; + pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE; + } + if ((in_flags_read | in_flags_write) & PR_POLL_EXCEPT) + { + unixpd->in_flags |= _PR_UNIX_POLL_EXCEPT; + } + unixpd++; pdcnt++; + } + } + else + { + if (0 == ready) + { + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pd->out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + } + + if (0 != ready) + { + /* no need to block */ + PR_DELETE(unixpds); + return ready; + } + + ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout); + + /* + * Copy the out_flags from the _PRUnixPollDesc structures to the + * user's PRPollDesc structures and free the allocated memory + */ + unixpd = unixpds; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRInt16 out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + /* + * take errors from the poll operation, + * the R/W bits from the request + */ + if (0 != unixpd->out_flags) + { + if (unixpd->out_flags & _PR_UNIX_POLL_READ) + { + if (pd->out_flags & _PR_POLL_READ_SYS_READ) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) { + out_flags |= PR_POLL_WRITE; + } + } + if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) + { + if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) { + out_flags |= PR_POLL_WRITE; + } + } + if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) { + out_flags |= PR_POLL_EXCEPT; + } + if (unixpd->out_flags & _PR_UNIX_POLL_ERR) { + out_flags |= PR_POLL_ERR; + } + if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { + out_flags |= PR_POLL_NVAL; + } + if (unixpd->out_flags & _PR_UNIX_POLL_HUP) { + out_flags |= PR_POLL_HUP; + } + } + unixpd++; + } + pd->out_flags = out_flags; + } + + PR_DELETE(unixpds); + + return ready; +} /* LocalThreads */ + +#if defined(_PR_USE_POLL) +#define NativeThreads NativeThreadPoll +#else +#define NativeThreads NativeThreadSelect +#endif + +PRInt32 _MD_pr_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRInt32 rv = 0; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) + { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (0 == npds) { + PR_Sleep(timeout); + } + else if (_PR_IS_NATIVE_THREAD(me)) { + rv = NativeThreads(pds, npds, timeout); + } + else { + rv = LocalThreads(pds, npds, timeout); + } + + return rv; +} /* _MD_pr_poll */ + +#endif /* defined(_PR_PTHREADS) */ + +/* uxpoll.c */ + diff --git a/nsprpub/pr/src/md/unix/uxproces.c b/nsprpub/pr/src/md/unix/uxproces.c new file mode 100644 index 0000000000..4216c04424 --- /dev/null +++ b/nsprpub/pr/src/md/unix/uxproces.c @@ -0,0 +1,877 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/wait.h> +#include <string.h> +#if defined(AIX) +#include <dlfcn.h> /* For dlopen, dlsym, dlclose */ +#endif + +#if defined(DARWIN) +#if defined(HAVE_CRT_EXTERNS_H) +#include <crt_externs.h> +#endif +#else +PR_IMPORT_DATA(char **) environ; +#endif + +/* + * HP-UX 9 doesn't have the SA_RESTART flag. + */ +#ifndef SA_RESTART +#define SA_RESTART 0 +#endif + +/* + ********************************************************************** + * + * The Unix process routines + * + ********************************************************************** + */ + +#define _PR_SIGNALED_EXITSTATUS 256 + +typedef enum pr_PidState { + _PR_PID_DETACHED, + _PR_PID_REAPED, + _PR_PID_WAITING +} pr_PidState; + +typedef struct pr_PidRecord { + pid_t pid; + int exitStatus; + pr_PidState state; + PRCondVar *reapedCV; + struct pr_PidRecord *next; +} pr_PidRecord; + +/* + * LinuxThreads are actually a kind of processes + * that can share the virtual address space and file descriptors. + */ +#if ((defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)) \ + && defined(_PR_PTHREADS)) +#define _PR_SHARE_CLONES +#endif + +/* + * The macro _PR_NATIVE_THREADS indicates that we are + * using native threads only, so waitpid() blocks just the + * calling thread, not the process. In this case, the waitpid + * daemon thread can safely block in waitpid(). So we don't + * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is + * also not necessary. + */ + +#if defined(_PR_GLOBAL_THREADS_ONLY) \ + || (defined(_PR_PTHREADS) \ + && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__)) +#define _PR_NATIVE_THREADS +#endif + +/* + * All the static variables used by the Unix process routines are + * collected in this structure. + */ + +static struct { + PRCallOnceType once; + PRThread *thread; + PRLock *ml; +#if defined(_PR_NATIVE_THREADS) + PRInt32 numProcs; + PRCondVar *cv; +#else + int pipefd[2]; +#endif + pr_PidRecord **pidTable; + +#ifdef _PR_SHARE_CLONES + struct pr_CreateProcOp *opHead, *opTail; +#endif + +#ifdef AIX + pid_t (*forkptr)(void); /* Newer versions of AIX (starting in 4.3.2) + * have f_fork, which is faster than the + * regular fork in a multithreaded process + * because it skips calling the fork handlers. + * So we look up the f_fork symbol to see if + * it's available and fall back on fork. + */ +#endif /* AIX */ +} pr_wp; + +#ifdef _PR_SHARE_CLONES +static int pr_waitpid_daemon_exit; + +void +_MD_unix_terminate_waitpid_daemon(void) +{ + if (pr_wp.thread) { + pr_waitpid_daemon_exit = 1; + write(pr_wp.pipefd[1], "", 1); + PR_JoinThread(pr_wp.thread); + } +} +#endif + +static PRStatus _MD_InitProcesses(void); +#if !defined(_PR_NATIVE_THREADS) +static void pr_InstallSigchldHandler(void); +#endif + +static PRProcess * +ForkAndExec( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + PRProcess *process; + int nEnv, idx; + char *const *childEnvp; + char **newEnvp = NULL; + int flags; + + process = PR_NEW(PRProcess); + if (!process) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + childEnvp = envp; + if (attr && attr->fdInheritBuffer) { + PRBool found = PR_FALSE; + + if (NULL == childEnvp) { +#ifdef DARWIN +#ifdef HAVE_CRT_EXTERNS_H + childEnvp = *(_NSGetEnviron()); +#else + /* _NSGetEnviron() is not available on iOS. */ + PR_DELETE(process); + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +#endif +#else + childEnvp = environ; +#endif + } + + for (nEnv = 0; childEnvp[nEnv]; nEnv++) { + } + newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *)); + if (NULL == newEnvp) { + PR_DELETE(process); + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + for (idx = 0; idx < nEnv; idx++) { + newEnvp[idx] = childEnvp[idx]; + if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { + newEnvp[idx] = attr->fdInheritBuffer; + found = PR_TRUE; + } + } + if (!found) { + newEnvp[idx++] = attr->fdInheritBuffer; + } + newEnvp[idx] = NULL; + childEnvp = newEnvp; + } + +#ifdef AIX + process->md.pid = (*pr_wp.forkptr)(); +#elif defined(NTO) + /* + * fork() & exec() does not work in a multithreaded process. + * Use spawn() instead. + */ + { + int fd_map[3] = { 0, 1, 2 }; + + if (attr) { + if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) { + fd_map[0] = dup(attr->stdinFd->secret->md.osfd); + flags = fcntl(fd_map[0], F_GETFL, 0); + if (flags & O_NONBLOCK) { + fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK); + } + } + if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) { + fd_map[1] = dup(attr->stdoutFd->secret->md.osfd); + flags = fcntl(fd_map[1], F_GETFL, 0); + if (flags & O_NONBLOCK) { + fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK); + } + } + if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) { + fd_map[2] = dup(attr->stderrFd->secret->md.osfd); + flags = fcntl(fd_map[2], F_GETFL, 0); + if (flags & O_NONBLOCK) { + fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK); + } + } + + PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */ + } + + process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp); + + if (fd_map[0] != 0) { + close(fd_map[0]); + } + if (fd_map[1] != 1) { + close(fd_map[1]); + } + if (fd_map[2] != 2) { + close(fd_map[2]); + } + } +#else + process->md.pid = fork(); +#endif + if ((pid_t) -1 == process->md.pid) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno); + PR_DELETE(process); + if (newEnvp) { + PR_DELETE(newEnvp); + } + return NULL; + } + if (0 == process->md.pid) { /* the child process */ + /* + * If the child process needs to exit, it must call _exit(). + * Do not call exit(), because exit() will flush and close + * the standard I/O file descriptors, and hence corrupt + * the parent process's standard I/O data structures. + */ + +#if !defined(NTO) + if (attr) { + /* the osfd's to redirect stdin, stdout, and stderr to */ + int in_osfd = -1, out_osfd = -1, err_osfd = -1; + + if (attr->stdinFd + && attr->stdinFd->secret->md.osfd != 0) { + in_osfd = attr->stdinFd->secret->md.osfd; + if (dup2(in_osfd, 0) != 0) { + _exit(1); /* failed */ + } + flags = fcntl(0, F_GETFL, 0); + if (flags & O_NONBLOCK) { + fcntl(0, F_SETFL, flags & ~O_NONBLOCK); + } + } + if (attr->stdoutFd + && attr->stdoutFd->secret->md.osfd != 1) { + out_osfd = attr->stdoutFd->secret->md.osfd; + if (dup2(out_osfd, 1) != 1) { + _exit(1); /* failed */ + } + flags = fcntl(1, F_GETFL, 0); + if (flags & O_NONBLOCK) { + fcntl(1, F_SETFL, flags & ~O_NONBLOCK); + } + } + if (attr->stderrFd + && attr->stderrFd->secret->md.osfd != 2) { + err_osfd = attr->stderrFd->secret->md.osfd; + if (dup2(err_osfd, 2) != 2) { + _exit(1); /* failed */ + } + flags = fcntl(2, F_GETFL, 0); + if (flags & O_NONBLOCK) { + fcntl(2, F_SETFL, flags & ~O_NONBLOCK); + } + } + if (in_osfd != -1) { + close(in_osfd); + } + if (out_osfd != -1 && out_osfd != in_osfd) { + close(out_osfd); + } + if (err_osfd != -1 && err_osfd != in_osfd + && err_osfd != out_osfd) { + close(err_osfd); + } + if (attr->currentDirectory) { + if (chdir(attr->currentDirectory) < 0) { + _exit(1); /* failed */ + } + } + } + + if (childEnvp) { + (void)execve(path, argv, childEnvp); + } else { + /* Inherit the environment of the parent. */ + (void)execv(path, argv); + } + /* Whoops! It returned. That's a bad sign. */ + _exit(1); +#endif /* !NTO */ + } + + if (newEnvp) { + PR_DELETE(newEnvp); + } + +#if defined(_PR_NATIVE_THREADS) + PR_Lock(pr_wp.ml); + if (0 == pr_wp.numProcs++) { + PR_NotifyCondVar(pr_wp.cv); + } + PR_Unlock(pr_wp.ml); +#endif + return process; +} + +#ifdef _PR_SHARE_CLONES + +struct pr_CreateProcOp { + const char *path; + char *const *argv; + char *const *envp; + const PRProcessAttr *attr; + PRProcess *process; + PRErrorCode prerror; + PRInt32 oserror; + PRBool done; + PRCondVar *doneCV; + struct pr_CreateProcOp *next; +}; + +PRProcess * +_MD_CreateUnixProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + struct pr_CreateProcOp *op; + PRProcess *proc; + int rv; + + if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { + return NULL; + } + + op = PR_NEW(struct pr_CreateProcOp); + if (NULL == op) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + op->path = path; + op->argv = argv; + op->envp = envp; + op->attr = attr; + op->done = PR_FALSE; + op->doneCV = PR_NewCondVar(pr_wp.ml); + if (NULL == op->doneCV) { + PR_DELETE(op); + return NULL; + } + PR_Lock(pr_wp.ml); + + /* add to the tail of op queue */ + op->next = NULL; + if (pr_wp.opTail) { + pr_wp.opTail->next = op; + pr_wp.opTail = op; + } else { + PR_ASSERT(NULL == pr_wp.opHead); + pr_wp.opHead = pr_wp.opTail = op; + } + + /* wake up the daemon thread */ + do { + rv = write(pr_wp.pipefd[1], "", 1); + } while (-1 == rv && EINTR == errno); + + while (op->done == PR_FALSE) { + PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pr_wp.ml); + PR_DestroyCondVar(op->doneCV); + proc = op->process; + if (!proc) { + PR_SetError(op->prerror, op->oserror); + } + PR_DELETE(op); + return proc; +} + +#else /* ! _PR_SHARE_CLONES */ + +PRProcess * +_MD_CreateUnixProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { + return NULL; + } + return ForkAndExec(path, argv, envp, attr); +} /* _MD_CreateUnixProcess */ + +#endif /* _PR_SHARE_CLONES */ + +/* + * The pid table is a hashtable. + * + * The number of buckets in the hashtable (NBUCKETS) must be a power of 2. + */ +#define NBUCKETS_LOG2 6 +#define NBUCKETS (1 << NBUCKETS_LOG2) +#define PID_HASH_MASK ((pid_t) (NBUCKETS - 1)) + +static pr_PidRecord * +FindPidTable(pid_t pid) +{ + pr_PidRecord *pRec; + int keyHash = (int) (pid & PID_HASH_MASK); + + pRec = pr_wp.pidTable[keyHash]; + while (pRec) { + if (pRec->pid == pid) { + break; + } + pRec = pRec->next; + } + return pRec; +} + +static void +InsertPidTable(pr_PidRecord *pRec) +{ + int keyHash = (int) (pRec->pid & PID_HASH_MASK); + + pRec->next = pr_wp.pidTable[keyHash]; + pr_wp.pidTable[keyHash] = pRec; +} + +static void +DeletePidTable(pr_PidRecord *pRec) +{ + int keyHash = (int) (pRec->pid & PID_HASH_MASK); + + if (pr_wp.pidTable[keyHash] == pRec) { + pr_wp.pidTable[keyHash] = pRec->next; + } else { + pr_PidRecord *pred, *cur; /* predecessor and current */ + + pred = pr_wp.pidTable[keyHash]; + cur = pred->next; + while (cur) { + if (cur == pRec) { + pred->next = cur->next; + break; + } + pred = cur; + cur = cur->next; + } + PR_ASSERT(cur != NULL); + } +} + +static int +ExtractExitStatus(int rawExitStatus) +{ + /* + * We did not specify the WCONTINUED and WUNTRACED options + * for waitpid, so these two events should not be reported. + */ + PR_ASSERT(!WIFSTOPPED(rawExitStatus)); +#ifdef WIFCONTINUED + PR_ASSERT(!WIFCONTINUED(rawExitStatus)); +#endif + if (WIFEXITED(rawExitStatus)) { + return WEXITSTATUS(rawExitStatus); + } + PR_ASSERT(WIFSIGNALED(rawExitStatus)); + return _PR_SIGNALED_EXITSTATUS; +} + +static void +ProcessReapedChildInternal(pid_t pid, int status) +{ + pr_PidRecord *pRec; + + pRec = FindPidTable(pid); + if (NULL == pRec) { + pRec = PR_NEW(pr_PidRecord); + pRec->pid = pid; + pRec->state = _PR_PID_REAPED; + pRec->exitStatus = ExtractExitStatus(status); + pRec->reapedCV = NULL; + InsertPidTable(pRec); + } else { + PR_ASSERT(pRec->state != _PR_PID_REAPED); + if (_PR_PID_DETACHED == pRec->state) { + PR_ASSERT(NULL == pRec->reapedCV); + DeletePidTable(pRec); + PR_DELETE(pRec); + } else { + PR_ASSERT(_PR_PID_WAITING == pRec->state); + PR_ASSERT(NULL != pRec->reapedCV); + pRec->exitStatus = ExtractExitStatus(status); + pRec->state = _PR_PID_REAPED; + PR_NotifyCondVar(pRec->reapedCV); + } + } +} + +#if defined(_PR_NATIVE_THREADS) + +/* + * If all the threads are native threads, the daemon thread is + * simpler. We don't need to catch the SIGCHLD signal. We can + * just have the daemon thread block in waitpid(). + */ + +static void WaitPidDaemonThread(void *unused) +{ + pid_t pid; + int status; + + while (1) { + PR_Lock(pr_wp.ml); + while (0 == pr_wp.numProcs) { + PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pr_wp.ml); + + while (1) { + do { + pid = waitpid((pid_t) -1, &status, 0); + } while ((pid_t) -1 == pid && EINTR == errno); + + /* + * waitpid() cannot return 0 because we did not invoke it + * with the WNOHANG option. + */ + PR_ASSERT(0 != pid); + + /* + * The only possible error code is ECHILD. But if we do + * our accounting correctly, we should only call waitpid() + * when there is a child process to wait for. + */ + PR_ASSERT((pid_t) -1 != pid); + if ((pid_t) -1 == pid) { + break; + } + + PR_Lock(pr_wp.ml); + ProcessReapedChildInternal(pid, status); + pr_wp.numProcs--; + while (0 == pr_wp.numProcs) { + PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pr_wp.ml); + } + } +} + +#else /* _PR_NATIVE_THREADS */ + +static void WaitPidDaemonThread(void *unused) +{ + PRPollDesc pd; + PRFileDesc *fd; + int rv; + char buf[128]; + pid_t pid; + int status; +#ifdef _PR_SHARE_CLONES + struct pr_CreateProcOp *op; +#endif + +#ifdef _PR_SHARE_CLONES + pr_InstallSigchldHandler(); +#endif + + fd = PR_ImportFile(pr_wp.pipefd[0]); + PR_ASSERT(NULL != fd); + pd.fd = fd; + pd.in_flags = PR_POLL_READ; + + while (1) { + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(1 == rv); + +#ifdef _PR_SHARE_CLONES + if (pr_waitpid_daemon_exit) { + return; + } + PR_Lock(pr_wp.ml); +#endif + + do { + rv = read(pr_wp.pipefd[0], buf, sizeof(buf)); + } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno)); + +#ifdef _PR_SHARE_CLONES + while ((op = pr_wp.opHead) != NULL) { + PR_Unlock(pr_wp.ml); + op->process = ForkAndExec(op->path, op->argv, + op->envp, op->attr); + if (NULL == op->process) { + op->prerror = PR_GetError(); + op->oserror = PR_GetOSError(); + } + PR_Lock(pr_wp.ml); + pr_wp.opHead = op->next; + if (NULL == pr_wp.opHead) { + pr_wp.opTail = NULL; + } + op->done = PR_TRUE; + PR_NotifyCondVar(op->doneCV); + } + PR_Unlock(pr_wp.ml); +#endif + + while (1) { + do { + pid = waitpid((pid_t) -1, &status, WNOHANG); + } while ((pid_t) -1 == pid && EINTR == errno); + if (0 == pid) { + break; + } + if ((pid_t) -1 == pid) { + /* must be because we have no child processes */ + PR_ASSERT(ECHILD == errno); + break; + } + + PR_Lock(pr_wp.ml); + ProcessReapedChildInternal(pid, status); + PR_Unlock(pr_wp.ml); + } + } +} + +static void pr_SigchldHandler(int sig) +{ + int errnoCopy; + int rv; + + errnoCopy = errno; + + do { + rv = write(pr_wp.pipefd[1], "", 1); + } while (-1 == rv && EINTR == errno); + +#ifdef DEBUG + if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) { + char *msg = "cannot write to pipe\n"; + write(2, msg, strlen(msg) + 1); + _exit(1); + } +#endif + + errno = errnoCopy; +} + +static void pr_InstallSigchldHandler() +{ + struct sigaction act, oact; + int rv; + + act.sa_handler = pr_SigchldHandler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NOCLDSTOP | SA_RESTART; + rv = sigaction(SIGCHLD, &act, &oact); + PR_ASSERT(0 == rv); + /* Make sure we are not overriding someone else's SIGCHLD handler */ +#ifndef _PR_SHARE_CLONES + PR_ASSERT(oact.sa_handler == SIG_DFL); +#endif +} + +#endif /* !defined(_PR_NATIVE_THREADS) */ + +static PRStatus _MD_InitProcesses(void) +{ +#if !defined(_PR_NATIVE_THREADS) + int rv; + int flags; +#endif + +#ifdef AIX + { + void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); + pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork"); + if (!pr_wp.forkptr) { + pr_wp.forkptr = fork; + } + dlclose(handle); + } +#endif /* AIX */ + + pr_wp.ml = PR_NewLock(); + PR_ASSERT(NULL != pr_wp.ml); + +#if defined(_PR_NATIVE_THREADS) + pr_wp.numProcs = 0; + pr_wp.cv = PR_NewCondVar(pr_wp.ml); + PR_ASSERT(NULL != pr_wp.cv); +#else + rv = pipe(pr_wp.pipefd); + PR_ASSERT(0 == rv); + flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0); + fcntl(pr_wp.pipefd[0], F_SETFL, flags | O_NONBLOCK); + flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0); + fcntl(pr_wp.pipefd[1], F_SETFL, flags | O_NONBLOCK); + +#ifndef _PR_SHARE_CLONES + pr_InstallSigchldHandler(); +#endif +#endif /* !_PR_NATIVE_THREADS */ + + pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD, + WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL, +#ifdef _PR_SHARE_CLONES + PR_GLOBAL_THREAD, +#else + PR_LOCAL_THREAD, +#endif + PR_JOINABLE_THREAD, 0); + PR_ASSERT(NULL != pr_wp.thread); + + pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *)); + PR_ASSERT(NULL != pr_wp.pidTable); + return PR_SUCCESS; +} + +PRStatus _MD_DetachUnixProcess(PRProcess *process) +{ + PRStatus retVal = PR_SUCCESS; + pr_PidRecord *pRec; + + PR_Lock(pr_wp.ml); + pRec = FindPidTable(process->md.pid); + if (NULL == pRec) { + pRec = PR_NEW(pr_PidRecord); + if (NULL == pRec) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + retVal = PR_FAILURE; + goto done; + } + pRec->pid = process->md.pid; + pRec->state = _PR_PID_DETACHED; + pRec->reapedCV = NULL; + InsertPidTable(pRec); + } else { + PR_ASSERT(_PR_PID_REAPED == pRec->state); + if (_PR_PID_REAPED != pRec->state) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + retVal = PR_FAILURE; + } else { + DeletePidTable(pRec); + PR_ASSERT(NULL == pRec->reapedCV); + PR_DELETE(pRec); + } + } + PR_DELETE(process); + +done: + PR_Unlock(pr_wp.ml); + return retVal; +} + +PRStatus _MD_WaitUnixProcess( + PRProcess *process, + PRInt32 *exitCode) +{ + pr_PidRecord *pRec; + PRStatus retVal = PR_SUCCESS; + PRBool interrupted = PR_FALSE; + + PR_Lock(pr_wp.ml); + pRec = FindPidTable(process->md.pid); + if (NULL == pRec) { + pRec = PR_NEW(pr_PidRecord); + if (NULL == pRec) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + retVal = PR_FAILURE; + goto done; + } + pRec->pid = process->md.pid; + pRec->state = _PR_PID_WAITING; + pRec->reapedCV = PR_NewCondVar(pr_wp.ml); + if (NULL == pRec->reapedCV) { + PR_DELETE(pRec); + retVal = PR_FAILURE; + goto done; + } + InsertPidTable(pRec); + while (!interrupted && _PR_PID_REAPED != pRec->state) { + if (PR_WaitCondVar(pRec->reapedCV, + PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE + && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { + interrupted = PR_TRUE; + } + } + if (_PR_PID_REAPED == pRec->state) { + if (exitCode) { + *exitCode = pRec->exitStatus; + } + } else { + PR_ASSERT(interrupted); + retVal = PR_FAILURE; + } + DeletePidTable(pRec); + PR_DestroyCondVar(pRec->reapedCV); + PR_DELETE(pRec); + } else { + PR_ASSERT(_PR_PID_REAPED == pRec->state); + PR_ASSERT(NULL == pRec->reapedCV); + DeletePidTable(pRec); + if (exitCode) { + *exitCode = pRec->exitStatus; + } + PR_DELETE(pRec); + } + PR_DELETE(process); + +done: + PR_Unlock(pr_wp.ml); + return retVal; +} /* _MD_WaitUnixProcess */ + +PRStatus _MD_KillUnixProcess(PRProcess *process) +{ + PRErrorCode prerror; + PRInt32 oserror; + + if (kill(process->md.pid, SIGKILL) == 0) { + return PR_SUCCESS; + } + oserror = errno; + switch (oserror) { + case EPERM: + prerror = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case ESRCH: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prerror, oserror); + return PR_FAILURE; +} /* _MD_KillUnixProcess */ diff --git a/nsprpub/pr/src/md/unix/uxrng.c b/nsprpub/pr/src/md/unix/uxrng.c new file mode 100644 index 0000000000..479859000a --- /dev/null +++ b/nsprpub/pr/src/md/unix/uxrng.c @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "primpl.h" + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> + + +#if defined(SOLARIS) + +static size_t +GetHighResClock(void *buf, size_t maxbytes) +{ + hrtime_t t; + t = gethrtime(); + if (t) { + return _pr_CopyLowBits(buf, maxbytes, &t, sizeof(t)); + } + return 0; +} + +#elif defined(HPUX) + +#ifdef __ia64 +#include <ia64/sys/inline.h> + +static size_t +GetHighResClock(void *buf, size_t maxbytes) +{ + PRUint64 t; + +#ifdef __GNUC__ + __asm__ __volatile__("mov %0 = ar.itc" : "=r" (t)); +#else + t = _Asm_mov_from_ar(_AREG44); +#endif + return _pr_CopyLowBits(buf, maxbytes, &t, sizeof(t)); +} +#else +static size_t +GetHighResClock(void *buf, size_t maxbytes) +{ + extern int ret_cr16(); + int cr16val; + + cr16val = ret_cr16(); + return(_pr_CopyLowBits(buf, maxbytes, &cr16val, sizeof(cr16val))); +} +#endif + +#elif defined(AIX) + +static size_t +GetHighResClock(void *buf, size_t maxbytes) +{ + return 0; +} + +#elif (defined(LINUX) || defined(FREEBSD) || defined(__FreeBSD_kernel__) \ + || defined(NETBSD) || defined(__NetBSD_kernel__) || defined(OPENBSD) \ + || defined(__GNU__)) +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +static int fdDevURandom; +static PRCallOnceType coOpenDevURandom; + +static PRStatus OpenDevURandom( void ) +{ + fdDevURandom = open( "/dev/urandom", O_RDONLY ); + return((-1 == fdDevURandom)? PR_FAILURE : PR_SUCCESS ); +} /* end OpenDevURandom() */ + +static size_t GetDevURandom( void *buf, size_t size ) +{ + int bytesIn; + int rc; + + rc = PR_CallOnce( &coOpenDevURandom, OpenDevURandom ); + if ( PR_FAILURE == rc ) { + _PR_MD_MAP_OPEN_ERROR( errno ); + return(0); + } + + bytesIn = read( fdDevURandom, buf, size ); + if ( -1 == bytesIn ) { + _PR_MD_MAP_READ_ERROR( errno ); + return(0); + } + + return( bytesIn ); +} /* end GetDevURandom() */ + +static size_t +GetHighResClock(void *buf, size_t maxbytes) +{ + return(GetDevURandom( buf, maxbytes )); +} + +#elif defined(SCO) || defined(UNIXWARE) || defined(BSDI) || defined(NTO) \ + || defined(QNX) || defined(DARWIN) || defined(RISCOS) +#include <sys/times.h> + +static size_t +GetHighResClock(void *buf, size_t maxbytes) +{ + int ticks; + struct tms buffer; + + ticks=times(&buffer); + return _pr_CopyLowBits(buf, maxbytes, &ticks, sizeof(ticks)); +} +#else +#error! Platform undefined +#endif /* defined(SOLARIS) */ + +extern PRSize _PR_MD_GetRandomNoise( void *buf, PRSize size ) +{ + struct timeval tv; + int n = 0; + int s; + + n += GetHighResClock(buf, size); + size -= n; + + GETTIMEOFDAY(&tv); + + if ( size > 0 ) { + s = _pr_CopyLowBits((char*)buf+n, size, &tv.tv_usec, sizeof(tv.tv_usec)); + size -= s; + n += s; + } + if ( size > 0 ) { + s = _pr_CopyLowBits((char*)buf+n, size, &tv.tv_sec, sizeof(tv.tv_usec)); + size -= s; + n += s; + } + + return n; +} /* end _PR_MD_GetRandomNoise() */ diff --git a/nsprpub/pr/src/md/unix/uxshm.c b/nsprpub/pr/src/md/unix/uxshm.c new file mode 100644 index 0000000000..29a6030f44 --- /dev/null +++ b/nsprpub/pr/src/md/unix/uxshm.c @@ -0,0 +1,632 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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" +#include <fcntl.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> + +#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 = (char*)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; + + /* 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> + +#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; + 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 rc; + } + + 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 */ + PRInt64 size64; /* 64-bit version of 'size' */ + + /* + ** 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++ ) { +#define NSPR_AFM_FILENAME "%s/.NSPR-AFM-%d-%p.%d" + genName = PR_smprintf( NSPR_AFM_FILENAME, + 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 */ + } + _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 )); + + LL_UI2L(size64, size); /* PRSize (size_t) is unsigned */ + fm = PR_CreateFileMap( fd, size64, 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 = NULL; /* default return value */ + PRFileInfo64 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_GetOpenFileInfo64( fd, &info ); + if ( PR_FAILURE == rc ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ImportFileMapFromString(): PR_GetOpenFileInfo64() 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/nsprpub/pr/src/md/unix/uxwrap.c b/nsprpub/pr/src/md/unix/uxwrap.c new file mode 100644 index 0000000000..e0ddc50eb8 --- /dev/null +++ b/nsprpub/pr/src/md/unix/uxwrap.c @@ -0,0 +1,512 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + *------------------------------------------------------------------------ + * File: uxwrap.c + * + * Our wrapped versions of the Unix select() and poll() system calls. + * + *------------------------------------------------------------------------ + */ + +#include "primpl.h" + +#if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) || defined(QNX) +/* Do not wrap select() and poll(). */ +#else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ +/* The include files for select() */ +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> + +#define ZAP_SET(_to, _width) \ + PR_BEGIN_MACRO \ + memset(_to, 0, \ + ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \ + * sizeof(int) \ + ); \ + PR_END_MACRO + +/* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */ +static int _pr_xt_hack_fd = -1; + +int PR_XGetXtHackFD(void) +{ + int fds[2]; + + if (_pr_xt_hack_fd == -1) { + if (!pipe(fds)) { + _pr_xt_hack_fd = fds[0]; + } + } + return _pr_xt_hack_fd; +} + +static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0; + +void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void)) +{ + _pr_xt_hack_okayToReleaseXLock = fn; +} + + +/* + *----------------------------------------------------------------------- + * select() -- + * + * Wrap up the select system call so that we can deschedule + * a thread that tries to wait for i/o. + * + *----------------------------------------------------------------------- + */ + +#if defined(HPUX9) +int select(size_t width, int *rl, int *wl, int *el, const struct timeval *tv) +#elif defined(AIX_RENAME_SELECT) +int wrap_select(unsigned long width, void *rl, void *wl, void *el, + struct timeval *tv) +#elif defined(_PR_SELECT_CONST_TIMEVAL) +int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, + const struct timeval *tv) +#else +int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *tv) +#endif +{ + int osfd; + _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; + PRInt32 pdcnt; + PRIntervalTime timeout; + int retVal; +#if defined(HPUX9) || defined(AIX_RENAME_SELECT) + fd_set *rd = (fd_set*) rl; + fd_set *wr = (fd_set*) wl; + fd_set *ex = (fd_set*) el; +#endif + +#if 0 + /* + * Easy special case: zero timeout. Simply call the native + * select() with no fear of blocking. + */ + if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) { +#if defined(HPUX9) || defined(AIX_RENAME_SELECT) + return _MD_SELECT(width, rl, wl, el, tv); +#else + return _MD_SELECT(width, rd, wr, ex, tv); +#endif + } +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#ifndef _PR_LOCAL_THREADS_ONLY + if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { + return _MD_SELECT(width, rd, wr, ex, tv); + } +#endif + + if (width < 0 || width > FD_SETSIZE) { + errno = EINVAL; + return -1; + } + + /* Compute timeout */ + if (tv) { + /* + * These acceptable ranges for t_sec and t_usec are taken + * from the select() man pages. + */ + if (tv->tv_sec < 0 || tv->tv_sec > 100000000 + || tv->tv_usec < 0 || tv->tv_usec >= 1000000) { + errno = EINVAL; + return -1; + } + + /* Convert microseconds to ticks */ + timeout = PR_MicrosecondsToInterval(1000000*tv->tv_sec + tv->tv_usec); + } else { + /* tv being a NULL pointer means blocking indefinitely */ + timeout = PR_INTERVAL_NO_TIMEOUT; + } + + /* Check for no descriptors case (just doing a timeout) */ + if ((!rd && !wr && !ex) || !width) { + PR_Sleep(timeout); + return 0; + } + + /* + * Set up for PR_Poll(). The PRPollDesc array is allocated + * dynamically. If this turns out to have high performance + * penalty, one can change to use a large PRPollDesc array + * on the stack, and allocate dynamically only when it turns + * out to be not large enough. + * + * I allocate an array of size 'width', which is the maximum + * number of fds we may need to poll. + */ + unixpds = (_PRUnixPollDesc *) PR_CALLOC(width * sizeof(_PRUnixPollDesc)); + if (!unixpds) { + errno = ENOMEM; + return -1; + } + + pdcnt = 0; + unixpd = unixpds; + for (osfd = 0; osfd < width; osfd++) { + int in_flags = 0; + if (rd && FD_ISSET(osfd, rd)) { + in_flags |= _PR_UNIX_POLL_READ; + } + if (wr && FD_ISSET(osfd, wr)) { + in_flags |= _PR_UNIX_POLL_WRITE; + } + if (ex && FD_ISSET(osfd, ex)) { + in_flags |= _PR_UNIX_POLL_EXCEPT; + } + if (in_flags) { + unixpd->osfd = osfd; + unixpd->in_flags = in_flags; + unixpd->out_flags = 0; + unixpd++; + pdcnt++; + } + } + + /* + * see comments in mozilla/cmd/xfe/mozilla.c (look for + * "PR_XGetXtHackFD") + */ + { + int needToLockXAgain; + + needToLockXAgain = 0; + if (rd && (_pr_xt_hack_fd != -1) + && FD_ISSET(_pr_xt_hack_fd, rd) && _PR_XIsLocked() + && (!_pr_xt_hack_okayToReleaseXLock + || _pr_xt_hack_okayToReleaseXLock())) { + _PR_XUnlock(); + needToLockXAgain = 1; + } + + /* This is the potentially blocking step */ + retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout); + + if (needToLockXAgain) { + _PR_XLock(); + } + } + + if (retVal > 0) { + /* Compute select results */ + if (rd) { + ZAP_SET(rd, width); + } + if (wr) { + ZAP_SET(wr, width); + } + if (ex) { + ZAP_SET(ex, width); + } + + /* + * The return value can be either the number of ready file + * descriptors or the number of set bits in the three fd_set's. + */ + retVal = 0; /* we're going to recompute */ + eunixpd = unixpds + pdcnt; + for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { + if (unixpd->out_flags) { + int nbits = 0; /* The number of set bits on for this fd */ + + if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { + errno = EBADF; + PR_LOG(_pr_io_lm, PR_LOG_ERROR, + ("select returns EBADF for %d", unixpd->osfd)); + retVal = -1; + break; + } + /* + * If a socket has a pending error, it is considered + * both readable and writable. (See W. Richard Stevens, + * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3, + * pp. 153-154.) We also consider a socket readable if + * it has a hangup condition. + */ + if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ) + && (unixpd->out_flags & (_PR_UNIX_POLL_READ + | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) { + FD_SET(unixpd->osfd, rd); + nbits++; + } + if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) + && (unixpd->out_flags & (_PR_UNIX_POLL_WRITE + | _PR_UNIX_POLL_ERR))) { + FD_SET(unixpd->osfd, wr); + nbits++; + } + if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) + && (unixpd->out_flags & PR_POLL_EXCEPT)) { + FD_SET(unixpd->osfd, ex); + nbits++; + } + PR_ASSERT(nbits > 0); +#if defined(HPUX) || defined(SOLARIS) || defined(AIX) + retVal += nbits; +#endif + } + } + } + + PR_ASSERT(tv || retVal != 0); + PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal)); + PR_DELETE(unixpds); + + return retVal; +} + +/* + * Redefine poll, when supported on platforms, for local threads + */ + +/* + * I am commenting out the poll() wrapper for Linux for now + * because it is difficult to define _MD_POLL that works on all + * Linux varieties. People reported that glibc 2.0.7 on Debian + * 2.0 Linux machines doesn't have the __syscall_poll symbol + * defined. (WTC 30 Nov. 1998) + */ +#if defined(_PR_POLL_AVAILABLE) && !defined(LINUX) + +/* + *----------------------------------------------------------------------- + * poll() -- + * + * RETURN VALUES: + * -1: fails, errno indicates the error. + * 0: timed out, the revents bitmasks are not set. + * positive value: the number of file descriptors for which poll() + * has set the revents bitmask. + * + *----------------------------------------------------------------------- + */ + +#include <poll.h> + +#if defined(AIX_RENAME_SELECT) +int wrap_poll(void *listptr, unsigned long nfds, long timeout) +#elif (defined(AIX) && !defined(AIX_RENAME_SELECT)) +int poll(void *listptr, unsigned long nfds, long timeout) +#elif defined(HPUX) && !defined(HPUX9) +int poll(struct pollfd filedes[], unsigned int nfds, int timeout) +#elif defined(HPUX9) +int poll(struct pollfd filedes[], int nfds, int timeout) +#elif defined(NETBSD) +int poll(struct pollfd *filedes, nfds_t nfds, int timeout) +#elif defined(OPENBSD) +int poll(struct pollfd filedes[], nfds_t nfds, int timeout) +#elif defined(FREEBSD) +int poll(struct pollfd *filedes, unsigned nfds, int timeout) +#else +int poll(struct pollfd *filedes, unsigned long nfds, int timeout) +#endif +{ +#ifdef AIX + struct pollfd *filedes = (struct pollfd *) listptr; +#endif + struct pollfd *pfd, *epfd; + _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; + PRIntervalTime ticks; + PRInt32 pdcnt; + int ready; + + /* + * Easy special case: zero timeout. Simply call the native + * poll() with no fear of blocking. + */ + if (timeout == 0) { +#if defined(AIX) + return _MD_POLL(listptr, nfds, timeout); +#else + return _MD_POLL(filedes, nfds, timeout); +#endif + } + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#ifndef _PR_LOCAL_THREADS_ONLY + if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { + return _MD_POLL(filedes, nfds, timeout); + } +#endif + + /* We do not support the pollmsg structures on AIX */ +#ifdef AIX + PR_ASSERT((nfds & 0xff00) == 0); +#endif + + if (timeout < 0 && timeout != -1) { + errno = EINVAL; + return -1; + } + + /* Convert timeout from miliseconds to ticks */ + if (timeout == -1) { + ticks = PR_INTERVAL_NO_TIMEOUT; + } else { + ticks = PR_MillisecondsToInterval(timeout); + } + + /* Check for no descriptor case (just do a timeout) */ + if (nfds == 0) { + PR_Sleep(ticks); + return 0; + } + + unixpds = (_PRUnixPollDesc *) + PR_MALLOC(nfds * sizeof(_PRUnixPollDesc)); + if (NULL == unixpds) { + errno = EAGAIN; + return -1; + } + + pdcnt = 0; + epfd = filedes + nfds; + unixpd = unixpds; + for (pfd = filedes; pfd < epfd; pfd++) { + /* + * poll() ignores negative fd's. + */ + if (pfd->fd >= 0) { + unixpd->osfd = pfd->fd; +#ifdef _PR_USE_POLL + unixpd->in_flags = pfd->events; +#else + /* + * Map the poll events to one of the three that can be + * represented by the select fd_sets: + * POLLIN, POLLRDNORM ===> readable + * POLLOUT, POLLWRNORM ===> writable + * POLLPRI, POLLRDBAND ===> exception + * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) + * are ignored. + * + * The output events POLLERR and POLLHUP are never turned on. + * POLLNVAL may be turned on. + */ + unixpd->in_flags = 0; + if (pfd->events & (POLLIN +#ifdef POLLRDNORM + | POLLRDNORM +#endif + )) { + unixpd->in_flags |= _PR_UNIX_POLL_READ; + } + if (pfd->events & (POLLOUT +#ifdef POLLWRNORM + | POLLWRNORM +#endif + )) { + unixpd->in_flags |= _PR_UNIX_POLL_WRITE; + } + if (pfd->events & (POLLPRI +#ifdef POLLRDBAND + | POLLRDBAND +#endif + )) { + unixpd->in_flags |= PR_POLL_EXCEPT; + } +#endif /* _PR_USE_POLL */ + unixpd->out_flags = 0; + unixpd++; + pdcnt++; + } + } + + ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks); + if (-1 == ready) { + if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { + errno = EINTR; /* XXX we aren't interrupted by a signal, but... */ + } else { + errno = PR_GetOSError(); + } + } + if (ready <= 0) { + goto done; + } + + /* + * Copy the out_flags from the _PRUnixPollDesc structures to the + * user's pollfd structures and free the allocated memory + */ + unixpd = unixpds; + for (pfd = filedes; pfd < epfd; pfd++) { + pfd->revents = 0; + if (pfd->fd >= 0) { +#ifdef _PR_USE_POLL + pfd->revents = unixpd->out_flags; +#else + if (0 != unixpd->out_flags) { + if (unixpd->out_flags & _PR_UNIX_POLL_READ) { + if (pfd->events & POLLIN) { + pfd->revents |= POLLIN; + } +#ifdef POLLRDNORM + if (pfd->events & POLLRDNORM) { + pfd->revents |= POLLRDNORM; + } +#endif + } + if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) { + if (pfd->events & POLLOUT) { + pfd->revents |= POLLOUT; + } +#ifdef POLLWRNORM + if (pfd->events & POLLWRNORM) { + pfd->revents |= POLLWRNORM; + } +#endif + } + if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) { + if (pfd->events & POLLPRI) { + pfd->revents |= POLLPRI; + } +#ifdef POLLRDBAND + if (pfd->events & POLLRDBAND) { + pfd->revents |= POLLRDBAND; + } +#endif + } + if (unixpd->out_flags & _PR_UNIX_POLL_ERR) { + pfd->revents |= POLLERR; + } + if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { + pfd->revents |= POLLNVAL; + } + if (unixpd->out_flags & _PR_UNIX_POLL_HUP) { + pfd->revents |= POLLHUP; + } + } +#endif /* _PR_USE_POLL */ + unixpd++; + } + } + +done: + PR_DELETE(unixpds); + return ready; +} + +#endif /* !defined(LINUX) */ + +#endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ + +/* uxwrap.c */ + diff --git a/nsprpub/pr/src/md/windows/Makefile.in b/nsprpub/pr/src/md/windows/Makefile.in new file mode 100644 index 0000000000..04bd716d9a --- /dev/null +++ b/nsprpub/pr/src/md/windows/Makefile.in @@ -0,0 +1,72 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +ifeq (,$(filter-out WIN95 WINCE WINMO, $(OS_TARGET))) +CSRCS = \ + ntmisc.c \ + ntsec.c \ + ntsem.c \ + ntinrval.c \ + ntgc.c \ + w95thred.c \ + w95io.c \ + w95cv.c \ + w32rng.c \ + w95sock.c \ + win32_errors.c \ + w32ipcsem.c \ + w32poll.c \ + w32shm.c \ + w95dllmain.c \ + $(NULL) +else +CSRCS = \ + ntdllmn.c \ + ntmisc.c \ + ntsec.c \ + ntsem.c \ + ntinrval.c \ + ntgc.c \ + ntthread.c \ + ntio.c \ + win32_errors.c \ + w32ipcsem.c \ + w32poll.c \ + w32rng.c \ + w32shm.c \ + $(NULL) +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + +# Bug 122433 workaround: disable global optimization (-Og-) on ntio.c. +ifdef MOZ_OPTIMIZE +ifeq ($(OS_TARGET), WINNT) +ifndef NS_USE_GCC +$(OBJDIR)/ntio.$(OBJ_SUFFIX): ntio.c + @$(MAKE_OBJDIR) + $(CC) -Fo$@ -c $(CFLAGS) -Og- $< +endif +endif +endif diff --git a/nsprpub/pr/src/md/windows/ntdllmn.c b/nsprpub/pr/src/md/windows/ntdllmn.c new file mode 100644 index 0000000000..e64b312d09 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntdllmn.c @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * The DLL entry point (DllMain) for NSPR. + * + * The only reason we use DLLMain() now is to find out whether + * the NSPR DLL is statically or dynamically loaded. When + * dynamically loaded, we cannot use static thread-local storage. + * However, static TLS is faster than the TlsXXX() functions. + * So we want to use static TLS whenever we can. A global + * variable _pr_use_static_tls is set in DllMain() during process + * attachment to indicate whether it is safe to use static TLS + * or not. + */ + +#include <windows.h> +#include <primpl.h> + +extern BOOL _pr_use_static_tls; /* defined in ntthread.c */ + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + PRThread *me; + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + /* + * If lpvReserved is NULL, we are dynamically loaded + * and therefore can't use static thread-local storage. + */ + if (lpvReserved == NULL) { + _pr_use_static_tls = FALSE; + } else { + _pr_use_static_tls = TRUE; + } + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + if (_pr_initialized) { + me = _MD_GET_ATTACHED_THREAD(); + if ((me != NULL) && (me->flags & _PR_ATTACHED)) { + _PRI_DetachThread(); + } + } + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/nsprpub/pr/src/md/windows/ntgc.c b/nsprpub/pr/src/md/windows/ntgc.c new file mode 100644 index 0000000000..20ad8a81e4 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntgc.c @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * GC related routines + * + */ +#include <windows.h> +#include "primpl.h" + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#if defined(_X86_) + CONTEXT context; + context.ContextFlags = CONTEXT_INTEGER; + + if (_PR_IS_NATIVE_THREAD(t)) { + context.ContextFlags |= CONTEXT_CONTROL; + if (GetThreadContext(t->md.handle, &context)) { + t->md.gcContext[0] = context.Eax; + t->md.gcContext[1] = context.Ebx; + t->md.gcContext[2] = context.Ecx; + t->md.gcContext[3] = context.Edx; + t->md.gcContext[4] = context.Esi; + t->md.gcContext[5] = context.Edi; + t->md.gcContext[6] = context.Esp; + t->md.gcContext[7] = context.Ebp; + *np = PR_NUM_GCREGS; + } else { + PR_ASSERT(0);/* XXX */ + } + } else { + /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * This code is extremely machine dependant and completely + * undocumented by MS. Its only known to work experimentally. + * Ready for a walk on the wild * side? + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ + +#if !defined WIN95 // Win95 does not have fibers + int *fiberData = t->md.fiber_id; + + /* I found these offsets by disassembling SwitchToFiber(). + * Are your palms sweating yet? + */ + + /* + ** EAX is on the stack (ESP+0) + ** EDX is on the stack (ESP+4) + ** ECX is on the stack (ESP+8) + */ + t->md.gcContext[0] = 0; /* context.Eax */ + t->md.gcContext[1] = fiberData[0x2e]; /* context.Ebx */ + t->md.gcContext[2] = 0; /* context.Ecx */ + t->md.gcContext[3] = 0; /* context.Edx */ + t->md.gcContext[4] = fiberData[0x2d]; /* context.Esi */ + t->md.gcContext[5] = fiberData[0x2c]; /* context.Edi */ + t->md.gcContext[6] = fiberData[0x36]; /* context.Esp */ + t->md.gcContext[7] = fiberData[0x32]; /* context.Ebp */ + *np = PR_NUM_GCREGS; +#endif + } + return (PRWord *)&t->md.gcContext; +#else + PR_NOT_REACHED("not implemented"); + return NULL; +#endif /* defined(_X86_) */ +} + +/* This function is not used right now, but is left as a reference. + * If you ever need to get the fiberID from the currently running fiber, + * this is it. + */ +void * +GetMyFiberID() +{ +#if defined(_X86_) && !defined(__MINGW32__) + void *fiberData; + + /* A pointer to our tib entry is found at FS:[18] + * At offset 10h is the fiberData pointer. The context of the + * fiber is stored in there. + */ + __asm { + mov EDX, FS:[18h] + mov EAX, DWORD PTR [EDX+10h] + mov [fiberData], EAX + } + + return fiberData; +#else + PR_NOT_REACHED("not implemented"); + return NULL; +#endif /* defined(_X86_) */ +} diff --git a/nsprpub/pr/src/md/windows/ntinrval.c b/nsprpub/pr/src/md/windows/ntinrval.c new file mode 100644 index 0000000000..a447804558 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntinrval.c @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * NT interval timers + * + */ + +/* Mozilla's build system defines this globally. */ +#ifdef WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#endif +#include "primpl.h" + +#ifdef WINCE +typedef DWORD (*IntervalFuncType)(void); +static IntervalFuncType intervalFunc; +#endif + +void +_PR_MD_INTERVAL_INIT() +{ +#ifdef WINCE + HMODULE mmtimerlib = LoadLibraryW(L"mmtimer.dll"); /* XXX leaked! */ + if (mmtimerlib) { + intervalFunc = (IntervalFuncType)GetProcAddress(mmtimerlib, + "timeGetTime"); + } else { + intervalFunc = &GetTickCount; + } +#endif +} + +PRIntervalTime +_PR_MD_GET_INTERVAL() +{ + /* milliseconds since system start */ +#ifdef WINCE + return (*intervalFunc)(); +#else + return timeGetTime(); +#endif +} + +PRIntervalTime +_PR_MD_INTERVAL_PER_SEC() +{ + return 1000; +} diff --git a/nsprpub/pr/src/md/windows/ntio.c b/nsprpub/pr/src/md/windows/ntio.c new file mode 100644 index 0000000000..40f5200789 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntio.c @@ -0,0 +1,4761 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Windows NT IO module + * + * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads. + * For LOCAL_SCOPE threads, we're using NT fibers. For GLOBAL_SCOPE threads + * we're using NT-native threads. + * + * When doing IO, we want to use completion ports for optimal performance + * with fibers. But if we use completion ports for all IO, it is difficult + * to project a blocking model with GLOBAL_SCOPE threads. To handle this + * we create an extra thread for completing IO for GLOBAL_SCOPE threads. + * We don't really want to complete IO on a separate thread for LOCAL_SCOPE + * threads because it means extra context switches, which are really slow + * on NT... Since we're using a single completion port, some IO will + * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean + * extra context switching; but I don't think there is anything I can do + * about it. + */ + +#include "primpl.h" +#include "pprmwait.h" +#include <direct.h> +#include <mbstring.h> + +static HANDLE _pr_completion_port; +static PRThread *_pr_io_completion_thread; + +#define RECYCLE_SIZE 512 +static struct _MDLock _pr_recycle_lock; +static PRInt32 _pr_recycle_INET_array[RECYCLE_SIZE]; +static PRInt32 _pr_recycle_INET_tail = 0; +static PRInt32 _pr_recycle_INET6_array[RECYCLE_SIZE]; +static PRInt32 _pr_recycle_INET6_tail = 0; + +__declspec(thread) PRThread *_pr_io_restarted_io = NULL; +DWORD _pr_io_restartedIOIndex; /* The thread local storage slot for each + * thread is initialized to NULL. */ + +PRBool _nt_version_gets_lockfile_completion; + +struct _MDLock _pr_ioq_lock; +extern _MDLock _nt_idleLock; +extern PRCList _nt_idleList; +extern PRUint32 _nt_idleCount; + +#define CLOSE_TIMEOUT PR_SecondsToInterval(5) + +/* + * NSPR-to-NT access right mapping table for files. + */ +static DWORD fileAccessTable[] = { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE +}; + +/* + * NSPR-to-NT access right mapping table for directories. + */ +static DWORD dirAccessTable[] = { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE|FILE_DELETE_CHILD, + FILE_GENERIC_EXECUTE +}; + +static PRBool IsPrevCharSlash(const char *str, const char *current); + +#define _NEED_351_FILE_LOCKING_HACK +#ifdef _NEED_351_FILE_LOCKING_HACK +#define _PR_LOCAL_FILE 1 +#define _PR_REMOTE_FILE 2 +PRBool IsFileLocalInit(); +PRInt32 IsFileLocal(HANDLE hFile); +#endif /* _NEED_351_FILE_LOCKING_HACK */ + +static PRInt32 _md_MakeNonblock(HANDLE); + +static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime); +static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime); +static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime); +static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime); +static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime); +static PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime); +static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime); + +/* + * We cannot associate a fd (a socket) with an I/O completion port + * if the fd is nonblocking or inheritable. + * + * Nonblocking socket I/O won't work if the socket is associated with + * an I/O completion port. + * + * An inheritable fd cannot be associated with an I/O completion port + * because the completion notification of async I/O initiated by the + * child process is still posted to the I/O completion port in the + * parent process. + */ +#define _NT_USE_NB_IO(fd) \ + ((fd)->secret->nonblocking || (fd)->secret->inheritable == _PR_TRI_TRUE) + +/* + * UDP support + * + * UDP is supported on NT by the continuation thread mechanism. + * The code is borrowed from ptio.c in pthreads nspr, hence the + * PT and pt prefixes. This mechanism is in fact general and + * not limited to UDP. For now, only UDP's recvfrom and sendto + * go through the continuation thread if they get WSAEWOULDBLOCK + * on first try. Recv and send on a connected UDP socket still + * goes through asychronous io. + */ + +#define PT_DEFAULT_SELECT_MSEC 100 + +typedef struct pt_Continuation pt_Continuation; +typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent); + +typedef enum pr_ContuationStatus +{ + pt_continuation_sumbitted, + pt_continuation_inprogress, + pt_continuation_abort, + pt_continuation_done +} pr_ContuationStatus; + +struct pt_Continuation +{ + /* These objects are linked in ascending timeout order */ + pt_Continuation *next, *prev; /* self linked list of these things */ + + /* The building of the continuation operation */ + ContinuationFn function; /* what function to continue */ + union { + SOCKET osfd; + } arg1; /* #1 - the op's fd */ + union { + void* buffer; + } arg2; /* #2 - primary transfer buffer */ + union { + PRIntn amount; + } arg3; /* #3 - size of 'buffer' */ + union { + PRIntn flags; + } arg4; /* #4 - read/write flags */ + union { + PRNetAddr *addr; + } arg5; /* #5 - send/recv address */ + + PRIntervalTime timeout; /* representation of the timeout */ + + PRIntn event; /* flags for select()'s events */ + + /* + ** The representation and notification of the results of the operation. + ** These function can either return an int return code or a pointer to + ** some object. + */ + union { + PRIntn code; + void *object; + } result; + + PRIntn syserrno; /* in case it failed, why (errno) */ + pr_ContuationStatus status; /* the status of the operation */ + PRCondVar *complete; /* to notify the initiating thread */ +}; + +static struct pt_TimedQueue +{ + PRLock *ml; /* a little protection */ + PRThread *thread; /* internal thread's identification */ + PRCondVar *new_op; /* new operation supplied */ + PRCondVar *finish_op; /* an existing operation finished */ + PRUintn op_count; /* number of operations in the list */ + pt_Continuation *head, *tail; /* head/tail of list of operations */ + + pt_Continuation *op; /* timed operation furthest in future */ + PRIntervalTime epoch; /* the epoch of 'timed' */ +} pt_tq; + +#if defined(DEBUG) +static struct pt_debug_s +{ + PRIntn predictionsFoiled; + PRIntn pollingListMax; + PRIntn continuationsServed; +} pt_debug; +#endif /* DEBUG */ + +static void ContinuationThread(void *arg); +static PRInt32 pt_SendTo( + SOCKET osfd, const void *buf, + PRInt32 amount, PRInt32 flags, const PRNetAddr *addr, + PRIntn addrlen, PRIntervalTime timeout); +static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount, + PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout); + + +/* The key returned from GetQueuedCompletionStatus() is used to determine what + * type of completion we have. We differentiate between IO completions and + * CVAR completions. + */ +#define KEY_IO 0xaaaaaaaa +#define KEY_CVAR 0xbbbbbbbb + +PRInt32 +_PR_MD_PAUSE_CPU(PRIntervalTime ticks) +{ + int awoken = 0; + unsigned long bytes, key; + int rv; + LPOVERLAPPED olp; + _MDOverlapped *mdOlp; + PRUint32 timeout; + + if (_nt_idleCount > 0) { + PRThread *deadThread; + + _MD_LOCK(&_nt_idleLock); + while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) { + deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList)); + PR_REMOVE_LINK(&deadThread->links); + + PR_ASSERT(deadThread->state == _PR_DEAD_STATE); + + /* XXXMB - cleanup to do here? */ + if ( !_PR_IS_NATIVE_THREAD(deadThread) ) { + /* Spinlock while user thread is still running. + * There is no way to use a condition variable here. The thread + * is dead, and we have to wait until we switch off the dead + * thread before we can kill the fiber completely. + */ + while ( deadThread->no_sched) + ; + + DeleteFiber(deadThread->md.fiber_id); + } + memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */ + if (!deadThread->threadAllocatedOnStack) { + PR_DELETE(deadThread); + } + _nt_idleCount--; + } + _MD_UNLOCK(&_nt_idleLock); + } + + if (ticks == PR_INTERVAL_NO_TIMEOUT) +#if 0 + timeout = INFINITE; +#else + /* + * temporary hack to poll the runq every 5 seconds because of bug in + * native threads creating user threads and not poking the right cpu. + * + * A local thread that was interrupted is bound to its current + * cpu but there is no easy way for the interrupter to poke the + * right cpu. This is a hack to poll the runq every 5 seconds. + */ + timeout = 5000; +#endif + else { + timeout = PR_IntervalToMilliseconds(ticks); + } + + /* + * The idea of looping here is to complete as many IOs as possible before + * returning. This should minimize trips to the idle thread. + */ + while(1) { + rv = GetQueuedCompletionStatus( + _pr_completion_port, + &bytes, + &key, + &olp, + timeout); + if (rv == 0 && olp == NULL) { + /* Error in GetQueuedCompetionStatus */ + if (GetLastError() != WAIT_TIMEOUT) { + /* ARGH - what can we do here? Log an error? XXXMB */ + return -1; + } else { + /* If awoken == 0, then we just had a timeout */ + return awoken; + } + } + + if (olp == NULL) { + return 0; + } + + mdOlp = (_MDOverlapped *)olp; + + if (mdOlp->ioModel == _MD_MultiWaitIO) { + PRRecvWait *desc; + PRWaitGroup *group; + PRThread *thred = NULL; + PRMWStatus mwstatus; + + desc = mdOlp->data.mw.desc; + PR_ASSERT(desc != NULL); + mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE; + if (InterlockedCompareExchange((PVOID *)&desc->outcome, + (PVOID)mwstatus, (PVOID)PR_MW_PENDING) + == (PVOID)PR_MW_PENDING) { + if (mwstatus == PR_MW_SUCCESS) { + desc->bytesRecv = bytes; + } else { + mdOlp->data.mw.error = GetLastError(); + } + } + group = mdOlp->data.mw.group; + PR_ASSERT(group != NULL); + + _PR_MD_LOCK(&group->mdlock); + PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready); + PR_ASSERT(desc->fd != NULL); + NT_HashRemoveInternal(group, desc->fd); + if (!PR_CLIST_IS_EMPTY(&group->wait_list)) { + thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list)); + PR_REMOVE_LINK(&thred->waitQLinks); + } + _PR_MD_UNLOCK(&group->mdlock); + + if (thred) { + if (!_PR_IS_NATIVE_THREAD(thred)) { + int pri = thred->priority; + _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU(); + _PR_THREAD_LOCK(thred); + if (thred->flags & _PR_ON_PAUSEQ) { + _PR_SLEEPQ_LOCK(thred->cpu); + _PR_DEL_SLEEPQ(thred, PR_TRUE); + _PR_SLEEPQ_UNLOCK(thred->cpu); + _PR_THREAD_UNLOCK(thred); + thred->cpu = lockedCPU; + thred->state = _PR_RUNNABLE; + _PR_RUNQ_LOCK(lockedCPU); + _PR_ADD_RUNQ(thred, lockedCPU, pri); + _PR_RUNQ_UNLOCK(lockedCPU); + } else { + /* + * The thread was just interrupted and moved + * from the pause queue to the run queue. + */ + _PR_THREAD_UNLOCK(thred); + } + } else { + _PR_THREAD_LOCK(thred); + thred->state = _PR_RUNNABLE; + _PR_THREAD_UNLOCK(thred); + ReleaseSemaphore(thred->md.blocked_sema, 1, NULL); + } + } + } else { + PRThread *completed_io; + + PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO); + completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread); + completed_io->md.blocked_io_status = rv; + if (rv == 0) { + completed_io->md.blocked_io_error = GetLastError(); + } + completed_io->md.blocked_io_bytes = bytes; + + if ( !_PR_IS_NATIVE_THREAD(completed_io) ) { + int pri = completed_io->priority; + _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU(); + + /* The KEY_CVAR notification only occurs when a native thread + * is notifying a user thread. For user-user notifications + * the wakeup occurs by having the notifier place the thread + * on the runq directly; for native-native notifications the + * wakeup occurs by calling ReleaseSemaphore. + */ + if ( key == KEY_CVAR ) { + PR_ASSERT(completed_io->io_pending == PR_FALSE); + PR_ASSERT(completed_io->io_suspended == PR_FALSE); + PR_ASSERT(completed_io->md.thr_bound_cpu == NULL); + + /* Thread has already been deleted from sleepQ */ + + /* Switch CPU and add to runQ */ + completed_io->cpu = lockedCPU; + completed_io->state = _PR_RUNNABLE; + _PR_RUNQ_LOCK(lockedCPU); + _PR_ADD_RUNQ(completed_io, lockedCPU, pri); + _PR_RUNQ_UNLOCK(lockedCPU); + } else { + PR_ASSERT(key == KEY_IO); + PR_ASSERT(completed_io->io_pending == PR_TRUE); + + _PR_THREAD_LOCK(completed_io); + + completed_io->io_pending = PR_FALSE; + + /* If io_suspended is true, then this IO has already resumed. + * We don't need to do anything; because the thread is + * already running. + */ + if (completed_io->io_suspended == PR_FALSE) { + if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) { + _PR_SLEEPQ_LOCK(completed_io->cpu); + _PR_DEL_SLEEPQ(completed_io, PR_TRUE); + _PR_SLEEPQ_UNLOCK(completed_io->cpu); + + _PR_THREAD_UNLOCK(completed_io); + + /* + * 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(completed_io->cpu); + _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri); + _PR_RUNQ_UNLOCK(completed_io->cpu); + } else { + _PR_THREAD_UNLOCK(completed_io); + } + } else { + _PR_THREAD_UNLOCK(completed_io); + } + } + } else { + /* For native threads, they are only notified through this loop + * when completing IO. So, don't worry about this being a CVAR + * notification, because that is not possible. + */ + _PR_THREAD_LOCK(completed_io); + completed_io->io_pending = PR_FALSE; + if (completed_io->io_suspended == PR_FALSE) { + completed_io->state = _PR_RUNNABLE; + _PR_THREAD_UNLOCK(completed_io); + rv = ReleaseSemaphore(completed_io->md.blocked_sema, + 1, NULL); + PR_ASSERT(0 != rv); + } else { + _PR_THREAD_UNLOCK(completed_io); + } + } + } + + awoken++; + timeout = 0; /* Don't block on subsequent trips through the loop */ + } + + /* never reached */ + 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); + rv = WaitForSingleObject(thread->md.blocked_sema, msecs); + switch(rv) { + case WAIT_OBJECT_0: + return PR_SUCCESS; + break; + case WAIT_TIMEOUT: + _PR_THREAD_LOCK(thread); + if (thread->state == _PR_IO_WAIT) { + if (thread->io_pending == PR_TRUE) { + thread->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 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); + } + } 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 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; + } + } else { + PRInt32 is; + + _PR_INTSOFF(is); + _PR_MD_SWITCH_CONTEXT(thread); + } + + 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); + + /* The IO may have already completed; if so, don't add to sleepQ, + * since we are already on the runQ! + */ + if (thread->io_pending == PR_TRUE) { + _PR_SLEEPQ_LOCK(thread->cpu); + _PR_ADD_SLEEPQ(thread, timeout); + _PR_SLEEPQ_UNLOCK(thread->cpu); + } else { + fWait = PR_FALSE; + } + _PR_THREAD_UNLOCK(thread); + } + if (fWait) { + return _PR_MD_WAIT(thread, timeout); + } + else { + return PR_SUCCESS; + } +} + +/* + * Unblock threads waiting for I/O + * used when interrupting threads + * + * NOTE: The thread lock should held when this function is called. + * On return, the thread lock is released. + */ +void _PR_Unblock_IO_Wait(PRThread *thr) +{ + PRStatus rv; + _PRCPU *cpu = thr->cpu; + + PR_ASSERT(thr->state == _PR_IO_WAIT); + /* + * 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; + + if (!_PR_IS_NATIVE_THREAD(thr)) { + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ)); + _PR_SLEEPQ_LOCK(cpu); + _PR_DEL_SLEEPQ(thr, PR_TRUE); + _PR_SLEEPQ_UNLOCK(cpu); + /* + * 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 = cpu; + + PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD)); + _PR_AddThreadToRunQ(me, thr); + } + _PR_THREAD_UNLOCK(thr); + rv = _PR_MD_WAKEUP_WAITER(thr); + PR_ASSERT(PR_SUCCESS == rv); +} + +/* Resume an outstanding IO; requires that after the switch, we disable */ +static PRStatus +_NT_ResumeIO(PRThread *thread, PRIntervalTime ticks) +{ + PRBool fWait = PR_TRUE; + + if (!_PR_IS_NATIVE_THREAD(thread)) { + if (_pr_use_static_tls) { + _pr_io_restarted_io = thread; + } else { + TlsSetValue(_pr_io_restartedIOIndex, thread); + } + } else { + _PR_THREAD_LOCK(thread); + if (!thread->io_pending) { + fWait = PR_FALSE; + } + thread->io_suspended = PR_FALSE; + + _PR_THREAD_UNLOCK(thread); + } + /* We don't put ourselves back on the sleepQ yet; until we + * set the suspended bit to false, we can't do that. Just save + * the sleep time here, and then continue. The restarted_io handler + * will add us to the sleepQ if needed. + */ + thread->sleep = ticks; + + if (fWait) { + if (!_PR_IS_NATIVE_THREAD(thread)) { + return _PR_MD_WAIT(thread, ticks); + } + else { + return _NT_IO_WAIT(thread, ticks); + } + } + return PR_SUCCESS; +} + +PRStatus +_PR_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread == NULL) { + /* If thread is NULL, we aren't waking a thread, we're just poking + * idle thread + */ + if ( PostQueuedCompletionStatus(_pr_completion_port, 0, + KEY_CVAR, NULL) == FALSE) { + return PR_FAILURE; + } + return PR_SUCCESS; + } + + if ( _PR_IS_NATIVE_THREAD(thread) ) { + if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) { + return PR_FAILURE; + } + else { + return PR_SUCCESS; + } + } else { + PRThread *me = _PR_MD_CURRENT_THREAD(); + + /* When a Native thread has to awaken a user thread, it has to poke + * the completion port because all user threads might be idle, and + * thus the CPUs are just waiting for a completion. + * + * XXXMB - can we know when we are truely idle (and not checking + * the runq)? + */ + if ((_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) && + (!thread->md.thr_bound_cpu)) { + /* The thread should not be in any queue */ + PR_ASSERT(thread->queueCount == 0); + if ( PostQueuedCompletionStatus(_pr_completion_port, 0, + KEY_CVAR, &(thread->md.overlapped.overlapped)) == FALSE) { + return PR_FAILURE; + } + } + return PR_SUCCESS; + } +} + +void +_PR_MD_INIT_IO() +{ + WORD WSAVersion = 0x0101; + WSADATA WSAData; + int err; + OSVERSIONINFO OSversion; + + err = WSAStartup( WSAVersion, &WSAData ); + PR_ASSERT(0 == err); + + _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, + NULL, + 0, + 0); + + _MD_NEW_LOCK(&_pr_recycle_lock); + _MD_NEW_LOCK(&_pr_ioq_lock); + + OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (GetVersionEx(&OSversion)) { + _nt_version_gets_lockfile_completion = PR_FALSE; + if (OSversion.dwMajorVersion >= 4) { + _nt_version_gets_lockfile_completion = PR_TRUE; + } + } else { + PR_ASSERT(0); + } + +#ifdef _NEED_351_FILE_LOCKING_HACK + IsFileLocalInit(); +#endif /* _NEED_351_FILE_LOCKING_HACK */ + + /* + * UDP support: start up the continuation thread + */ + + pt_tq.op_count = 0; + pt_tq.head = pt_tq.tail = NULL; + pt_tq.ml = PR_NewLock(); + PR_ASSERT(NULL != pt_tq.ml); + pt_tq.new_op = PR_NewCondVar(pt_tq.ml); + PR_ASSERT(NULL != pt_tq.new_op); +#if defined(DEBUG) + memset(&pt_debug, 0, sizeof(struct pt_debug_s)); +#endif + + pt_tq.thread = PR_CreateThread( + PR_SYSTEM_THREAD, ContinuationThread, NULL, + PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + + PR_ASSERT(NULL != pt_tq.thread); + +#ifdef DEBUG + /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */ + { + SYSTEMTIME systime; + union { + PRTime prt; + FILETIME ft; + } filetime; + BOOL rv; + + systime.wYear = 1970; + systime.wMonth = 1; + /* wDayOfWeek is ignored */ + systime.wDay = 1; + systime.wHour = 0; + systime.wMinute = 0; + systime.wSecond = 0; + systime.wMilliseconds = 0; + + rv = SystemTimeToFileTime(&systime, &filetime.ft); + PR_ASSERT(0 != rv); + PR_ASSERT(filetime.prt == _pr_filetime_offset); + } +#endif /* DEBUG */ + + _PR_NT_InitSids(); +} + +/* --- SOCKET IO --------------------------------------------------------- */ + +/* _md_get_recycled_socket() + * Get a socket from the recycle bin; if no sockets are in the bin, + * create one. The socket will be passed to AcceptEx() as the + * second argument. + */ +static SOCKET +_md_get_recycled_socket(int af) +{ + SOCKET rv; + + _MD_LOCK(&_pr_recycle_lock); + if (af == AF_INET && _pr_recycle_INET_tail) { + _pr_recycle_INET_tail--; + rv = _pr_recycle_INET_array[_pr_recycle_INET_tail]; + _MD_UNLOCK(&_pr_recycle_lock); + return rv; + } + if (af == AF_INET6 && _pr_recycle_INET6_tail) { + _pr_recycle_INET6_tail--; + rv = _pr_recycle_INET6_array[_pr_recycle_INET6_tail]; + _MD_UNLOCK(&_pr_recycle_lock); + return rv; + } + _MD_UNLOCK(&_pr_recycle_lock); + + rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0); + if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) { + closesocket(rv); + return INVALID_SOCKET; + } + return rv; +} + +/* _md_put_recycled_socket() + * Add a socket to the recycle bin. + */ +static void +_md_put_recycled_socket(SOCKET newsock, int af) +{ + PR_ASSERT(_pr_recycle_INET_tail >= 0); + PR_ASSERT(_pr_recycle_INET6_tail >= 0); + + _MD_LOCK(&_pr_recycle_lock); + if (af == AF_INET && _pr_recycle_INET_tail < RECYCLE_SIZE) { + _pr_recycle_INET_array[_pr_recycle_INET_tail] = newsock; + _pr_recycle_INET_tail++; + _MD_UNLOCK(&_pr_recycle_lock); + } else if (af == AF_INET6 && _pr_recycle_INET6_tail < RECYCLE_SIZE) { + _pr_recycle_INET6_array[_pr_recycle_INET6_tail] = newsock; + _pr_recycle_INET6_tail++; + _MD_UNLOCK(&_pr_recycle_lock); + } else { + _MD_UNLOCK(&_pr_recycle_lock); + closesocket(newsock); + } + + return; +} + +/* _md_Associate() + * Associates a file with the completion port. + * Returns 0 on failure, 1 on success. + */ +PRInt32 +_md_Associate(HANDLE file) +{ + HANDLE port; + + 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); + } else { + return 1; + } +} + +/* + * _md_MakeNonblock() + * Make a socket nonblocking. + * Returns 0 on failure, 1 on success. + */ +static PRInt32 +_md_MakeNonblock(HANDLE file) +{ + int rv; + u_long one = 1; + + rv = ioctlsocket((SOCKET)file, FIONBIO, &one); + /* XXX should map error codes on failures */ + return (rv == 0); +} + +static int missing_completions = 0; +static int max_wait_loops = 0; + +static PRInt32 +_NT_IO_ABORT(PROsfd sock) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRBool fWait; + PRInt32 rv; + int loop_count; + + /* This is a clumsy way to abort the IO, but it is all we can do. + * It looks a bit racy, but we handle all the cases. + * case 1: IO completes before calling closesocket + * case 1a: fWait is set to PR_FALSE + * This should e the most likely case. We'll properly + * not wait call _NT_IO_WAIT, since the closesocket() + * won't be forcing a completion. + * case 1b: fWait is set to PR_TRUE + * This hopefully won't happen much. When it does, this + * thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL + * before cleaning up. + * case 2: IO does not complete before calling closesocket + * case 2a: IO never completes + * This is the likely case. We'll close it and wait + * for the completion forced by the close. Return should + * be immediate. + * case 2b: IO completes just after calling closesocket + * Since the closesocket is issued, we'll either get a + * completion back for the real IO or for the close. We + * don't really care. It may not even be possible to get + * a real completion here. In any event, we'll awaken + * from NT_IO_WAIT immediately. + */ + + _PR_THREAD_LOCK(me); + fWait = me->io_pending; + if (fWait) { + /* + * If there's still I/O pending, it should have already timed + * out once before this function is called. + */ + PR_ASSERT(me->io_suspended == PR_TRUE); + + /* Set up to wait for I/O completion again */ + me->state = _PR_IO_WAIT; + me->io_suspended = PR_FALSE; + me->md.interrupt_disabled = PR_TRUE; + } + _PR_THREAD_UNLOCK(me); + + /* Close the socket if there is one */ + if (sock != INVALID_SOCKET) { + rv = closesocket((SOCKET)sock); + } + + /* If there was I/O pending before the close, wait for it to complete */ + if (fWait) { + + /* Wait and wait for the I/O to complete */ + for (loop_count = 0; fWait; ++loop_count) { + + _NT_IO_WAIT(me, CLOSE_TIMEOUT); + + _PR_THREAD_LOCK(me); + fWait = me->io_pending; + if (fWait) { + PR_ASSERT(me->io_suspended == PR_TRUE); + me->state = _PR_IO_WAIT; + me->io_suspended = PR_FALSE; + } + _PR_THREAD_UNLOCK(me); + + if (loop_count > max_wait_loops) { + max_wait_loops = loop_count; + } + } + + if (loop_count > 1) { + ++missing_completions; + } + + me->md.interrupt_disabled = PR_FALSE; + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + } + + PR_ASSERT(me->io_pending == PR_FALSE); + me->md.thr_bound_cpu = NULL; + me->io_suspended = PR_FALSE; + + return rv; +} + + +PROsfd +_PR_MD_SOCKET(int af, int type, int flags) +{ + SOCKET sock; + + sock = socket(af, type, flags); + + if (sock == INVALID_SOCKET) { + _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError()); + } + + return (PROsfd)sock; +} + +struct connect_data_s { + PRInt32 status; + PRInt32 error; + PROsfd osfd; + struct sockaddr *addr; + PRUint32 addrlen; + PRIntervalTime timeout; +}; + +void +_PR_MD_connect_thread(void *cdata) +{ + struct connect_data_s *cd = (struct connect_data_s *)cdata; + + cd->status = connect(cd->osfd, cd->addr, cd->addrlen); + + if (cd->status == SOCKET_ERROR) { + cd->error = WSAGetLastError(); + } + + return; +} + + +PRInt32 +_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, + PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + u_long nbio; + PRInt32 rc; + + if (fd->secret->nonblocking) { + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + + if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) { + err = WSAGetLastError(); + _PR_MD_MAP_CONNECT_ERROR(err); + } + return rv; + } + + /* + * Temporarily make the socket non-blocking so that we can + * initiate a non-blocking connect and wait for its completion + * (with a timeout) in select. + */ + PR_ASSERT(!fd->secret->md.io_model_committed); + nbio = 1; + rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio); + PR_ASSERT(0 == rv); + + rc = _nt_nonblock_connect(fd, (struct sockaddr *) addr, addrlen, timeout); + + /* Set the socket back to blocking. */ + nbio = 0; + rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio); + PR_ASSERT(0 == rv); + + return rc; +} + +PRInt32 +_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) +{ + PRInt32 rv; +#if 0 + int one = 1; +#endif + + rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen); + + if (rv == SOCKET_ERROR) { + _PR_MD_MAP_BIND_ERROR(WSAGetLastError()); + return -1; + } + +#if 0 + /* Disable nagle- so far unknown if this is good or not... + */ + rv = setsockopt(fd->secret->md.osfd, + SOL_SOCKET, + TCP_NODELAY, + (const char *)&one, + sizeof(one)); + PR_ASSERT(rv == 0); +#endif + + return 0; +} + +void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd accept_sock, PROsfd listen_sock) +{ + /* Sockets accept()'d with AcceptEx need to call this setsockopt before + * calling anything other than ReadFile(), WriteFile(), send(), recv(), + * Transmitfile(), and closesocket(). In order to call any other + * winsock functions, we have to make this setsockopt call. + * + * XXXMB - For the server, we *NEVER* need this in + * the "normal" code path. But now we have to call it. This is a waste + * of a system call. We'd like to only call it before calling the + * obscure socket calls, but since we don't know at that point what the + * original socket was (or even if it is still alive) we can't do it + * at that point... + */ + setsockopt((SOCKET)accept_sock, + SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, + (char *)&listen_sock, + sizeof(listen_sock)); + +} + +#define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16) +PROsfd +_PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen, + PRIntervalTime timeout, PRBool fast, + _PR_AcceptTimeoutCallback callback, void *callbackArg) +{ + PROsfd osfd = fd->secret->md.osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + SOCKET accept_sock; + int bytes; + PRNetAddr *Laddr; + PRNetAddr *Raddr; + PRUint32 llen, err; + int rv; + + if (_NT_USE_NB_IO(fd)) { + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + /* + * The accepted socket inherits the nonblocking and + * inheritable (HANDLE_FLAG_INHERIT) attributes of + * the listening socket. + */ + accept_sock = _nt_nonblock_accept(fd, (struct sockaddr *)raddr, rlen, timeout); + if (!fd->secret->nonblocking) { + u_long zero = 0; + + rv = ioctlsocket(accept_sock, FIONBIO, &zero); + PR_ASSERT(0 == rv); + } + return accept_sock; + } + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + if (!fd->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + + if (!me->md.acceptex_buf) { + me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED); + if (!me->md.acceptex_buf) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + } + + accept_sock = _md_get_recycled_socket(fd->secret->af); + if (accept_sock == INVALID_SOCKET) { + 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); + closesocket(accept_sock); + return -1; + } + me->io_pending = PR_TRUE; + me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = osfd; + + rv = AcceptEx((SOCKET)osfd, + accept_sock, + me->md.acceptex_buf, + 0, + INET_ADDR_PADDED, + INET_ADDR_PADDED, + &bytes, + &(me->md.overlapped.overlapped)); + + if ( (rv == 0) && ((err = WSAGetLastError()) != ERROR_IO_PENDING)) { + /* Argh! The IO failed */ + closesocket(accept_sock); + _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 (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + PR_ASSERT(0); + closesocket(accept_sock); + return -1; + } + + PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE); + + if (me->io_suspended) { + closesocket(accept_sock); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + return -1; + } + + if (me->md.blocked_io_status == 0) { + closesocket(accept_sock); + _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error); + return -1; + } + + if (!fast) { + _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd); + } + + /* IO is done */ + GetAcceptExSockaddrs( + me->md.acceptex_buf, + 0, + INET_ADDR_PADDED, + INET_ADDR_PADDED, + (LPSOCKADDR *)&(Laddr), + &llen, + (LPSOCKADDR *)&(Raddr), + (unsigned int *)rlen); + + if (raddr != NULL) { + memcpy((char *)raddr, (char *)&Raddr->inet, *rlen); + } + + PR_ASSERT(me->io_pending == PR_FALSE); + + return accept_sock; +} + +PRInt32 +_PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PROsfd *newSock, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout, + PRBool fast, _PR_AcceptTimeoutCallback callback, + void *callbackArg) +{ + PROsfd sock = sd->secret->md.osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + int bytes; + PRNetAddr *Laddr; + PRUint32 llen, rlen, err; + int rv; + PRBool isConnected; + PRBool madeCallback = PR_FALSE; + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + if (!sd->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)sock); + PR_ASSERT(0 != rv); + sd->secret->md.io_model_committed = PR_TRUE; + } + + *newSock = _md_get_recycled_socket(sd->secret->af); + if (*newSock == INVALID_SOCKET) { + 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); + closesocket(*newSock); + return -1; + } + me->io_pending = PR_TRUE; + me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = sock; + + rv = AcceptEx((SOCKET)sock, + *newSock, + buf, + amount, + INET_ADDR_PADDED, + INET_ADDR_PADDED, + &bytes, + &(me->md.overlapped.overlapped)); + + if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) { + closesocket(*newSock); + _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 (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + PR_ASSERT(0); + closesocket(*newSock); + return -1; + } + +retry: + if (me->io_suspended) { + PRInt32 err; + INT seconds; + INT bytes = sizeof(seconds); + + PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT); + + err = getsockopt(*newSock, + SOL_SOCKET, + SO_CONNECT_TIME, + (char *)&seconds, + (PINT)&bytes); + if ( err == NO_ERROR ) { + PRIntervalTime elapsed = PR_SecondsToInterval(seconds); + + if (seconds == 0xffffffff) { + isConnected = PR_FALSE; + } + else { + isConnected = PR_TRUE; + } + + if (!isConnected) { + if (madeCallback == PR_FALSE && callback) { + callback(callbackArg); + } + madeCallback = PR_TRUE; + me->state = _PR_IO_WAIT; + if (_NT_ResumeIO(me, timeout) == PR_FAILURE) { + closesocket(*newSock); + return -1; + } + goto retry; + } + + if (elapsed < timeout) { + /* Socket is connected but time not elapsed, RESUME IO */ + timeout -= elapsed; + me->state = _PR_IO_WAIT; + if (_NT_ResumeIO(me, timeout) == PR_FAILURE) { + closesocket(*newSock); + return -1; + } + goto retry; + } + } else { + /* What to do here? Assume socket not open?*/ + PR_ASSERT(0); + isConnected = PR_FALSE; + } + + rv = _NT_IO_ABORT(*newSock); + + PR_ASSERT(me->io_pending == PR_FALSE); + PR_ASSERT(me->io_suspended == PR_FALSE); + PR_ASSERT(me->md.thr_bound_cpu == NULL); + /* If the IO is still suspended, it means we didn't get any + * completion from NT_IO_WAIT. This is not disasterous, I hope, + * but it may mean we still have an IO outstanding... Try to + * recover by just allowing ourselves to continue. + */ + me->io_suspended = PR_FALSE; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + me->state = _PR_RUNNING; + closesocket(*newSock); + return -1; + } + + PR_ASSERT(me->io_pending == PR_FALSE); + PR_ASSERT(me->io_suspended == PR_FALSE); + PR_ASSERT(me->md.thr_bound_cpu == NULL); + + if (me->md.blocked_io_status == 0) { + _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error); + closesocket(*newSock); + return -1; + } + + if (!fast) { + _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock); + } + + /* IO is done */ + GetAcceptExSockaddrs( + buf, + amount, + INET_ADDR_PADDED, + INET_ADDR_PADDED, + (LPSOCKADDR *)&(Laddr), + &llen, + (LPSOCKADDR *)(raddr), + (unsigned int *)&rlen); + + return me->md.blocked_io_bytes; +} + +PRInt32 +_PR_MD_SENDFILE(PRFileDesc *sock, PRSendFileData *sfd, + PRInt32 flags, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 tflags; + int rv, err; + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + if (!sock->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)sock->secret->md.osfd); + PR_ASSERT(0 != rv); + sock->secret->md.io_model_committed = PR_TRUE; + } + if (!me->md.xmit_bufs) { + me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS); + if (!me->md.xmit_bufs) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + } + me->md.xmit_bufs->Head = (void *)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->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = sock->secret->md.osfd; + + rv = TransmitFile((SOCKET)sock->secret->md.osfd, + (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) ) { + _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; + } + + if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + PR_ASSERT(0); + return -1; + } + + PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE); + + if (me->io_suspended) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + return -1; + } + + if (me->md.blocked_io_status == 0) { + _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error); + return -1; + } + + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + _md_put_recycled_socket(sock->secret->md.osfd, sock->secret->af); + } + + PR_ASSERT(me->io_pending == PR_FALSE); + + return me->md.blocked_io_bytes; +} + +PRInt32 +_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + int bytes; + int rv, err; + + if (_NT_USE_NB_IO(fd)) { + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + return _nt_nonblock_recv(fd, buf, amount, flags, timeout); + } + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + if (!fd->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + + memset(&(me->md.overlapped.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->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 (_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; + } + + PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE); + + if (me->io_suspended) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + return -1; + } + + if (me->md.blocked_io_status == 0) { + if (me->md.blocked_io_error == ERROR_HANDLE_EOF) { + return 0; + } + _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error); + return -1; + } + + PR_ASSERT(me->io_pending == PR_FALSE); + + return me->md.blocked_io_bytes; +} + +PRInt32 +_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + int bytes; + int rv, err; + + if (_NT_USE_NB_IO(fd)) { + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + return _nt_nonblock_send(fd, (char *)buf, amount, timeout); + } + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + if (!fd->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + + memset(&(me->md.overlapped.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->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 (_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; + } + + PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE); + + if (me->io_suspended) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + return -1; + } + + if (me->md.blocked_io_status == 0) { + _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error); + return -1; + } + + PR_ASSERT(me->io_pending == PR_FALSE); + + return me->md.blocked_io_bytes; +} + +PRInt32 +_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv; + + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + if (_NT_USE_NB_IO(fd)) { + return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout); + } + else { + return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout); + } +} + +PRInt32 +_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv; + + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + if (_NT_USE_NB_IO(fd)) { + return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout); + } + else { + return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout); + } +} + +/* XXXMB - for now this is a sockets call only */ +PRInt32 +_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + int index; + int sent = 0; + int rv; + + if (_NT_USE_NB_IO(fd)) { + if (!fd->secret->md.io_model_committed) { + rv = _md_MakeNonblock((HANDLE)osfd); + PR_ASSERT(0 != rv); + fd->secret->md.io_model_committed = PR_TRUE; + } + return _nt_nonblock_writev(fd, iov, iov_size, timeout); + } + + for (index=0; index<iov_size; index++) { + rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, + timeout); + if (rv > 0) { + sent += rv; + } + if ( rv != iov[index].iov_len ) { + if (sent <= 0) { + return -1; + } + return -1; + } + } + + return sent; +} + +PRInt32 +_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 rv; + + rv = listen(fd->secret->md.osfd, backlog); + if (rv < 0) { + _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError()); + } + return(rv); +} + +PRInt32 +_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how) +{ + PRInt32 rv; + + rv = shutdown(fd->secret->md.osfd, how); + if (rv < 0) { + _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError()); + } + return(rv); +} + +PRStatus +_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) +{ + PRInt32 rv; + + rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len); + if (rv==0) { + return PR_SUCCESS; + } + else { + _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) +{ + PRInt32 rv; + + /* + * NT has a bug that, when invoked on a socket accepted by + * AcceptEx(), getpeername() returns an all-zero peer address. + * To work around this bug, we store the peer's address (returned + * by AcceptEx()) with the socket fd and use the cached peer + * address if the socket is an accepted socket. + */ + + if (fd->secret->md.accepted_socket) { + INT seconds; + INT bytes = sizeof(seconds); + + /* + * Determine if the socket is connected. + */ + + rv = getsockopt(fd->secret->md.osfd, + SOL_SOCKET, + SO_CONNECT_TIME, + (char *) &seconds, + (PINT) &bytes); + if (rv == NO_ERROR) { + if (seconds == 0xffffffff) { + PR_SetError(PR_NOT_CONNECTED_ERROR, 0); + return PR_FAILURE; + } + *len = PR_NETADDR_SIZE(&fd->secret->md.peer_addr); + memcpy(addr, &fd->secret->md.peer_addr, *len); + return PR_SUCCESS; + } else { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } + } else { + rv = getpeername((SOCKET)fd->secret->md.osfd, + (struct sockaddr *) addr, len); + if (rv == 0) { + return PR_SUCCESS; + } else { + _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError()); + return PR_FAILURE; + } + } +} + +PRStatus +_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) +{ + PRInt32 rv; + + rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen); + if (rv==0) { + return PR_SUCCESS; + } + else { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) +{ + PRInt32 rv; + + rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen); + if (rv==0) { + return PR_SUCCESS; + } + else { + _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +/* --- FILE IO ----------------------------------------------------------- */ + +PROsfd +_PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode) +{ + HANDLE file; + PRInt32 access = 0; + PRInt32 flags = 0; + PRInt32 flag6 = 0; + + if (osflags & PR_SYNC) { + flag6 = FILE_FLAG_WRITE_THROUGH; + } + + if (osflags & PR_RDONLY || osflags & PR_RDWR) { + access |= GENERIC_READ; + } + if (osflags & PR_WRONLY || osflags & PR_RDWR) { + access |= GENERIC_WRITE; + } + + if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) { + flags = CREATE_NEW; + } + else if (osflags & PR_CREATE_FILE) { + flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS; + } + else if (osflags & PR_TRUNCATE) { + flags = TRUNCATE_EXISTING; + } + else { + flags = OPEN_EXISTING; + } + + + flag6 |= FILE_FLAG_OVERLAPPED; + + file = CreateFile(name, + access, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + flags, + flag6, + NULL); + if (file == INVALID_HANDLE_VALUE) { + _PR_MD_MAP_OPEN_ERROR(GetLastError()); + return -1; + } + + if (osflags & PR_APPEND) { + if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) { + _PR_MD_MAP_LSEEK_ERROR(GetLastError()); + CloseHandle(file); + return -1; + } + } + + return (PROsfd)file; +} + +PROsfd +_PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode) +{ + HANDLE file; + PRInt32 access = 0; + PRInt32 flags = 0; + PRInt32 flag6 = 0; + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + if (osflags & PR_SYNC) { + flag6 = FILE_FLAG_WRITE_THROUGH; + } + + if (osflags & PR_RDONLY || osflags & PR_RDWR) { + access |= GENERIC_READ; + } + if (osflags & PR_WRONLY || osflags & PR_RDWR) { + access |= GENERIC_WRITE; + } + + if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) { + flags = CREATE_NEW; + } + else if (osflags & PR_CREATE_FILE) { + flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS; + } + else if (osflags & PR_TRUNCATE) { + flags = TRUNCATE_EXISTING; + } + else { + flags = OPEN_EXISTING; + } + + + flag6 |= FILE_FLAG_OVERLAPPED; + + if (osflags & PR_CREATE_FILE) { + if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } + } + file = CreateFile(name, + access, + FILE_SHARE_READ|FILE_SHARE_WRITE, + lpSA, + flags, + flag6, + NULL); + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + if (file == INVALID_HANDLE_VALUE) { + _PR_MD_MAP_OPEN_ERROR(GetLastError()); + return -1; + } + + if (osflags & PR_APPEND) { + if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) { + _PR_MD_MAP_LSEEK_ERROR(GetLastError()); + CloseHandle(file); + return -1; + } + } + + return (PROsfd)file; +} + +PRInt32 +_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) +{ + PROsfd f = fd->secret->md.osfd; + PRUint32 bytes; + int rv, err; + LONG hiOffset = 0; + LONG loOffset; + LARGE_INTEGER offset; /* use for a normalized add of len to offset */ + + if (!fd->secret->md.sync_file_io) { + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + + me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT); + PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR)); + + if (fd->secret->inheritable == _PR_TRI_TRUE) { + rv = ReadFile((HANDLE)f, + (LPVOID)buf, + len, + &bytes, + &me->md.overlapped.overlapped); + if (rv != 0) { + loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT); + PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR)); + return bytes; + } + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + rv = GetOverlappedResult((HANDLE)f, + &me->md.overlapped.overlapped, &bytes, TRUE); + if (rv != 0) { + loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT); + PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR)); + return bytes; + } + err = GetLastError(); + } + if (err == ERROR_HANDLE_EOF) { + return 0; + } else { + _PR_MD_MAP_READ_ERROR(err); + return -1; + } + } else { + if (!fd->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)f); + 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; + + rv = ReadFile((HANDLE)f, + (LPVOID)buf, + len, + &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); + + if (err == ERROR_HANDLE_EOF) { + return 0; + } + _PR_MD_MAP_READ_ERROR(err); + return -1; + } + + 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; + } + + PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE); + + if (me->io_suspended) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + return -1; + } + + if (me->md.blocked_io_status == 0) { + if (me->md.blocked_io_error == ERROR_HANDLE_EOF) { + return 0; + } + _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error); + return -1; + } + + /* Apply the workaround from bug 70765 (see _PR_MD_WRITE) + * to the reading code, too. */ + + offset.LowPart = me->md.overlapped.overlapped.Offset; + offset.HighPart = me->md.overlapped.overlapped.OffsetHigh; + offset.QuadPart += me->md.blocked_io_bytes; + + SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN); + + PR_ASSERT(me->io_pending == PR_FALSE); + + return me->md.blocked_io_bytes; + } + } else { + + rv = ReadFile((HANDLE)f, + (LPVOID)buf, + len, + &bytes, + NULL); + if (rv == 0) { + err = GetLastError(); + /* ERROR_HANDLE_EOF can only be returned by async io */ + PR_ASSERT(err != ERROR_HANDLE_EOF); + if (err == ERROR_BROKEN_PIPE) { + /* The write end of the pipe has been closed. */ + return 0; + } + _PR_MD_MAP_READ_ERROR(err); + return -1; + } + return bytes; + } +} + +PRInt32 +_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len) +{ + PROsfd f = fd->secret->md.osfd; + PRInt32 bytes; + int rv, err; + LONG hiOffset = 0; + LONG loOffset; + LARGE_INTEGER offset; /* use for the calculation of the new offset */ + + if (!fd->secret->md.sync_file_io) { + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return -1; + } + + memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + + me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT); + PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR)); + + if (fd->secret->inheritable == _PR_TRI_TRUE) { + rv = WriteFile((HANDLE)f, + (LPVOID)buf, + len, + &bytes, + &me->md.overlapped.overlapped); + if (rv != 0) { + loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT); + PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR)); + return bytes; + } + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + rv = GetOverlappedResult((HANDLE)f, + &me->md.overlapped.overlapped, &bytes, TRUE); + if (rv != 0) { + loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT); + PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR)); + return bytes; + } + err = GetLastError(); + } + _PR_MD_MAP_READ_ERROR(err); + return -1; + } else { + if (!fd->secret->md.io_model_committed) { + rv = _md_Associate((HANDLE)f); + 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; + + rv = WriteFile((HANDLE)f, + buf, + len, + &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 (_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; + } + + PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE); + + if (me->io_suspended) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } + return -1; + } + + if (me->md.blocked_io_status == 0) { + _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error); + return -1; + } + + /* + * Moving the file pointer by a relative offset (FILE_CURRENT) + * does not work with a file on a network drive exported by a + * Win2K system. We still don't know why. A workaround is to + * move the file pointer by an absolute offset (FILE_BEGIN). + * (Bugzilla bug 70765) + */ + offset.LowPart = me->md.overlapped.overlapped.Offset; + offset.HighPart = me->md.overlapped.overlapped.OffsetHigh; + offset.QuadPart += me->md.blocked_io_bytes; + + SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN); + + PR_ASSERT(me->io_pending == PR_FALSE); + + return me->md.blocked_io_bytes; + } + } else { + rv = WriteFile((HANDLE)f, + buf, + len, + &bytes, + NULL); + if (rv == 0) { + _PR_MD_MAP_WRITE_ERROR(GetLastError()); + return -1; + } + return bytes; + } +} + +PRInt32 +_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd) +{ + PRInt32 result; + + if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError()); + return -1; + } + return result; +} + +PRInt32 +_PR_MD_PIPEAVAILABLE(PRFileDesc *fd) +{ + if (NULL == fd) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + } + else { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + } + return -1; +} + +PROffset32 +_PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) +{ + DWORD moveMethod; + PROffset32 rv; + + switch (whence) { + case PR_SEEK_SET: + moveMethod = FILE_BEGIN; + break; + case PR_SEEK_CUR: + moveMethod = FILE_CURRENT; + break; + case PR_SEEK_END: + moveMethod = FILE_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod); + + /* + * If the lpDistanceToMoveHigh argument (third argument) is + * NULL, SetFilePointer returns 0xffffffff on failure. + */ + if (-1 == rv) { + _PR_MD_MAP_LSEEK_ERROR(GetLastError()); + } + return rv; +} + +PROffset64 +_PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) +{ + DWORD moveMethod; + LARGE_INTEGER li; + DWORD err; + + switch (whence) { + case PR_SEEK_SET: + moveMethod = FILE_BEGIN; + break; + case PR_SEEK_CUR: + moveMethod = FILE_CURRENT; + break; + case PR_SEEK_END: + moveMethod = FILE_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + li.QuadPart = offset; + li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, + li.LowPart, &li.HighPart, moveMethod); + + if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(err); + li.QuadPart = -1; + } + return li.QuadPart; +} + +/* + * This is documented to succeed on read-only files, but Win32's + * FlushFileBuffers functions fails with "access denied" in such a + * case. So we only signal an error if the error is *not* "access + * denied". + */ +PRInt32 +_PR_MD_FSYNC(PRFileDesc *fd) +{ + /* + * From the documentation: + * + * On Windows NT, the function FlushFileBuffers fails if hFile + * is a handle to console output. That is because console + * output is not buffered. The function returns FALSE, and + * GetLastError returns ERROR_INVALID_HANDLE. + * + * On the other hand, on Win95, it returns without error. I cannot + * assume that 0, 1, and 2 are console, because if someone closes + * System.out and then opens a file, they might get file descriptor + * 1. An error on *that* version of 1 should be reported, whereas + * an error on System.out (which was the original 1) should be + * ignored. So I use isatty() to ensure that such an error was + * because of this, and if it was, I ignore the error. + */ + + BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd); + + if (!ok) { + DWORD err = GetLastError(); + + if (err != ERROR_ACCESS_DENIED) { /* from winerror.h */ + _PR_MD_MAP_FSYNC_ERROR(err); + return -1; + } + } + return 0; +} + +PRInt32 +_PR_MD_CLOSE(PROsfd osfd, PRBool socket) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (socket) { + rv = closesocket((SOCKET)osfd); + if (rv < 0) { + _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError()); + } + } else { + rv = CloseHandle((HANDLE)osfd)?0:-1; + if (rv < 0) { + _PR_MD_MAP_CLOSE_ERROR(GetLastError()); + } + } + + if (rv == 0 && me->io_suspended) { + if (me->io_fd == osfd) { + PRBool fWait; + + _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. + */ + 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); + } + } + return rv; +} + +PRStatus +_PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable) +{ + BOOL rv; + + if (fd->secret->md.io_model_committed) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + rv = SetHandleInformation( + (HANDLE)fd->secret->md.osfd, + HANDLE_FLAG_INHERIT, + inheritable ? HANDLE_FLAG_INHERIT : 0); + if (0 == rv) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +void +_PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported) +{ + if (imported) { + fd->secret->inheritable = _PR_TRI_UNKNOWN; + } else { + fd->secret->inheritable = _PR_TRI_FALSE; + } +} + +void +_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd) +{ + DWORD flags; + + PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); + if (fd->secret->md.io_model_committed) { + return; + } + if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) { + if (flags & HANDLE_FLAG_INHERIT) { + fd->secret->inheritable = _PR_TRI_TRUE; + } else { + fd->secret->inheritable = _PR_TRI_FALSE; + } + } +} + + +/* --- DIR IO ------------------------------------------------------------ */ +#define GetFileFromDIR(d) (d)->d_entry.cFileName +#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) + +void FlipSlashes(char *cp, int len) +{ + while (--len >= 0) { + if (cp[0] == '/') { + cp[0] = PR_DIRECTORY_SEPARATOR; + } + cp = _mbsinc(cp); + } +} /* end FlipSlashes() */ + +/* +** +** Local implementations of standard Unix RTL functions which are not provided +** by the VC RTL. +** +*/ + +PRInt32 +_PR_MD_CLOSE_DIR(_MDDir *d) +{ + if ( d ) { + if (FindClose( d->d_hdl )) { + d->magic = (PRUint32)-1; + return 0; + } else { + _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); + return -1; + } + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; +} + + +PRStatus +_PR_MD_OPEN_DIR(_MDDir *d, const char *name) +{ + char filename[ MAX_PATH ]; + int len; + + len = strlen(name); + /* Need 5 bytes for \*.* and the trailing null byte. */ + if (len + 5 > MAX_PATH) { + PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); + return PR_FAILURE; + } + strcpy(filename, name); + + /* + * If 'name' ends in a slash or backslash, do not append + * another backslash. + */ + if (IsPrevCharSlash(filename, filename + len)) { + len--; + } + strcpy(&filename[len], "\\*.*"); + FlipSlashes( filename, strlen(filename) ); + + d->d_hdl = FindFirstFile( filename, &(d->d_entry) ); + if ( d->d_hdl == INVALID_HANDLE_VALUE ) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return PR_FAILURE; + } + d->firstEntry = PR_TRUE; + d->magic = _MD_MAGIC_DIR; + return PR_SUCCESS; +} + +char * +_PR_MD_READ_DIR(_MDDir *d, PRIntn flags) +{ + PRInt32 err; + BOOL rv; + char *fileName; + + if ( d ) { + while (1) { + if (d->firstEntry) { + d->firstEntry = PR_FALSE; + rv = 1; + } else { + rv = FindNextFile(d->d_hdl, &(d->d_entry)); + } + if (rv == 0) { + break; + } + fileName = GetFileFromDIR(d); + if ( (flags & PR_SKIP_DOT) && + (fileName[0] == '.') && (fileName[1] == '\0')) { + continue; + } + if ( (flags & PR_SKIP_DOT_DOT) && + (fileName[0] == '.') && (fileName[1] == '.') && + (fileName[2] == '\0')) { + continue; + } + if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) { + continue; + } + return fileName; + } + err = GetLastError(); + PR_ASSERT(NO_ERROR != err); + _PR_MD_MAP_READDIR_ERROR(err); + return NULL; + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; +} + +PRInt32 +_PR_MD_DELETE(const char *name) +{ + if (DeleteFile(name)) { + return 0; + } else { + _PR_MD_MAP_DELETE_ERROR(GetLastError()); + return -1; + } +} + +void +_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm) +{ + PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); + CopyMemory(prtm, filetime, sizeof(PRTime)); +#ifdef __GNUC__ + *prtm = (*prtm - _pr_filetime_offset) / 10LL; +#else + *prtm = (*prtm - _pr_filetime_offset) / 10i64; +#endif + +#ifdef DEBUG + /* Doublecheck our calculation. */ + { + SYSTEMTIME systime; + PRExplodedTime etm; + PRTime cmp; /* for comparison */ + BOOL rv; + + rv = FileTimeToSystemTime(filetime, &systime); + PR_ASSERT(0 != rv); + + /* + * PR_ImplodeTime ignores wday and yday. + */ + etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC; + etm.tm_sec = systime.wSecond; + etm.tm_min = systime.wMinute; + etm.tm_hour = systime.wHour; + etm.tm_mday = systime.wDay; + etm.tm_month = systime.wMonth - 1; + etm.tm_year = systime.wYear; + /* + * It is not well-documented what time zone the FILETIME's + * are in. WIN32_FIND_DATA is documented to be in UTC (GMT). + * But BY_HANDLE_FILE_INFORMATION is unclear about this. + * By our best judgement, we assume that FILETIME is in UTC. + */ + etm.tm_params.tp_gmt_offset = 0; + etm.tm_params.tp_dst_offset = 0; + cmp = PR_ImplodeTime(&etm); + + /* + * SYSTEMTIME is in milliseconds precision, so we convert PRTime's + * microseconds to milliseconds before doing the comparison. + */ + PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC)); + } +#endif /* DEBUG */ +} + +PRInt32 +_PR_MD_STAT(const char *fn, struct stat *info) +{ + PRInt32 rv; + + rv = _stat(fn, (struct _stat *)info); + if (-1 == rv) { + /* + * Check for MSVC runtime library _stat() bug. + * (It's really a bug in FindFirstFile().) + * If a pathname ends in a backslash or slash, + * e.g., c:\temp\ or c:/temp/, _stat() will fail. + * Note: a pathname ending in a slash (e.g., c:/temp/) + * can be handled by _stat() on NT but not on Win95. + * + * We remove the backslash or slash at the end and + * try again. + */ + + int len = strlen(fn); + if (len > 0 && len <= _MAX_PATH + && IsPrevCharSlash(fn, fn + len)) { + char newfn[_MAX_PATH + 1]; + + strcpy(newfn, fn); + newfn[len - 1] = '\0'; + rv = _stat(newfn, (struct _stat *)info); + } + } + + if (-1 == rv) { + _PR_MD_MAP_STAT_ERROR(errno); + } + return rv; +} + +#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\') + +static PRBool +IsPrevCharSlash(const char *str, const char *current) +{ + const char *prev; + + if (str >= current) { + return PR_FALSE; + } + prev = _mbsdec(str, current); + return (prev == current - 1) && _PR_IS_SLASH(*prev); +} + +/* + * IsRootDirectory -- + * + * Return PR_TRUE if the pathname 'fn' is a valid root directory, + * else return PR_FALSE. The char buffer pointed to by 'fn' must + * be writable. During the execution of this function, the contents + * of the buffer pointed to by 'fn' may be modified, but on return + * the original contents will be restored. 'buflen' is the size of + * the buffer pointed to by 'fn'. + * + * Root directories come in three formats: + * 1. / or \, meaning the root directory of the current drive. + * 2. C:/ or C:\, where C is a drive letter. + * 3. \\<server name>\<share point name>\ or + * \\<server name>\<share point name>, meaning the root directory + * of a UNC (Universal Naming Convention) name. + */ + +static PRBool +IsRootDirectory(char *fn, size_t buflen) +{ + char *p; + PRBool slashAdded = PR_FALSE; + PRBool rv = PR_FALSE; + + if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') { + return PR_TRUE; + } + + if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) + && fn[3] == '\0') { + rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; + return rv; + } + + /* The UNC root directory */ + + if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) { + /* The 'server' part should have at least one character. */ + p = &fn[2]; + if (*p == '\0' || _PR_IS_SLASH(*p)) { + return PR_FALSE; + } + + /* look for the next slash */ + do { + p = _mbsinc(p); + } while (*p != '\0' && !_PR_IS_SLASH(*p)); + if (*p == '\0') { + return PR_FALSE; + } + + /* The 'share' part should have at least one character. */ + p++; + if (*p == '\0' || _PR_IS_SLASH(*p)) { + return PR_FALSE; + } + + /* look for the final slash */ + do { + p = _mbsinc(p); + } while (*p != '\0' && !_PR_IS_SLASH(*p)); + if (_PR_IS_SLASH(*p) && p[1] != '\0') { + return PR_FALSE; + } + if (*p == '\0') { + /* + * GetDriveType() doesn't work correctly if the + * path is of the form \\server\share, so we add + * a final slash temporarily. + */ + if ((p + 1) < (fn + buflen)) { + *p++ = '\\'; + *p = '\0'; + slashAdded = PR_TRUE; + } else { + return PR_FALSE; /* name too long */ + } + } + rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; + /* restore the 'fn' buffer */ + if (slashAdded) { + *--p = '\0'; + } + } + return rv; +} + +PRInt32 +_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info) +{ + WIN32_FILE_ATTRIBUTE_DATA findFileData; + + if (NULL == fn || '\0' == *fn) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + if (!GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData)) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return -1; + } + + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = PR_FILE_DIRECTORY; + } else { + info->type = PR_FILE_FILE; + } + + info->size = findFileData.nFileSizeHigh; + info->size = (info->size << 32) + findFileData.nFileSizeLow; + + _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); + + if (0 == findFileData.ftCreationTime.dwLowDateTime && + 0 == findFileData.ftCreationTime.dwHighDateTime) { + info->creationTime = info->modifyTime; + } else { + _PR_FileTimeToPRTime(&findFileData.ftCreationTime, + &info->creationTime); + } + + return 0; +} + +PRInt32 +_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info) +{ + PRFileInfo64 info64; + PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64); + if (0 == rv) + { + info->type = info64.type; + info->size = (PRUint32) info64.size; + info->modifyTime = info64.modifyTime; + info->creationTime = info64.creationTime; + } + return rv; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info) +{ + int rv; + + BY_HANDLE_FILE_INFORMATION hinfo; + + rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo); + if (rv == FALSE) { + _PR_MD_MAP_FSTAT_ERROR(GetLastError()); + return -1; + } + + if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_FILE; + } + + info->size = hinfo.nFileSizeHigh; + info->size = (info->size << 32) + hinfo.nFileSizeLow; + + _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) ); + _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) ); + + return 0; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info) +{ + int rv; + + BY_HANDLE_FILE_INFORMATION hinfo; + + rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo); + if (rv == FALSE) { + _PR_MD_MAP_FSTAT_ERROR(GetLastError()); + return -1; + } + + if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_FILE; + } + + info->size = hinfo.nFileSizeLow; + + _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) ); + _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) ); + + return 0; +} + +PRInt32 +_PR_MD_RENAME(const char *from, const char *to) +{ + /* Does this work with dot-relative pathnames? */ + if (MoveFile(from, to)) { + return 0; + } else { + _PR_MD_MAP_RENAME_ERROR(GetLastError()); + return -1; + } +} + +PRInt32 +_PR_MD_ACCESS(const char *name, PRAccessHow how) +{ + PRInt32 rv; + + switch (how) { + case PR_ACCESS_WRITE_OK: + rv = _access(name, 02); + break; + case PR_ACCESS_READ_OK: + rv = _access(name, 04); + break; + case PR_ACCESS_EXISTS: + rv = _access(name, 00); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + if (rv < 0) { + _PR_MD_MAP_ACCESS_ERROR(errno); + } + return rv; +} + +PRInt32 +_PR_MD_MKDIR(const char *name, PRIntn mode) +{ + /* XXXMB - how to translate the "mode"??? */ + if (CreateDirectory(name, NULL)) { + return 0; + } else { + _PR_MD_MAP_MKDIR_ERROR(GetLastError()); + return -1; + } +} + +PRInt32 +_PR_MD_MAKE_DIR(const char *name, PRIntn mode) +{ + BOOL rv; + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } + rv = CreateDirectory(name, lpSA); + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + if (rv) { + return 0; + } else { + _PR_MD_MAP_MKDIR_ERROR(GetLastError()); + return -1; + } +} + +PRInt32 +_PR_MD_RMDIR(const char *name) +{ + if (RemoveDirectory(name)) { + return 0; + } else { + _PR_MD_MAP_RMDIR_ERROR(GetLastError()); + return -1; + } +} + +PRStatus +_PR_MD_LOCKFILE(PROsfd f) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return PR_FAILURE; + } + + memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + + _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, + 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; + } + + /* HACK AROUND NT BUG + * NT 3.51 has a bug. In NT 3.51, if LockFileEx returns true, you + * don't get any completion on the completion port. This is a bug. + * + * They fixed it on NT4.0 so that you do get a completion. + * + * If we pretend we won't get a completion, NSPR gets confused later + * when the unexpected completion arrives. If we assume we do get + * a completion, we hang on 3.51. Worse, Microsoft informs me that the + * behavior varies on 3.51 depending on if you are using a network + * file system or a local disk! + * + * Solution: For now, _nt_version_gets_lockfile_completion is set + * depending on whether or not this system is EITHER + * - running NT 4.0 + * - running NT 3.51 with a service pack greater than 5. + * + * In the meantime, this code may not work on network file systems. + * + */ + + if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) { + _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 + else if (rv) { + /* If this is NT 3.51 and the file is local, then we won't get a + * completion back from LockFile when it succeeded. + */ + if (_nt_version_gets_lockfile_completion == PR_FALSE) { + if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) { + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + return PR_SUCCESS; + } + } + } +#endif /* _NEED_351_FILE_LOCKING_HACK */ + + if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + + if (me->md.blocked_io_status == 0) { + _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +PRStatus +_PR_MD_TLOCKFILE(PROsfd f) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return PR_FAILURE; + } + + memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + + _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)) { + _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 + else if (rv) { + /* If this is NT 3.51 and the file is local, then we won't get a + * completion back from LockFile when it succeeded. + */ + if (_nt_version_gets_lockfile_completion == PR_FALSE) { + if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) { + _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; + } + } + } +#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; + 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; + } + + if (me->md.blocked_io_status == 0) { + _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + + +PRStatus +_PR_MD_UNLOCKFILE(PROsfd f) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me->io_suspended) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return PR_FAILURE; + } + + memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + + rv = UnlockFileEx((HANDLE)f, + 0, + 0x7fffffff, + 0, + &me->md.overlapped.overlapped); + + if (rv) { + return PR_SUCCESS; + } + else { + int err = GetLastError(); + _PR_MD_MAP_LOCKF_ERROR(err); + return PR_FAILURE; + } +} + +void +_PR_MD_MAKE_NONBLOCK(PRFileDesc *f) +{ + /* + * On NT, we either call _md_Associate() or _md_MakeNonblock(), + * depending on whether the socket is blocking or not. + * + * Once we associate a socket with the io completion port, + * there is no way to disassociate it from the io completion + * port. So we have to call _md_Associate/_md_MakeNonblock + * lazily. + */ +} + +#ifdef _NEED_351_FILE_LOCKING_HACK +/*************** +** +** Lockfile hacks +** +** The following code is a hack to work around a microsoft bug with lockfile. +** The problem is that on NT 3.51, if LockFileEx() succeeds, you never +** get a completion back for files that are on local disks. So, we need to +** know if a file is local or remote so we can tell if we should expect +** a completion. +** +** The only way to check if a file is local or remote based on the handle is +** to get the serial number for the volume it is mounted on and then to +** compare that with mounted drives. This code caches the volume numbers of +** fixed disks and does a relatively quick check. +** +** Locking: Since the only thing we ever do when multithreaded is a 32bit +** assignment, we probably don't need locking. It is included just +** case anyway. +** +** Limitations: Does not work on floppies because they are too slow +** Unknown if it will work on wierdo 3rd party file systems +** +**************** +*/ + +/* There can only be 26 drive letters on NT */ +#define _PR_MAX_DRIVES 26 + +_MDLock cachedVolumeLock; +DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0}; +DWORD dwLastCachedDrive = 0; +DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */ + +PRBool IsFileLocalInit() +{ + TCHAR lpBuffer[_PR_MAX_DRIVES*5]; + DWORD nBufferLength = _PR_MAX_DRIVES*5; + DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL); + DWORD dwIndex = 0; + DWORD dwDriveType; + DWORD dwVolumeSerialNumber; + DWORD dwDriveIndex = 0; + DWORD oldmode = (DWORD) -1; + + _MD_NEW_LOCK(&cachedVolumeLock); + + nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer); + if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength) { + return PR_FALSE; + } + + // Calling GetVolumeInformation on a removeable drive where the + // disk is currently removed will cause a dialog box to the + // console. This is not good. + // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the + // damn dialog. + + dwCachedVolumeSerialNumbers[dwDriveIndex] = 0; + oldmode = SetErrorMode(SEM_FAILCRITICALERRORS); + + // now loop through the logical drives + while(lpBuffer[dwIndex] != TEXT('\0')) + { + // skip the floppy drives. This is *SLOW* + if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B'))) + /* Skip over floppies */; + else + { + dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A')); + + dwDriveType = GetDriveType(&lpBuffer[dwIndex]); + + switch(dwDriveType) + { + // Ignore these drive types + case 0: + case 1: + case DRIVE_REMOTE: + default: // If the drive type is unknown, ignore it. + break; + + // Removable media drives can have different serial numbers + // at different times, so cache the current serial number + // but keep track of them so they can be rechecked if necessary. + case DRIVE_REMOVABLE: + + // CDROM is a removable media + case DRIVE_CDROM: + + // no idea if ramdisks can change serial numbers or not + // but it doesn't hurt to treat them as removable. + + case DRIVE_RAMDISK: + + + // Here is where we keep track of removable drives. + dwRemoveableDrivesToCheck |= 1 << dwDriveIndex; + + // removable drives fall through to fixed drives and get cached. + + case DRIVE_FIXED: + + // cache volume serial numbers. + if (GetVolumeInformation( + &lpBuffer[dwIndex], + NULL, 0, + &dwVolumeSerialNumber, + NULL, NULL, NULL, 0) + ) + { + if (dwLastCachedDrive < dwDriveIndex) { + dwLastCachedDrive = dwDriveIndex; + } + dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber; + } + + break; + } + } + + dwIndex += lstrlen(&lpBuffer[dwIndex]) +1; + } + + if (oldmode != (DWORD) -1) { + SetErrorMode(oldmode); + oldmode = (DWORD) -1; + } + + return PR_TRUE; +} + +PRInt32 IsFileLocal(HANDLE hFile) +{ + DWORD dwIndex = 0, dwMask; + BY_HANDLE_FILE_INFORMATION Info; + TCHAR szDrive[4] = TEXT("C:\\"); + DWORD dwVolumeSerialNumber; + DWORD oldmode = (DWORD) -1; + int rv = _PR_REMOTE_FILE; + + if (!GetFileInformationByHandle(hFile, &Info)) { + return -1; + } + + // look to see if the volume serial number has been cached. + _MD_LOCK(&cachedVolumeLock); + while(dwIndex <= dwLastCachedDrive) + if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber) + { + _MD_UNLOCK(&cachedVolumeLock); + return _PR_LOCAL_FILE; + } + _MD_UNLOCK(&cachedVolumeLock); + + // volume serial number not found in the cache. Check removable files. + // removable drives are noted as a bitmask. If the bit associated with + // a specific drive is set, then we should query its volume serial number + // as its possible it has changed. + dwMask = dwRemoveableDrivesToCheck; + dwIndex = 0; + + while(dwMask) + { + while(!(dwMask & 1)) + { + dwIndex++; + dwMask = dwMask >> 1; + } + + szDrive[0] = TEXT('A')+ (TCHAR) dwIndex; + + // Calling GetVolumeInformation on a removeable drive where the + // disk is currently removed will cause a dialog box to the + // console. This is not good. + // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the + // dialog. + + oldmode = SetErrorMode(SEM_FAILCRITICALERRORS); + + if (GetVolumeInformation( + szDrive, + NULL, 0, + &dwVolumeSerialNumber, + NULL, NULL, NULL, 0) + ) + { + if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber) + { + _MD_LOCK(&cachedVolumeLock); + if (dwLastCachedDrive < dwIndex) { + dwLastCachedDrive = dwIndex; + } + dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber; + _MD_UNLOCK(&cachedVolumeLock); + rv = _PR_LOCAL_FILE; + } + } + if (oldmode != (DWORD) -1) { + SetErrorMode(oldmode); + oldmode = (DWORD) -1; + } + + if (rv == _PR_LOCAL_FILE) { + return _PR_LOCAL_FILE; + } + + dwIndex++; + dwMask = dwMask >> 1; + } + + return _PR_REMOTE_FILE; +} +#endif /* _NEED_351_FILE_LOCKING_HACK */ + +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); + } + 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->md.thr_bound_cpu = NULL; + me->io_suspended = PR_FALSE; + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(me); + return PR_SUCCESS; +} + +static PROsfd _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + SOCKET sock; + PRInt32 rv, err; + fd_set rd; + struct timeval tv, *tvp; + + FD_ZERO(&rd); + FD_SET((SOCKET)osfd, &rd); + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + while ((sock = accept(osfd, addr, addrlen)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, + NULL)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + break; + } + } else { + _PR_MD_MAP_ACCEPT_ERROR(err); + break; + } + } + } else if (timeout == PR_INTERVAL_NO_WAIT) { + if ((sock = accept(osfd, addr, addrlen)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } else { + _PR_MD_MAP_ACCEPT_ERROR(err); + } + } + } else { +retry: + if ((sock = accept(osfd, addr, addrlen)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + + rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp); + if (rv > 0) { + goto retry; + } else if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + } else { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + } + } else { + _PR_MD_MAP_ACCEPT_ERROR(err); + } + } + } + return (PROsfd)sock; +} + +static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv; + int err; + fd_set wr, ex; + struct timeval tv, *tvp; + int len; + + if ((rv = connect(osfd, addr, addrlen)) == -1) { + if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) { + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET((SOCKET)osfd, &wr); + FD_SET((SOCKET)osfd, &ex); + if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wr, &ex, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + return rv; + } + if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + return -1; + } + /* Call Sleep(0) to work around a Winsock timeing bug. */ + Sleep(0); + if (FD_ISSET((SOCKET)osfd, &ex)) { + len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, + (char *) &err, &len) == SOCKET_ERROR) { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return -1; + } + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } + PR_ASSERT(FD_ISSET((SOCKET)osfd, &wr)); + rv = 0; + } else { + _PR_MD_MAP_CONNECT_ERROR(err); + } + } + return rv; +} + +static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + struct timeval tv, *tvp; + fd_set rd; + int osflags; + + if (0 == flags) { + osflags = 0; + } else { + PR_ASSERT(PR_MSG_PEEK == flags); + osflags = MSG_PEEK; + } + while ((rv = recv(osfd,buf,len,osflags)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + FD_ZERO(&rd); + FD_SET((SOCKET)osfd, &rd); + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + break; + } else if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } + } else { + _PR_MD_MAP_RECV_ERROR(err); + break; + } + } + return(rv); +} + +static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + struct timeval tv, *tvp; + fd_set wd; + PRInt32 bytesSent = 0; + + while(bytesSent < len) { + while ((rv = send(osfd,buf,len,0)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&wd); + FD_SET((SOCKET)osfd, &wd); + if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + return -1; + } + if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + return -1; + } + } else { + _PR_MD_MAP_SEND_ERROR(err); + return -1; + } + } + bytesSent += rv; + if (fd->secret->nonblocking) { + break; + } + if (bytesSent < len) { + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&wd); + FD_SET((SOCKET)osfd, &wd); + if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + return -1; + } + if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + return -1; + } + } + } + return bytesSent; +} + +static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime timeout) +{ + int index; + int sent = 0; + int rv; + + for (index=0; index<size; index++) { + rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout); + if (rv > 0) { + sent += rv; + } + if ( rv != iov[index].iov_len ) { + if (rv < 0) { + if (fd->secret->nonblocking + && (PR_GetError() == PR_WOULD_BLOCK_ERROR) + && (sent > 0)) { + return sent; + } else { + return -1; + } + } + /* Only a nonblocking socket can have partial sends */ + PR_ASSERT(fd->secret->nonblocking); + return sent; + } + } + + return sent; +} + +static PRInt32 _nt_nonblock_sendto( + PRFileDesc *fd, const char *buf, int len, + const struct sockaddr *addr, int addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + struct timeval tv, *tvp; + fd_set wd; + PRInt32 bytesSent = 0; + + while(bytesSent < len) { + while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&wd); + FD_SET((SOCKET)osfd, &wd); + if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + return -1; + } + if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + return -1; + } + } else { + _PR_MD_MAP_SENDTO_ERROR(err); + return -1; + } + } + bytesSent += rv; + if (fd->secret->nonblocking) { + break; + } + if (bytesSent < len) { + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&wd); + FD_SET((SOCKET)osfd, &wd); + if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + return -1; + } + if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + return -1; + } + } + } + return bytesSent; +} + +static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + struct timeval tv, *tvp; + fd_set rd; + + while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + tvp = NULL; + } else { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } + FD_ZERO(&rd); + FD_SET((SOCKET)osfd, &rd); + if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, + tvp)) == -1) { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + break; + } else if (rv == 0) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } + } else { + _PR_MD_MAP_RECVFROM_ERROR(err); + break; + } + } + return(rv); +} + +/* + * UDP support: the continuation thread functions and recvfrom and sendto. + */ + +static void pt_InsertTimedInternal(pt_Continuation *op) +{ + PRInt32 delta = 0; + pt_Continuation *t_op = NULL; + PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo; + + /* + * If this element operation isn't timed, it gets queued at the + * end of the list (just after pt_tq.tail) and we're + * finishd early. + */ + if (PR_INTERVAL_NO_TIMEOUT == op->timeout) + { + t_op = pt_tq.tail; /* put it at the end */ + goto done; + } + + /* + * The rest of this routine actaully deals with timed ops. + */ + + if (NULL != pt_tq.op) + { + /* + * To find where in the list to put the new operation, form + * the absolute time the operations in question will expire. + * + * The new operation ('op') will expire at now() + op->timeout. + * + * The operation that will time out furthest in the future will + * do so at pt_tq.epoch + pt_tq.op->timeout. + * + * Subsequently earlier timeouts are computed based on the latter + * knowledge by subracting the timeout deltas that are stored in + * the operation list. There are operation[n]->timeout ticks + * between the expiration of operation[n-1] and operation[n].e e + * + * Therefore, the operation[n-1] will expire operation[n]->timeout + * ticks prior to operation[n]. + * + * This should be easy! + */ + t_op = pt_tq.op; /* running pointer to queued op */ + op_tmo = now + op->timeout; /* that's in absolute ticks */ + qd_tmo = pt_tq.epoch + t_op->timeout; /* likewise */ + + do + { + /* + * If 'op' expires later than t_op, then insert 'op' just + * ahead of t_op. Otherwise, compute when operation[n-1] + * expires and try again. + * + * The actual different between the expiriation of 'op' + * and the current operation what becomes the new operaton's + * timeout interval. That interval is also subtracted from + * the interval of the operation immediately following where + * we stick 'op' (unless the next one isn't timed). The new + * timeout assigned to 'op' takes into account the values of + * now() and when the previous intervals were compured. + */ + delta = op_tmo - qd_tmo; + if (delta >= 0) + { + op->timeout += (now - pt_tq.epoch); + goto done; + } + + qd_tmo -= t_op->timeout; /* previous operaton expiration */ + t_op = t_op->prev; /* point to previous operation */ + if (NULL != t_op) { + qd_tmo += t_op->timeout; + } + } while (NULL != t_op); + + /* + * If we got here we backed off the head of the list. That means that + * this timed entry has to go at the head of the list. This is just + * about like having an empty timer list. + */ + delta = op->timeout; /* $$$ is this right? */ + } + +done: + + /* + * Insert 'op' into the queue just after t_op or if t_op is null, + * at the head of the list. + * + * If t_op is NULL, the list is currently empty and this is pretty + * easy. + */ + if (NULL == t_op) + { + op->prev = NULL; + op->next = pt_tq.head; + pt_tq.head = op; + if (NULL == pt_tq.tail) { + pt_tq.tail = op; + } + else { + op->next->prev = op; + } + } + else + { + op->prev = t_op; + op->next = t_op->next; + if (NULL != op->prev) { + op->prev->next = op; + } + if (NULL != op->next) { + op->next->prev = op; + } + if (t_op == pt_tq.tail) { + pt_tq.tail = op; + } + } + + /* + * Are we adjusting our epoch, etc? Are we replacing + * what was previously the element due to expire furthest + * out in the future? Is this even a timed operation? + */ + if (PR_INTERVAL_NO_TIMEOUT != op->timeout) + { + if ((NULL == pt_tq.op) /* we're the one and only */ + || (t_op == pt_tq.op)) /* we're replacing */ + { + pt_tq.op = op; + pt_tq.epoch = now; + } + } + + pt_tq.op_count += 1; + +} /* pt_InsertTimedInternal */ + +/* + * function: pt_FinishTimed + * + * Takes the finished operation out of the timed queue. It + * notifies the initiating thread that the opertions is + * complete and returns to the caller the value of the next + * operation in the list (or NULL). + */ +static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op) +{ + pt_Continuation *next; + + /* remove this one from the list */ + if (NULL == op->prev) { + pt_tq.head = op->next; + } + else { + op->prev->next = op->next; + } + if (NULL == op->next) { + pt_tq.tail = op->prev; + } + else { + op->next->prev = op->prev; + } + + /* did we happen to hit the timed op? */ + if (op == pt_tq.op) { + pt_tq.op = op->prev; + } + + next = op->next; + op->next = op->prev = NULL; + op->status = pt_continuation_done; + + pt_tq.op_count -= 1; +#if defined(DEBUG) + pt_debug.continuationsServed += 1; +#endif + PR_NotifyCondVar(op->complete); + + return next; +} /* pt_FinishTimedInternal */ + +static void ContinuationThread(void *arg) +{ + /* initialization */ + fd_set readSet, writeSet, exceptSet; + struct timeval tv; + SOCKET *pollingList = 0; /* list built for polling */ + PRIntn pollingListUsed; /* # entries used in the list */ + PRIntn pollingListNeeded; /* # entries needed this time */ + PRIntn pollingSlotsAllocated = 0; /* # entries available in list */ + PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC); + + /* do some real work */ + while (1) + { + PRIntn rv; + PRStatus status; + PRIntn pollIndex; + pt_Continuation *op; + PRIntervalTime now = PR_IntervalNow(); + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; + + PR_Lock(pt_tq.ml); + while (NULL == pt_tq.head) + { + status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT); + if ((PR_FAILURE == status) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + break; + } + } + pollingListNeeded = pt_tq.op_count; + PR_Unlock(pt_tq.ml); + + /* Okay. We're history */ + if ((PR_FAILURE == status) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + break; + } + + /* + * We are not holding the pt_tq.ml lock now, so more items may + * get added to pt_tq during this window of time. We hope + * that 10 more spaces in the polling list should be enough. + */ + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&exceptSet); + pollingListNeeded += 10; + if (pollingListNeeded > pollingSlotsAllocated) + { + if (NULL != pollingList) { + PR_DELETE(pollingList); + } + pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc)); + PR_ASSERT(NULL != pollingList); + pollingSlotsAllocated = pollingListNeeded; + } + +#if defined(DEBUG) + if (pollingListNeeded > pt_debug.pollingListMax) { + pt_debug.pollingListMax = pollingListUsed; + } +#endif + + /* + * Build up a polling list. + * This list is sorted on time. Operations that have been + * interrupted are completed and not included in the list. + * There is an assertion that the operation is in progress. + */ + pollingListUsed = 0; + PR_Lock(pt_tq.ml); + + for (op = pt_tq.head; NULL != op;) + { + if (pt_continuation_abort == op->status) + { + op->result.code = -1; + op->syserrno = WSAEINTR; + op = pt_FinishTimedInternal(op); + } + else + { + PR_ASSERT(pt_continuation_done != op->status); + op->status = pt_continuation_inprogress; + if (op->event & PR_POLL_READ) { + FD_SET(op->arg1.osfd, &readSet); + } + if (op->event & PR_POLL_WRITE) { + FD_SET(op->arg1.osfd, &writeSet); + } + if (op->event & PR_POLL_EXCEPT) { + FD_SET(op->arg1.osfd, &exceptSet); + } + pollingList[pollingListUsed] = op->arg1.osfd; + pollingListUsed += 1; + if (pollingListUsed == pollingSlotsAllocated) { + break; + } + op = op->next; + } + } + + PR_Unlock(pt_tq.ml); + + /* + * If 'op' isn't NULL at this point, then we didn't get to + * the end of the list. That means that more items got added + * to the list than we anticipated. So, forget this iteration, + * go around the horn again. + * One would hope this doesn't happen all that often. + */ + if (NULL != op) + { +#if defined(DEBUG) + pt_debug.predictionsFoiled += 1; /* keep track */ +#endif + continue; /* make it rethink things */ + } + + /* there's a chance that all ops got blown away */ + if (NULL == pt_tq.head) { + continue; + } + /* if not, we know this is the shortest timeout */ + timeout = pt_tq.head->timeout; + + /* + * We don't want to wait forever on this poll. So keep + * the interval down. The operations, if they are timed, + * still have to timeout, while those that are not timed + * should persist forever. But they may be aborted. That's + * what this anxiety is all about. + */ + if (timeout > mx_select_ticks) { + timeout = mx_select_ticks; + } + + if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout) { + pt_tq.head->timeout -= timeout; + } + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC; + + rv = select(0, &readSet, &writeSet, &exceptSet, &tv); + + if (0 == rv) /* poll timed out - what about leading op? */ + { + if (0 == pt_tq.head->timeout) + { + /* + * The leading element of the timed queue has timed + * out. Get rid of it. In any case go around the + * loop again, computing the polling list, checking + * for interrupted operations. + */ + PR_Lock(pt_tq.ml); + do + { + pt_tq.head->result.code = -1; + pt_tq.head->syserrno = WSAETIMEDOUT; + op = pt_FinishTimedInternal(pt_tq.head); + } while ((NULL != op) && (0 == op->timeout)); + PR_Unlock(pt_tq.ml); + } + continue; + } + + if (-1 == rv && (WSAGetLastError() == WSAEINTR + || WSAGetLastError() == WSAEINPROGRESS)) + { + continue; /* go around the loop again */ + } + + /* + * select() says that something in our list is ready for some more + * action or is an invalid fd. Find it, load up the operation and + * see what happens. + */ + + PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK); + + + /* + * $$$ There's a problem here. I'm running the operations list + * and I'm not holding any locks. I don't want to hold the lock + * and do the operation, so this is really messed up.. + * + * This may work out okay. The rule is that only this thread, + * the continuation thread, can remove elements from the list. + * Therefore, the list is at worst, longer than when we built + * the polling list. + */ + op = pt_tq.head; + for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex) + { + PRInt16 revents = 0; + + PR_ASSERT(NULL != op); + + /* + * This one wants attention. Redo the operation. + * We know that there can only be more elements + * in the op list than we knew about when we created + * the poll list. Therefore, we might have to skip + * a few ops to find the right one to operation on. + */ + while (pollingList[pollIndex] != op->arg1.osfd ) + { + op = op->next; + PR_ASSERT(NULL != op); + } + + if (FD_ISSET(op->arg1.osfd, &readSet)) { + revents |= PR_POLL_READ; + } + if (FD_ISSET(op->arg1.osfd, &writeSet)) { + revents |= PR_POLL_WRITE; + } + if (FD_ISSET(op->arg1.osfd, &exceptSet)) { + revents |= PR_POLL_EXCEPT; + } + + /* + * Sip over all those not in progress. They'll be + * pruned next time we build a polling list. Call + * the continuation function. If it reports completion, + * finish off the operation. + */ + if (revents && (pt_continuation_inprogress == op->status) + && (op->function(op, revents))) + { + PR_Lock(pt_tq.ml); + op = pt_FinishTimedInternal(op); + PR_Unlock(pt_tq.ml); + } + } + } + if (NULL != pollingList) { + PR_DELETE(pollingList); + } +} /* ContinuationThread */ + +static int pt_Continue(pt_Continuation *op) +{ + PRStatus rv; + /* Finish filling in the blank slots */ + op->status = pt_continuation_sumbitted; + op->complete = PR_NewCondVar(pt_tq.ml); + + PR_Lock(pt_tq.ml); /* we provide the locking */ + + pt_InsertTimedInternal(op); /* insert in the structure */ + + PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */ + + while (pt_continuation_done != op->status) /* wait for completion */ + { + rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT); + /* + * If we get interrupted, we set state the continuation thread will + * see and allow it to finish the I/O operation w/ error. That way + * the rule that only the continuation thread is removing elements + * from the list is still valid. + * + * Don't call interrupt on the continuation thread. That'll just + * piss him off. He's cycling around at least every mx_select_ticks + * anyhow and should notice the request in there. + */ + if ((PR_FAILURE == rv) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + op->status = pt_continuation_abort; /* our status */ + } + } + + PR_Unlock(pt_tq.ml); /* we provide the locking */ + + PR_DestroyCondVar(op->complete); + + return op->result.code; /* and the primary answer */ +} /* pt_Continue */ + +static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes = sendto( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags, + (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr))); + op->syserrno = WSAGetLastError(); + if (bytes > 0) /* this is progress */ + { + char *bp = op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_sendto_cont */ + +static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn addr_len = sizeof(*(op->arg5.addr)); + op->result.code = recvfrom( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, + op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len); + op->syserrno = WSAGetLastError(); + return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recvfrom_cont */ + +static PRInt32 pt_SendTo( + SOCKET osfd, const void *buf, + PRInt32 amount, PRInt32 flags, const PRNetAddr *addr, + PRIntn addrlen, PRIntervalTime timeout) +{ + PRInt32 bytes = -1, err; + PRBool fNeedContinue = PR_FALSE; + + bytes = sendto( + osfd, buf, amount, flags, + (struct sockaddr*)addr, PR_NETADDR_SIZE(addr)); + if (bytes == -1) { + if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) { + fNeedContinue = PR_TRUE; + } + else { + _PR_MD_MAP_SENDTO_ERROR(err); + } + } + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addr; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = PR_POLL_WRITE | PR_POLL_EXCEPT; + bytes = pt_Continue(&op); + if (bytes < 0) { + WSASetLastError(op.syserrno); + _PR_MD_MAP_SENDTO_ERROR(op.syserrno); + } + } + return bytes; +} /* pt_SendTo */ + +static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount, + PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout) +{ + PRInt32 bytes = -1, err; + PRBool fNeedContinue = PR_FALSE; + + bytes = recvfrom( + osfd, buf, amount, flags, + (struct sockaddr*)addr, addr_len); + if (bytes == -1) { + if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) { + fNeedContinue = PR_TRUE; + } + else { + _PR_MD_MAP_RECVFROM_ERROR(err); + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = addr; + op.timeout = timeout; + op.function = pt_recvfrom_cont; + op.event = PR_POLL_READ | PR_POLL_EXCEPT; + bytes = pt_Continue(&op); + if (bytes < 0) { + WSASetLastError(op.syserrno); + _PR_MD_MAP_RECVFROM_ERROR(op.syserrno); + } + } + return bytes; +} /* pt_RecvFrom */ diff --git a/nsprpub/pr/src/md/windows/ntmisc.c b/nsprpub/pr/src/md/windows/ntmisc.c new file mode 100644 index 0000000000..839e3de8d8 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntmisc.c @@ -0,0 +1,1230 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * ntmisc.c + * + */ + +#include "primpl.h" +#include <math.h> /* for fabs() */ +#include <windows.h> + +char *_PR_MD_GET_ENV(const char *name) +{ + return getenv(name); +} + +/* +** _PR_MD_PUT_ENV() -- add or change environment variable +** +** +*/ +PRIntn _PR_MD_PUT_ENV(const char *name) +{ + return(putenv(name)); +} + + +/* + ************************************************************************** + ************************************************************************** + ** + ** Date and time routines + ** + ************************************************************************** + ************************************************************************** + */ + +/* + * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME. + * We store the value in a PRTime variable for convenience. + */ +#ifdef __GNUC__ +const PRTime _pr_filetime_offset = 116444736000000000LL; +const PRTime _pr_filetime_divisor = 10LL; +#else +const PRTime _pr_filetime_offset = 116444736000000000i64; +const PRTime _pr_filetime_divisor = 10i64; +#endif + +#ifdef WINCE + +#define FILETIME_TO_INT64(ft) \ + (((PRInt64)ft.dwHighDateTime) << 32 | (PRInt64)ft.dwLowDateTime) + +static void +LowResTime(LPFILETIME lpft) +{ + GetCurrentFT(lpft); +} + +typedef struct CalibrationData { + long double freq; /* The performance counter frequency */ + long double offset; /* The low res 'epoch' */ + long double timer_offset; /* The high res 'epoch' */ + + /* The last high res time that we returned since recalibrating */ + PRInt64 last; + + PRBool calibrated; + + CRITICAL_SECTION data_lock; + CRITICAL_SECTION calibration_lock; + PRInt64 granularity; +} CalibrationData; + +static CalibrationData calibration; + +typedef void (*GetSystemTimeAsFileTimeFcn)(LPFILETIME); +static GetSystemTimeAsFileTimeFcn ce6_GetSystemTimeAsFileTime = NULL; + +static void +NowCalibrate(void) +{ + FILETIME ft, ftStart; + LARGE_INTEGER liFreq, now; + + if (calibration.freq == 0.0) { + if(!QueryPerformanceFrequency(&liFreq)) { + /* High-performance timer is unavailable */ + calibration.freq = -1.0; + } else { + calibration.freq = (long double) liFreq.QuadPart; + } + } + if (calibration.freq > 0.0) { + PRInt64 calibrationDelta = 0; + /* + * By wrapping a timeBegin/EndPeriod pair of calls around this loop, + * the loop seems to take much less time (1 ms vs 15ms) on Vista. + */ + timeBeginPeriod(1); + LowResTime(&ftStart); + do { + LowResTime(&ft); + } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0); + timeEndPeriod(1); + + calibration.granularity = + (FILETIME_TO_INT64(ft) - FILETIME_TO_INT64(ftStart))/10; + + QueryPerformanceCounter(&now); + + calibration.offset = (long double) FILETIME_TO_INT64(ft); + calibration.timer_offset = (long double) now.QuadPart; + /* + * The windows epoch is around 1600. The unix epoch is around 1970. + * _pr_filetime_offset is the difference (in windows time units which + * are 10 times more highres than the JS time unit) + */ + calibration.offset -= _pr_filetime_offset; + calibration.offset *= 0.1; + calibration.last = 0; + + calibration.calibrated = PR_TRUE; + } +} + +#define CALIBRATIONLOCK_SPINCOUNT 0 +#define DATALOCK_SPINCOUNT 4096 +#define LASTLOCK_SPINCOUNT 4096 + +void +_MD_InitTime(void) +{ + /* try for CE6 GetSystemTimeAsFileTime first */ + HANDLE h = GetModuleHandleW(L"coredll.dll"); + ce6_GetSystemTimeAsFileTime = (GetSystemTimeAsFileTimeFcn) + GetProcAddressA(h, "GetSystemTimeAsFileTime"); + + /* otherwise go the slow route */ + if (ce6_GetSystemTimeAsFileTime == NULL) { + memset(&calibration, 0, sizeof(calibration)); + NowCalibrate(); + InitializeCriticalSection(&calibration.calibration_lock); + InitializeCriticalSection(&calibration.data_lock); + } +} + +void +_MD_CleanupTime(void) +{ + if (ce6_GetSystemTimeAsFileTime == NULL) { + DeleteCriticalSection(&calibration.calibration_lock); + DeleteCriticalSection(&calibration.data_lock); + } +} + +#define MUTEX_SETSPINCOUNT(m, c) + +/* + *----------------------------------------------------------------------- + * + * PR_Now -- + * + * Returns the current time in microseconds since the epoch. + * The epoch is midnight January 1, 1970 GMT. + * The implementation is machine dependent. This is the + * implementation for Windows. + * Cf. time_t time(time_t *tp) + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(PRTime) +PR_Now(void) +{ + long double lowresTime, highresTimerValue; + FILETIME ft; + LARGE_INTEGER now; + PRBool calibrated = PR_FALSE; + PRBool needsCalibration = PR_FALSE; + PRInt64 returnedTime; + long double cachedOffset = 0.0; + + if (ce6_GetSystemTimeAsFileTime) { + union { + FILETIME ft; + PRTime prt; + } currentTime; + + PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); + + ce6_GetSystemTimeAsFileTime(¤tTime.ft); + + /* written this way on purpose, since the second term becomes + * a constant, and the entire expression is faster to execute. + */ + return currentTime.prt/_pr_filetime_divisor - + _pr_filetime_offset/_pr_filetime_divisor; + } + + do { + if (!calibration.calibrated || needsCalibration) { + EnterCriticalSection(&calibration.calibration_lock); + EnterCriticalSection(&calibration.data_lock); + + /* Recalibrate only if no one else did before us */ + if (calibration.offset == cachedOffset) { + /* + * Since calibration can take a while, make any other + * threads immediately wait + */ + MUTEX_SETSPINCOUNT(&calibration.data_lock, 0); + + NowCalibrate(); + + calibrated = PR_TRUE; + + /* Restore spin count */ + MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT); + } + LeaveCriticalSection(&calibration.data_lock); + LeaveCriticalSection(&calibration.calibration_lock); + } + + /* Calculate a low resolution time */ + LowResTime(&ft); + lowresTime = + ((long double)(FILETIME_TO_INT64(ft) - _pr_filetime_offset)) * 0.1; + + if (calibration.freq > 0.0) { + long double highresTime, diff; + DWORD timeAdjustment, timeIncrement; + BOOL timeAdjustmentDisabled; + + /* Default to 15.625 ms if the syscall fails */ + long double skewThreshold = 15625.25; + + /* Grab high resolution time */ + QueryPerformanceCounter(&now); + highresTimerValue = (long double)now.QuadPart; + + EnterCriticalSection(&calibration.data_lock); + highresTime = calibration.offset + 1000000L * + (highresTimerValue-calibration.timer_offset)/calibration.freq; + cachedOffset = calibration.offset; + + /* + * On some dual processor/core systems, we might get an earlier + * time so we cache the last time that we returned. + */ + calibration.last = PR_MAX(calibration.last,(PRInt64)highresTime); + returnedTime = calibration.last; + LeaveCriticalSection(&calibration.data_lock); + + /* Get an estimate of clock ticks per second from our own test */ + skewThreshold = calibration.granularity; + /* Check for clock skew */ + diff = lowresTime - highresTime; + + /* + * For some reason that I have not determined, the skew can be + * up to twice a kernel tick. This does not seem to happen by + * itself, but I have only seen it triggered by another program + * doing some kind of file I/O. The symptoms are a negative diff + * followed by an equally large positive diff. + */ + if (fabs(diff) > 2*skewThreshold) { + if (calibrated) { + /* + * If we already calibrated once this instance, and the + * clock is still skewed, then either the processor(s) are + * wildly changing clockspeed or the system is so busy that + * we get switched out for long periods of time. In either + * case, it would be infeasible to make use of high + * resolution results for anything, so let's resort to old + * behavior for this call. It's possible that in the + * future, the user will want the high resolution timer, so + * we don't disable it entirely. + */ + returnedTime = (PRInt64)lowresTime; + needsCalibration = PR_FALSE; + } else { + /* + * It is possible that when we recalibrate, we will return + * a value less than what we have returned before; this is + * unavoidable. We cannot tell the different between a + * faulty QueryPerformanceCounter implementation and user + * changes to the operating system time. Since we must + * respect user changes to the operating system time, we + * cannot maintain the invariant that Date.now() never + * decreases; the old implementation has this behavior as + * well. + */ + needsCalibration = PR_TRUE; + } + } else { + /* No detectable clock skew */ + returnedTime = (PRInt64)highresTime; + needsCalibration = PR_FALSE; + } + } else { + /* No high resolution timer is available, so fall back */ + returnedTime = (PRInt64)lowresTime; + } + } while (needsCalibration); + + return returnedTime; +} + +#else + +PR_IMPLEMENT(PRTime) +PR_Now(void) +{ + PRTime prt; + FILETIME ft; + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + _PR_FileTimeToPRTime(&ft, &prt); + return prt; +} + +#endif + +/* + *********************************************************************** + *********************************************************************** + * + * Process creation routines + * + *********************************************************************** + *********************************************************************** + */ + +/* + * Assemble the command line by concatenating the argv array. + * On success, this function returns 0 and the resulting command + * line is returned in *cmdLine. On failure, it returns -1. + */ +static int assembleCmdLine(char *const *argv, char **cmdLine) +{ + char *const *arg; + char *p, *q; + size_t cmdLineSize; + int numBackslashes; + int i; + int argNeedQuotes; + + /* + * Find out how large the command line buffer should be. + */ + cmdLineSize = 0; + for (arg = argv; *arg; arg++) { + /* + * \ and " need to be escaped by a \. In the worst case, + * every character is a \ or ", so the string of length + * may double. If we quote an argument, that needs two ". + * Finally, we need a space between arguments, and + * a null byte at the end of command line. + */ + cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */ + + 2 /* we quote every argument */ + + 1; /* space in between, or final null */ + } + p = *cmdLine = PR_MALLOC((PRUint32) cmdLineSize); + if (p == NULL) { + return -1; + } + + for (arg = argv; *arg; arg++) { + /* Add a space to separates the arguments */ + if (arg != argv) { + *p++ = ' '; + } + q = *arg; + numBackslashes = 0; + argNeedQuotes = 0; + + /* + * If the argument is empty or contains white space, it needs to + * be quoted. + */ + if (**arg == '\0' || strpbrk(*arg, " \f\n\r\t\v")) { + argNeedQuotes = 1; + } + + if (argNeedQuotes) { + *p++ = '"'; + } + while (*q) { + if (*q == '\\') { + numBackslashes++; + q++; + } else if (*q == '"') { + if (numBackslashes) { + /* + * Double the backslashes since they are followed + * by a quote + */ + for (i = 0; i < 2 * numBackslashes; i++) { + *p++ = '\\'; + } + numBackslashes = 0; + } + /* To escape the quote */ + *p++ = '\\'; + *p++ = *q++; + } else { + if (numBackslashes) { + /* + * Backslashes are not followed by a quote, so + * don't need to double the backslashes. + */ + for (i = 0; i < numBackslashes; i++) { + *p++ = '\\'; + } + numBackslashes = 0; + } + *p++ = *q++; + } + } + + /* Now we are at the end of this argument */ + if (numBackslashes) { + /* + * Double the backslashes if we have a quote string + * delimiter at the end. + */ + if (argNeedQuotes) { + numBackslashes *= 2; + } + for (i = 0; i < numBackslashes; i++) { + *p++ = '\\'; + } + } + if (argNeedQuotes) { + *p++ = '"'; + } + } + + *p = '\0'; + return 0; +} + +/* + * Assemble the environment block by concatenating the envp array + * (preserving the terminating null byte in each array element) + * and adding a null byte at the end. + * + * Returns 0 on success. The resulting environment block is returned + * in *envBlock. Note that if envp is NULL, a NULL pointer is returned + * in *envBlock. Returns -1 on failure. + */ +static int assembleEnvBlock(char **envp, char **envBlock) +{ + char *p; + char *q; + char **env; + char *curEnv; + char *cwdStart, *cwdEnd; + size_t envBlockSize; + + if (envp == NULL) { + *envBlock = NULL; + return 0; + } + +#ifdef WINCE + { + PRUnichar *wideCurEnv = mozce_GetEnvString(); + int len = WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, + NULL, 0, NULL, NULL); + curEnv = (char *) PR_MALLOC(len * sizeof(char)); + WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, + curEnv, len, NULL, NULL); + free(wideCurEnv); + } +#else + curEnv = GetEnvironmentStrings(); +#endif + + cwdStart = curEnv; + while (*cwdStart) { + if (cwdStart[0] == '=' && cwdStart[1] != '\0' + && cwdStart[2] == ':' && cwdStart[3] == '=') { + break; + } + cwdStart += strlen(cwdStart) + 1; + } + cwdEnd = cwdStart; + if (*cwdEnd) { + cwdEnd += strlen(cwdEnd) + 1; + while (*cwdEnd) { + if (cwdEnd[0] != '=' || cwdEnd[1] == '\0' + || cwdEnd[2] != ':' || cwdEnd[3] != '=') { + break; + } + cwdEnd += strlen(cwdEnd) + 1; + } + } + envBlockSize = cwdEnd - cwdStart; + + for (env = envp; *env; env++) { + envBlockSize += strlen(*env) + 1; + } + envBlockSize++; + + p = *envBlock = PR_MALLOC((PRUint32) envBlockSize); + if (p == NULL) { +#ifdef WINCE + PR_Free(curEnv); +#else + FreeEnvironmentStrings(curEnv); +#endif + return -1; + } + + q = cwdStart; + while (q < cwdEnd) { + *p++ = *q++; + } +#ifdef WINCE + PR_Free(curEnv); +#else + FreeEnvironmentStrings(curEnv); +#endif + + for (env = envp; *env; env++) { + q = *env; + while (*q) { + *p++ = *q++; + } + *p++ = '\0'; + } + *p = '\0'; + return 0; +} + +/* + * For qsort. We sort (case-insensitive) the environment strings + * before generating the environment block. + */ +static int compare(const void *arg1, const void *arg2) +{ + return _stricmp(* (char**)arg1, * (char**)arg2); +} + +PRProcess * _PR_CreateWindowsProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ +#ifdef WINCE + STARTUPINFOW startupInfo; + PRUnichar *wideCmdLine; + PRUnichar *wideCwd; + int len = 0; +#else + STARTUPINFO startupInfo; +#endif + DWORD creationFlags = 0; + PROCESS_INFORMATION procInfo; + BOOL retVal; + char *cmdLine = NULL; + char *envBlock = NULL; + char **newEnvp = NULL; + const char *cwd = NULL; /* current working directory */ + PRProcess *proc = NULL; + PRBool hasFdInheritBuffer; + + proc = PR_NEW(PRProcess); + if (!proc) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto errorExit; + } + + if (assembleCmdLine(argv, &cmdLine) == -1) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto errorExit; + } + +#ifndef WINCE + /* + * If attr->fdInheritBuffer is not NULL, we need to insert + * it into the envp array, so envp cannot be NULL. + */ + hasFdInheritBuffer = (attr && attr->fdInheritBuffer); + if ((envp == NULL) && hasFdInheritBuffer) { + envp = environ; + } + + if (envp != NULL) { + int idx; + int numEnv; + PRBool found = PR_FALSE; + + numEnv = 0; + while (envp[numEnv]) { + numEnv++; + } + newEnvp = (char **) PR_MALLOC((numEnv + 2) * sizeof(char *)); + for (idx = 0; idx < numEnv; idx++) { + newEnvp[idx] = envp[idx]; + if (hasFdInheritBuffer && !found + && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { + newEnvp[idx] = attr->fdInheritBuffer; + found = PR_TRUE; + } + } + if (hasFdInheritBuffer && !found) { + newEnvp[idx++] = attr->fdInheritBuffer; + } + newEnvp[idx] = NULL; + qsort((void *) newEnvp, (size_t) idx, sizeof(char *), compare); + } + if (assembleEnvBlock(newEnvp, &envBlock) == -1) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto errorExit; + } + + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + if (attr) { + PRBool redirected = PR_FALSE; + + /* + * XXX the default value for stdin, stdout, and stderr + * should probably be the console input and output, not + * those of the parent process. + */ + startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + if (attr->stdinFd) { + startupInfo.hStdInput = (HANDLE) attr->stdinFd->secret->md.osfd; + redirected = PR_TRUE; + } + if (attr->stdoutFd) { + startupInfo.hStdOutput = (HANDLE) attr->stdoutFd->secret->md.osfd; + redirected = PR_TRUE; + /* + * If stdout is redirected, we can assume that the process will + * not write anything useful to the console windows, and therefore + * automatically set the CREATE_NO_WINDOW flag. + */ + creationFlags |= CREATE_NO_WINDOW; + } + if (attr->stderrFd) { + startupInfo.hStdError = (HANDLE) attr->stderrFd->secret->md.osfd; + redirected = PR_TRUE; + } + if (redirected) { + startupInfo.dwFlags |= STARTF_USESTDHANDLES; + } + cwd = attr->currentDirectory; + } +#endif + +#ifdef WINCE + len = MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, NULL, 0); + wideCmdLine = (PRUnichar *)PR_MALLOC(len * sizeof(PRUnichar)); + MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, wideCmdLine, len); + len = MultiByteToWideChar(CP_ACP, 0, cwd, -1, NULL, 0); + wideCwd = PR_MALLOC(len * sizeof(PRUnichar)); + MultiByteToWideChar(CP_ACP, 0, cwd, -1, wideCwd, len); + retVal = CreateProcessW(NULL, + wideCmdLine, + NULL, /* security attributes for the new + * process */ + NULL, /* security attributes for the primary + * thread in the new process */ + TRUE, /* inherit handles */ + creationFlags, + envBlock, /* an environment block, consisting + * of a null-terminated block of + * null-terminated strings. Each + * string is in the form: + * name=value + * XXX: usually NULL */ + wideCwd, /* current drive and directory */ + &startupInfo, + &procInfo + ); + PR_Free(wideCmdLine); + PR_Free(wideCwd); +#else + retVal = CreateProcess(NULL, + cmdLine, + NULL, /* security attributes for the new + * process */ + NULL, /* security attributes for the primary + * thread in the new process */ + TRUE, /* inherit handles */ + creationFlags, + envBlock, /* an environment block, consisting + * of a null-terminated block of + * null-terminated strings. Each + * string is in the form: + * name=value + * XXX: usually NULL */ + cwd, /* current drive and directory */ + &startupInfo, + &procInfo + ); +#endif + + if (retVal == FALSE) { + /* XXX what error code? */ + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + goto errorExit; + } + + CloseHandle(procInfo.hThread); + proc->md.handle = procInfo.hProcess; + proc->md.id = procInfo.dwProcessId; + + PR_DELETE(cmdLine); + if (newEnvp) { + PR_DELETE(newEnvp); + } + if (envBlock) { + PR_DELETE(envBlock); + } + return proc; + +errorExit: + if (cmdLine) { + PR_DELETE(cmdLine); + } + if (newEnvp) { + PR_DELETE(newEnvp); + } + if (envBlock) { + PR_DELETE(envBlock); + } + if (proc) { + PR_DELETE(proc); + } + return NULL; +} /* _PR_CreateWindowsProcess */ + +PRStatus _PR_DetachWindowsProcess(PRProcess *process) +{ + CloseHandle(process->md.handle); + PR_DELETE(process); + return PR_SUCCESS; +} + +/* + * XXX: This implementation is a temporary quick solution. + * It can be called by native threads only (not by fibers). + */ +PRStatus _PR_WaitWindowsProcess(PRProcess *process, + PRInt32 *exitCode) +{ + DWORD dwRetVal; + + dwRetVal = WaitForSingleObject(process->md.handle, INFINITE); + if (dwRetVal == WAIT_FAILED) { + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; + } + PR_ASSERT(dwRetVal == WAIT_OBJECT_0); + if (exitCode != NULL && + GetExitCodeProcess(process->md.handle, exitCode) == FALSE) { + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; + } + CloseHandle(process->md.handle); + PR_DELETE(process); + return PR_SUCCESS; +} + +PRStatus _PR_KillWindowsProcess(PRProcess *process) +{ + /* + * On Unix, if a process terminates normally, its exit code is + * between 0 and 255. So here on Windows, we use the exit code + * 256 to indicate that the process is killed. + */ + if (TerminateProcess(process->md.handle, 256)) { + return PR_SUCCESS; + } + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; +} + +PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen) +{ + PRIntn rv; + PRInt32 syserror; + + rv = gethostname(name, (PRInt32) namelen); + if (0 == rv) { + return PR_SUCCESS; + } + syserror = WSAGetLastError(); + PR_ASSERT(WSANOTINITIALISED != syserror); + _PR_MD_MAP_GETHOSTNAME_ERROR(syserror); + return PR_FAILURE; +} + +PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char *name, PRUint32 namelen) +{ + OSVERSIONINFO osvi; + + PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) || + (cmd == PR_SI_RELEASE_BUILD)); + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if (! GetVersionEx (&osvi) ) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + + switch (osvi.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + if (PR_SI_SYSNAME == cmd) { + (void)PR_snprintf(name, namelen, "Windows_NT"); + } + else if (PR_SI_RELEASE == cmd) { + (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, + osvi.dwMinorVersion); + } + else if (PR_SI_RELEASE_BUILD == cmd) { + (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber); + } + break; + case VER_PLATFORM_WIN32_WINDOWS: + if (PR_SI_SYSNAME == cmd) { + if ((osvi.dwMajorVersion > 4) || + ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion > 0))) { + (void)PR_snprintf(name, namelen, "Windows_98"); + } + else { + (void)PR_snprintf(name, namelen, "Windows_95"); + } + } else if (PR_SI_RELEASE == cmd) { + (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, + osvi.dwMinorVersion); + } else if (PR_SI_RELEASE_BUILD == cmd) { + (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber); + } + break; +#ifdef VER_PLATFORM_WIN32_CE + case VER_PLATFORM_WIN32_CE: + if (PR_SI_SYSNAME == cmd) { + (void)PR_snprintf(name, namelen, "Windows_CE"); + } + else if (PR_SI_RELEASE == cmd) { + (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, + osvi.dwMinorVersion); + } + else if (PR_SI_RELEASE_BUILD == cmd) { + if (namelen) { + *name = 0; + } + } + break; +#endif + default: + if (PR_SI_SYSNAME == cmd) { + (void)PR_snprintf(name, namelen, "Windows_Unknown"); + } + else if (PR_SI_RELEASE == cmd) { + (void)PR_snprintf(name, namelen, "%d.%d",0,0); + } + else if (PR_SI_RELEASE_BUILD == cmd) { + if (namelen) { + *name = 0; + } + } + break; + } + return PR_SUCCESS; +} + +PRStatus _MD_WindowsGetReleaseName(char *name, PRUint32 namelen) +{ + OSVERSIONINFO osvi; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if (! GetVersionEx (&osvi) ) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + + switch (osvi.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + case VER_PLATFORM_WIN32_WINDOWS: + (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, + osvi.dwMinorVersion); + break; + default: + (void)PR_snprintf(name, namelen, "%d.%d",0,0); + break; + } + return PR_SUCCESS; +} + +/* + ********************************************************************** + * + * Memory-mapped files + * + ********************************************************************** + */ + +PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) +{ + DWORD dwHi, dwLo; + DWORD flProtect; + PROsfd osfd; + + osfd = ( fmap->fd == (PRFileDesc*)-1 )? -1 : fmap->fd->secret->md.osfd; + + dwLo = (DWORD) (size & 0xffffffff); + dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff); + + if (fmap->prot == PR_PROT_READONLY) { + flProtect = PAGE_READONLY; + fmap->md.dwAccess = FILE_MAP_READ; + } else if (fmap->prot == PR_PROT_READWRITE) { + flProtect = PAGE_READWRITE; + fmap->md.dwAccess = FILE_MAP_WRITE; + } else { + PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY); +#ifdef WINCE + /* WINCE does not have FILE_MAP_COPY. */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#else + flProtect = PAGE_WRITECOPY; + fmap->md.dwAccess = FILE_MAP_COPY; +#endif + } + + fmap->md.hFileMap = CreateFileMapping( + (HANDLE) osfd, + NULL, + flProtect, + dwHi, + dwLo, + NULL); + + if (fmap->md.hFileMap == NULL) { + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PRInt32 _MD_GetMemMapAlignment(void) +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwAllocationGranularity; +} + +extern PRLogModuleInfo *_pr_shma_lm; + +void * _MD_MemMap( + PRFileMap *fmap, + PROffset64 offset, + PRUint32 len) +{ + DWORD dwHi, dwLo; + void *addr; + + dwLo = (DWORD) (offset & 0xffffffff); + dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff); + if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess, + dwHi, dwLo, len)) == NULL) { + { + 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; +} + +PRStatus _MD_MemUnmap(void *addr, PRUint32 len) +{ + if (UnmapViewOfFile(addr)) { + return PR_SUCCESS; + } + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; +} + +PRStatus _MD_CloseFileMap(PRFileMap *fmap) +{ + CloseHandle(fmap->md.hFileMap); + PR_DELETE(fmap); + return PR_SUCCESS; +} + +PRStatus _MD_SyncMemMap( + PRFileDesc *fd, + void *addr, + PRUint32 len) +{ + PROsfd osfd = fd->secret->md.osfd; + + /* The FlushViewOfFile page on MSDN says: + * To flush all the dirty pages plus the metadata for the file and + * ensure that they are physically written to disk, call + * FlushViewOfFile and then call the FlushFileBuffers function. + */ + if (FlushViewOfFile(addr, len) && FlushFileBuffers((HANDLE) osfd)) { + return PR_SUCCESS; + } + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; +} + +/* + *********************************************************************** + * + * Atomic increment and decrement operations for x86 processors + * + * We don't use InterlockedIncrement and InterlockedDecrement + * because on NT 3.51 and Win95, they return a number with + * the same sign as the incremented/decremented result, rather + * than the result itself. On NT 4.0 these functions do return + * the incremented/decremented result. + * + * The result is returned in the eax register by the inline + * assembly code. We disable the harmless "no return value" + * warning (4035) for these two functions. + * + *********************************************************************** + */ + +#if defined(_M_IX86) || defined(_X86_) + +#pragma warning(disable: 4035) +PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ +#if defined(__GNUC__) + PRInt32 result; + asm volatile ("lock ; xadd %0, %1" + : "=r"(result), "=m"(*val) + : "0"(1), "m"(*val)); + return result + 1; +#else + __asm + { + mov ecx, val + mov eax, 1 + lock xadd dword ptr [ecx], eax + inc eax + } +#endif /* __GNUC__ */ +} +#pragma warning(default: 4035) + +#pragma warning(disable: 4035) +PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ +#if defined(__GNUC__) + PRInt32 result; + asm volatile ("lock ; xadd %0, %1" + : "=r"(result), "=m"(*val) + : "0"(-1), "m"(*val)); + //asm volatile("lock ; xadd %0, %1" : "=m" (val), "=a" (result) : "-1" (1)); + return result - 1; +#else + __asm + { + mov ecx, val + mov eax, 0ffffffffh + lock xadd dword ptr [ecx], eax + dec eax + } +#endif /* __GNUC__ */ +} +#pragma warning(default: 4035) + +#pragma warning(disable: 4035) +PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *intp, PRInt32 val) +{ +#if defined(__GNUC__) + PRInt32 result; + //asm volatile("lock ; xadd %1, %0" : "=m" (intp), "=a" (result) : "1" (val)); + asm volatile ("lock ; xadd %0, %1" + : "=r"(result), "=m"(*intp) + : "0"(val), "m"(*intp)); + return result + val; +#else + __asm + { + mov ecx, intp + mov eax, val + mov edx, eax + lock xadd dword ptr [ecx], eax + add eax, edx + } +#endif /* __GNUC__ */ +} +#pragma warning(default: 4035) + +#ifdef _PR_HAVE_ATOMIC_CAS + +#pragma warning(disable: 4035) +void +PR_StackPush(PRStack *stack, PRStackElem *stack_elem) +{ +#if defined(__GNUC__) + void **tos = (void **) stack; + void *tmp; + +retry: + if (*tos == (void *) -1) { + goto retry; + } + + __asm__("xchg %0,%1" + : "=r" (tmp), "=m"(*tos) + : "0" (-1), "m"(*tos)); + + if (tmp == (void *) -1) { + goto retry; + } + + *(void **)stack_elem = tmp; + __asm__("" : : : "memory"); + *tos = stack_elem; +#else + __asm + { + mov ebx, stack + mov ecx, stack_elem + retry: mov eax,[ebx] + cmp eax,-1 + je retry + mov eax,-1 + xchg dword ptr [ebx], eax + cmp eax,-1 + je retry + mov [ecx],eax + mov [ebx],ecx + } +#endif /* __GNUC__ */ +} +#pragma warning(default: 4035) + +#pragma warning(disable: 4035) +PRStackElem * +PR_StackPop(PRStack *stack) +{ +#if defined(__GNUC__) + void **tos = (void **) stack; + void *tmp; + +retry: + if (*tos == (void *) -1) { + goto retry; + } + + __asm__("xchg %0,%1" + : "=r" (tmp), "=m"(*tos) + : "0" (-1), "m"(*tos)); + + if (tmp == (void *) -1) { + goto retry; + } + + if (tmp != (void *) 0) + { + void *next = *(void **)tmp; + *tos = next; + *(void **)tmp = 0; + } + else { + *tos = tmp; + } + + return tmp; +#else + __asm + { + mov ebx, stack + retry: mov eax,[ebx] + cmp eax,-1 + je retry + mov eax,-1 + xchg dword ptr [ebx], eax + cmp eax,-1 + je retry + cmp eax,0 + je empty + mov ecx,[eax] + mov [ebx],ecx + mov [eax],0 + jmp done + empty: + mov [ebx],eax + done: + } +#endif /* __GNUC__ */ +} +#pragma warning(default: 4035) + +#endif /* _PR_HAVE_ATOMIC_CAS */ + +#endif /* x86 processors */ diff --git a/nsprpub/pr/src/md/windows/ntsec.c b/nsprpub/pr/src/md/windows/ntsec.c new file mode 100644 index 0000000000..0006e8399e --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntsec.c @@ -0,0 +1,279 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/* + * ntsec.c + * + * Implement the POSIX-style mode bits (access permissions) for + * files and other securable objects in Windows NT using Windows + * NT's security descriptors with appropriate discretionary + * access-control lists. + */ + +/* + * The security identifiers (SIDs) for owner, primary group, + * and the Everyone (World) group. + * + * These SIDs are looked up during NSPR initialization and + * saved in this global structure (see _PR_NT_InitSids) so + * that _PR_NT_MakeSecurityDescriptorACL doesn't need to + * look them up every time. + */ +static struct { + PSID owner; + PSID group; + PSID everyone; +} _pr_nt_sids; + +/* + * Initialize the SIDs for owner, primary group, and the Everyone + * group in the _pr_nt_sids structure. + * + * This function needs to be called by NSPR initialization. + */ +void _PR_NT_InitSids(void) +{ +#ifdef WINCE /* not supported */ + return; +#else + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; + HANDLE hToken = NULL; /* initialized to an arbitrary value to + * silence a Purify UMR warning */ + PSID infoBuffer[1024/sizeof(PSID)]; /* defined as an array of PSIDs + * to force proper alignment */ + PTOKEN_OWNER pTokenOwner = (PTOKEN_OWNER) infoBuffer; + PTOKEN_PRIMARY_GROUP pTokenPrimaryGroup + = (PTOKEN_PRIMARY_GROUP) infoBuffer; + DWORD dwLength; + BOOL rv; + + /* + * Look up and make a copy of the owner and primary group + * SIDs in the access token of the calling process. + */ + rv = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); + if (rv == 0) { + /* + * On non-NT systems, this function is not implemented + * (error code ERROR_CALL_NOT_IMPLEMENTED), and neither are + * the other security functions. There is no point in + * going further. + * + * A process with insufficient access permissions may fail + * with the error code ERROR_ACCESS_DENIED. + */ + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("_PR_NT_InitSids: OpenProcessToken() failed. Error: %d", + GetLastError())); + return; + } + + rv = GetTokenInformation(hToken, TokenOwner, infoBuffer, + sizeof(infoBuffer), &dwLength); + PR_ASSERT(rv != 0); + dwLength = GetLengthSid(pTokenOwner->Owner); + _pr_nt_sids.owner = (PSID) PR_Malloc(dwLength); + PR_ASSERT(_pr_nt_sids.owner != NULL); + rv = CopySid(dwLength, _pr_nt_sids.owner, pTokenOwner->Owner); + PR_ASSERT(rv != 0); + + rv = GetTokenInformation(hToken, TokenPrimaryGroup, infoBuffer, + sizeof(infoBuffer), &dwLength); + PR_ASSERT(rv != 0); + dwLength = GetLengthSid(pTokenPrimaryGroup->PrimaryGroup); + _pr_nt_sids.group = (PSID) PR_Malloc(dwLength); + PR_ASSERT(_pr_nt_sids.group != NULL); + rv = CopySid(dwLength, _pr_nt_sids.group, + pTokenPrimaryGroup->PrimaryGroup); + PR_ASSERT(rv != 0); + + rv = CloseHandle(hToken); + PR_ASSERT(rv != 0); + + /* Create a well-known SID for the Everyone group. */ + rv = AllocateAndInitializeSid(&SIDAuthWorld, 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &_pr_nt_sids.everyone); + PR_ASSERT(rv != 0); +#endif +} + +/* + * Free the SIDs for owner, primary group, and the Everyone group + * in the _pr_nt_sids structure. + * + * This function needs to be called by NSPR cleanup. + */ +void +_PR_NT_FreeSids(void) +{ +#ifdef WINCE + return; +#else + if (_pr_nt_sids.owner) { + PR_Free(_pr_nt_sids.owner); + } + if (_pr_nt_sids.group) { + PR_Free(_pr_nt_sids.group); + } + if (_pr_nt_sids.everyone) { + FreeSid(_pr_nt_sids.everyone); + } +#endif +} + +/* + * Construct a security descriptor whose discretionary access-control + * list implements the specified mode bits. The SIDs for owner, group, + * and everyone are obtained from the global _pr_nt_sids structure. + * Both the security descriptor and access-control list are returned + * and should be freed by a _PR_NT_FreeSecurityDescriptorACL call. + * + * The accessTable array maps NSPR's read, write, and execute access + * rights to the corresponding NT access rights for the securable + * object. + */ +PRStatus +_PR_NT_MakeSecurityDescriptorACL( + PRIntn mode, + DWORD accessTable[], + PSECURITY_DESCRIPTOR *resultSD, + PACL *resultACL) +{ +#ifdef WINCE + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#else + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + DWORD cbACL; /* size of ACL */ + DWORD accessMask; + + if (_pr_nt_sids.owner == NULL) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + + pSD = (PSECURITY_DESCRIPTOR) PR_Malloc(SECURITY_DESCRIPTOR_MIN_LENGTH); + if (pSD == NULL) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + if (!SetSecurityDescriptorOwner(pSD, _pr_nt_sids.owner, FALSE)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + if (!SetSecurityDescriptorGroup(pSD, _pr_nt_sids.group, FALSE)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + + /* + * Construct a discretionary access-control list with three + * access-control entries, one each for owner, primary group, + * and Everyone. + */ + + cbACL = sizeof(ACL) + + 3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + + GetLengthSid(_pr_nt_sids.owner) + + GetLengthSid(_pr_nt_sids.group) + + GetLengthSid(_pr_nt_sids.everyone); + pACL = (PACL) PR_Malloc(cbACL); + if (pACL == NULL) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + accessMask = 0; + if (mode & 00400) { + accessMask |= accessTable[0]; + } + if (mode & 00200) { + accessMask |= accessTable[1]; + } + if (mode & 00100) { + accessMask |= accessTable[2]; + } + if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask, + _pr_nt_sids.owner)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + accessMask = 0; + if (mode & 00040) { + accessMask |= accessTable[0]; + } + if (mode & 00020) { + accessMask |= accessTable[1]; + } + if (mode & 00010) { + accessMask |= accessTable[2]; + } + if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask, + _pr_nt_sids.group)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + accessMask = 0; + if (mode & 00004) { + accessMask |= accessTable[0]; + } + if (mode & 00002) { + accessMask |= accessTable[1]; + } + if (mode & 00001) { + accessMask |= accessTable[2]; + } + if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask, + _pr_nt_sids.everyone)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + + if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + goto failed; + } + + *resultSD = pSD; + *resultACL = pACL; + return PR_SUCCESS; + +failed: + if (pSD) { + PR_Free(pSD); + } + if (pACL) { + PR_Free(pACL); + } + return PR_FAILURE; +#endif +} + +/* + * Free the specified security descriptor and access-control list + * previously created by _PR_NT_MakeSecurityDescriptorACL. + */ +void +_PR_NT_FreeSecurityDescriptorACL(PSECURITY_DESCRIPTOR pSD, PACL pACL) +{ + if (pSD) { + PR_Free(pSD); + } + if (pACL) { + PR_Free(pACL); + } +} diff --git a/nsprpub/pr/src/md/windows/ntsem.c b/nsprpub/pr/src/md/windows/ntsem.c new file mode 100644 index 0000000000..757bc839e2 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntsem.c @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * NT-specific semaphore handling code. + * + */ + + +#include "primpl.h" + + +void +_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value) +{ + md->sem = CreateSemaphore(NULL, value, 0x7fffffff, NULL); +} + +void +_PR_MD_DESTROY_SEM(_MDSemaphore *md) +{ + CloseHandle(md->sem); +} + +PRStatus +_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks) +{ + int rv; + + rv = WaitForSingleObject(md->sem, PR_IntervalToMilliseconds(ticks)); + + if (rv == WAIT_OBJECT_0) { + return PR_SUCCESS; + } + else { + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_WAIT_SEM(_MDSemaphore *md) +{ + return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT); +} + +void +_PR_MD_POST_SEM(_MDSemaphore *md) +{ + ReleaseSemaphore(md->sem, 1, NULL); +} diff --git a/nsprpub/pr/src/md/windows/ntthread.c b/nsprpub/pr/src/md/windows/ntthread.c new file mode 100644 index 0000000000..395a80dcc1 --- /dev/null +++ b/nsprpub/pr/src/md/windows/ntthread.c @@ -0,0 +1,590 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include <process.h> /* for _beginthreadex() */ + +/* --- globals ------------------------------------------------ */ +PRLock *_pr_schedLock = NULL; +_PRInterruptTable _pr_interruptTable[] = { { 0 } }; + +BOOL _pr_use_static_tls = TRUE; +__declspec(thread) PRThread *_pr_current_fiber; +__declspec(thread) PRThread *_pr_fiber_last_run; +__declspec(thread) _PRCPU *_pr_current_cpu; +__declspec(thread) PRUintn _pr_ints_off; +DWORD _pr_currentFiberIndex; +DWORD _pr_lastFiberIndex; +DWORD _pr_currentCPUIndex; +DWORD _pr_intsOffIndex; + +_MDLock _nt_idleLock; +PRCList _nt_idleList; +PRUint32 _nt_idleCount; + +extern __declspec(thread) PRThread *_pr_io_restarted_io; +extern DWORD _pr_io_restartedIOIndex; + +typedef HRESULT (WINAPI *SETTHREADDESCRIPTION)(HANDLE, PCWSTR); +static SETTHREADDESCRIPTION sSetThreadDescription = NULL; + +/* Must check the restarted_io *before* decrementing no_sched to 0 */ +#define POST_SWITCH_WORK() \ + PR_BEGIN_MACRO \ + PRThread *restarted_io = \ + (_pr_use_static_tls ? _pr_io_restarted_io \ + : (PRThread *) TlsGetValue(_pr_io_restartedIOIndex)); \ + if (restarted_io) { \ + _nt_handle_restarted_io(restarted_io); \ + } \ + _PR_MD_LAST_THREAD()->no_sched = 0; \ + PR_END_MACRO + +void +_nt_handle_restarted_io(PRThread *restarted_io) +{ + /* After the switch we can resume an IO if needed. + * XXXMB - this needs to be done in create thread, since that could + * be the result for a context switch too.. + */ + PR_ASSERT(restarted_io->io_suspended == PR_TRUE); + PR_ASSERT(restarted_io->md.thr_bound_cpu == restarted_io->cpu); + + _PR_THREAD_LOCK(restarted_io); + if (restarted_io->io_pending == PR_FALSE) { + + /* The IO already completed, put us back on the runq. */ + int pri = restarted_io->priority; + + restarted_io->state = _PR_RUNNABLE; + _PR_RUNQ_LOCK(restarted_io->cpu); + _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri); + _PR_RUNQ_UNLOCK(restarted_io->cpu); + } else { + _PR_SLEEPQ_LOCK(restarted_io->cpu); + _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep); + _PR_SLEEPQ_UNLOCK(restarted_io->cpu); + } + restarted_io->io_suspended = PR_FALSE; + restarted_io->md.thr_bound_cpu = NULL; + + _PR_THREAD_UNLOCK(restarted_io); + + if (_pr_use_static_tls) { + _pr_io_restarted_io = NULL; + } else { + TlsSetValue(_pr_io_restartedIOIndex, NULL); + } +} + +void +_PR_MD_EARLY_INIT() +{ + HMODULE hModule; + + _MD_NEW_LOCK( &_nt_idleLock ); + _nt_idleCount = 0; + PR_INIT_CLIST(&_nt_idleList); + +#if 0 + /* Make the clock tick at least once per millisecond */ + if ( timeBeginPeriod(1) == TIMERR_NOCANDO) { + /* deep yoghurt; clock doesn't tick fast enough! */ + PR_ASSERT(0); + } +#endif + + if (!_pr_use_static_tls) { + _pr_currentFiberIndex = TlsAlloc(); + _pr_lastFiberIndex = TlsAlloc(); + _pr_currentCPUIndex = TlsAlloc(); + _pr_intsOffIndex = TlsAlloc(); + _pr_io_restartedIOIndex = TlsAlloc(); + } + + // SetThreadDescription is Windows 10 build 1607+ + hModule = GetModuleHandleW(L"kernel32.dll"); + if (hModule) { + sSetThreadDescription = + (SETTHREADDESCRIPTION) GetProcAddress( + hModule, + "SetThreadDescription"); + } +} + +void _PR_MD_CLEANUP_BEFORE_EXIT(void) +{ + _PR_NT_FreeSids(); + + WSACleanup(); + + if (!_pr_use_static_tls) { + TlsFree(_pr_currentFiberIndex); + TlsFree(_pr_lastFiberIndex); + TlsFree(_pr_currentCPUIndex); + TlsFree(_pr_intsOffIndex); + TlsFree(_pr_io_restartedIOIndex); + } +} + +PRStatus +_PR_MD_INIT_THREAD(PRThread *thread) +{ + thread->md.overlapped.ioModel = _MD_BlockingIO; + thread->md.overlapped.data.mdThread = &thread->md; + + if (thread->flags & _PR_GLOBAL_SCOPE) { + if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { + /* + ** Warning: + ** -------- + ** NSPR requires a real handle to every thread. + ** GetCurrentThread() returns a pseudo-handle which + ** is not suitable for some thread operations (e.g., + ** suspending). Therefore, get a real handle from + ** the pseudo handle via DuplicateHandle(...) + */ + DuplicateHandle( + GetCurrentProcess(), /* Process of source handle */ + GetCurrentThread(), /* Pseudo Handle to dup */ + GetCurrentProcess(), /* Process of handle */ + &(thread->md.handle), /* resulting handle */ + 0L, /* access flags */ + FALSE, /* Inheritable */ + DUPLICATE_SAME_ACCESS); /* Options */ + } + + /* Create the blocking IO semaphore */ + thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL); + 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; +} + +static unsigned __stdcall +pr_root(void *arg) +{ + PRThread *thread = (PRThread *)arg; + thread->md.start(thread); + return 0; +} + +PRStatus +_PR_MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + + thread->md.start = start; + thread->md.handle = (HANDLE) _beginthreadex( + NULL, + thread->stack->stackSize, + pr_root, + (void *)thread, + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + &(thread->id)); + if(!thread->md.handle) { + PRErrorCode prerror; + thread->md.fiber_last_error = GetLastError(); + switch (errno) { + case ENOMEM: + prerror = PR_OUT_OF_MEMORY_ERROR; + break; + case EAGAIN: + prerror = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case EINVAL: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + } + PR_SetError(prerror, errno); + return PR_FAILURE; + } + + thread->md.id = thread->id; + /* + * On windows, a thread is created with a thread priority of + * THREAD_PRIORITY_NORMAL. + */ + if (priority != PR_PRIORITY_NORMAL) { + _PR_MD_SET_PRIORITY(&(thread->md), priority); + } + + /* Activate the thread */ + if ( ResumeThread( thread->md.handle ) != -1) { + return PR_SUCCESS; + } + + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; +} + +void +_PR_MD_JOIN_THREAD(_MDThread *md) +{ + DWORD rv; + + rv = WaitForSingleObject(md->handle, INFINITE); + PR_ASSERT(WAIT_OBJECT_0 == rv); +} + +void +_PR_MD_END_THREAD(void) +{ + _endthreadex(0); +} + +void +_PR_MD_YIELD(void) +{ + /* Can NT really yield at all? */ + Sleep(0); +} + +void +_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) +{ + int nativePri; + BOOL rv; + + if (newPri < PR_PRIORITY_FIRST) { + newPri = PR_PRIORITY_FIRST; + } else if (newPri > PR_PRIORITY_LAST) { + newPri = PR_PRIORITY_LAST; + } + switch (newPri) { + case PR_PRIORITY_LOW: + nativePri = THREAD_PRIORITY_BELOW_NORMAL; + break; + case PR_PRIORITY_NORMAL: + nativePri = THREAD_PRIORITY_NORMAL; + break; + case PR_PRIORITY_HIGH: + nativePri = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case PR_PRIORITY_URGENT: + nativePri = THREAD_PRIORITY_HIGHEST; + } + rv = SetThreadPriority(thread->handle, nativePri); + PR_ASSERT(rv); + if (!rv) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_SetThreadPriority: can't set thread priority\n")); + } + return; +} + +const DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push,8) +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; +#pragma pack(pop) + +void +_PR_MD_SET_CURRENT_THREAD_NAME(const char *name) +{ +#ifdef _MSC_VER + THREADNAME_INFO info; +#endif + + if (sSetThreadDescription) { + WCHAR wideName[MAX_PATH]; + if (MultiByteToWideChar(CP_ACP, 0, name, -1, wideName, MAX_PATH)) { + sSetThreadDescription(GetCurrentThread(), wideName); + } + } + +#ifdef _MSC_VER + if (!IsDebuggerPresent()) { + return; + } + + info.dwType = 0x1000; + info.szName = (char*) name; + info.dwThreadID = -1; + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, + 0, + sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR*)&info); + } __except(EXCEPTION_CONTINUE_EXECUTION) { + } +#endif +} + +void +_PR_MD_CLEAN_THREAD(PRThread *thread) +{ + BOOL rv; + + if (thread->md.acceptex_buf) { + PR_DELETE(thread->md.acceptex_buf); + } + + if (thread->md.xmit_bufs) { + PR_DELETE(thread->md.xmit_bufs); + } + + if (thread->md.blocked_sema) { + rv = CloseHandle(thread->md.blocked_sema); + 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); + PR_ASSERT(rv); + thread->md.handle = 0; + } + + /* Don't call DeleteFiber on current fiber or we'll kill the whole thread. + * Don't call free(thread) until we've switched off the thread. + * So put this fiber (or thread) on a list to be deleted by the idle + * fiber next time we have a chance. + */ + if (!(thread->flags & (_PR_ATTACHED|_PR_GLOBAL_SCOPE))) { + _MD_LOCK(&_nt_idleLock); + _nt_idleCount++; + PR_APPEND_LINK(&thread->links, &_nt_idleList); + _MD_UNLOCK(&_nt_idleLock); + } +} + +void +_PR_MD_EXIT_THREAD(PRThread *thread) +{ + BOOL rv; + + if (thread->md.acceptex_buf) { + PR_DELETE(thread->md.acceptex_buf); + } + + if (thread->md.xmit_bufs) { + PR_DELETE(thread->md.xmit_bufs); + } + + if (thread->md.blocked_sema) { + rv = CloseHandle(thread->md.blocked_sema); + 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); + PR_ASSERT(rv); + thread->md.handle = 0; + } + + if (thread->flags & _PR_GLOBAL_SCOPE) { + _MD_SET_CURRENT_THREAD(NULL); + } +} + + +void +_PR_MD_EXIT(PRIntn status) +{ + _exit(status); +} + +#ifdef HAVE_FIBERS + +void +_pr_fiber_mainline(void *unused) +{ + PRThread *fiber = _PR_MD_CURRENT_THREAD(); + + POST_SWITCH_WORK(); + + fiber->md.fiber_fn(fiber->md.fiber_arg); +} + +PRThread *_PR_MD_CREATE_USER_THREAD( + PRUint32 stacksize, void (*start)(void *), void *arg) +{ + PRThread *thread; + + if ( (thread = PR_NEW(PRThread)) == NULL ) { + return NULL; + } + + memset(thread, 0, sizeof(PRThread)); + thread->md.fiber_fn = start; + thread->md.fiber_arg = arg; + thread->md.fiber_stacksize = stacksize; + return thread; +} + +void +_PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *thread) +{ + thread->md.fiber_id = ConvertThreadToFiber(NULL); + PR_ASSERT(thread->md.fiber_id); + _MD_SET_CURRENT_THREAD(thread); + _MD_SET_LAST_THREAD(thread); + thread->no_sched = 1; + return; +} + +void +_PR_MD_INIT_CONTEXT(PRThread *thread, char *top, void (*start) (void), PRBool *status) +{ + thread->md.fiber_fn = (void (*)(void *))start; + thread->md.fiber_id = CreateFiber(thread->md.fiber_stacksize, + (LPFIBER_START_ROUTINE)_pr_fiber_mainline, NULL); + if (thread->md.fiber_id != 0) { + *status = PR_TRUE; + } + else { + DWORD oserror = GetLastError(); + PRErrorCode prerror; + if (oserror == ERROR_NOT_ENOUGH_MEMORY) { + prerror = PR_OUT_OF_MEMORY_ERROR; + } else { + prerror = PR_UNKNOWN_ERROR; + } + PR_SetError(prerror, oserror); + *status = PR_FALSE; + } +} + +void +_PR_MD_SWITCH_CONTEXT(PRThread *thread) +{ + PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) ); + + thread->md.fiber_last_error = GetLastError(); + _PR_Schedule(); +} + +void +_PR_MD_RESTORE_CONTEXT(PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) ); + + /* The user-level code for yielding will happily add ourselves to the runq + * and then switch to ourselves; the NT fibers can't handle switching to + * ourselves. + */ + if (thread != me) { + SetLastError(thread->md.fiber_last_error); + _MD_SET_CURRENT_THREAD(thread); + _PR_MD_SET_LAST_THREAD(me); + thread->no_sched = 1; + SwitchToFiber(thread->md.fiber_id); + POST_SWITCH_WORK(); + } +} + + +#endif /* HAVE_FIBERS */ + +PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) +{ + int rv; + + rv = SetThreadAffinityMask(thread->md.handle, mask); + + return rv?0:-1; +} + +PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) +{ + PRInt32 rv, system_mask; + + rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask); + + return rv?0:-1; +} + +void +_PR_MD_SUSPEND_CPU(_PRCPU *cpu) +{ + _PR_MD_SUSPEND_THREAD(cpu->thread); +} + +void +_PR_MD_RESUME_CPU(_PRCPU *cpu) +{ + _PR_MD_RESUME_THREAD(cpu->thread); +} + +void +_PR_MD_SUSPEND_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + /* + ** There seems to be some doubt about whether or not SuspendThread + ** is a synchronous function. The test afterwards is to help veriry + ** that it is, which is what Microsoft says it is. + */ + PRUintn rv = SuspendThread(thread->md.handle); + PR_ASSERT(0xffffffffUL != rv); + } +} + +void +_PR_MD_RESUME_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + ResumeThread(thread->md.handle); + } +} + +PRThread* +_MD_CURRENT_THREAD(void) +{ + PRThread *thread; + + thread = _MD_GET_ATTACHED_THREAD(); + + if (NULL == thread) { + thread = _PRI_AttachThread( + PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0); + } + PR_ASSERT(thread != NULL); + return thread; +} + diff --git a/nsprpub/pr/src/md/windows/objs.mk b/nsprpub/pr/src/md/windows/objs.mk new file mode 100644 index 0000000000..89f022e8ad --- /dev/null +++ b/nsprpub/pr/src/md/windows/objs.mk @@ -0,0 +1,48 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +ifeq ($(OS_TARGET),WINNT) +CSRCS = ntmisc.c \ + ntsec.c \ + ntsem.c \ + ntinrval.c \ + ntgc.c \ + ntio.c \ + ntthread.c \ + ntdllmn.c \ + win32_errors.c \ + w32ipcsem.c \ + w32poll.c \ + w32rng.c \ + w32shm.c +else +ifeq (,$(filter-out WIN95 WINCE WINMO, $(OS_TARGET))) +CSRCS = ntmisc.c \ + ntsec.c \ + ntsem.c \ + ntinrval.c \ + ntgc.c \ + w95thred.c \ + w95io.c \ + w95cv.c \ + w95sock.c \ + win32_errors.c \ + w32ipcsem.c \ + w32poll.c \ + w32rng.c \ + w32shm.c \ + w95dllmain.c +else +endif # win95 +endif # winnt + +CSRCS += $(PR_MD_CSRCS) +ASFILES += $(PR_MD_ASFILES) + +OBJS += $(addprefix md/windows/$(OBJDIR)/,$(CSRCS:.c=.$(OBJ_SUFFIX))) \ + $(addprefix md/windows/$(OBJDIR)/,$(ASFILES:.s=.$(OBJ_SUFFIX))) + + diff --git a/nsprpub/pr/src/md/windows/w32ipcsem.c b/nsprpub/pr/src/md/windows/w32ipcsem.c new file mode 100644 index 0000000000..500009352b --- /dev/null +++ b/nsprpub/pr/src/md/windows/w32ipcsem.c @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: w32ipcsem.c + * Description: implements named semaphores for NT and WIN95. + */ + +#include "primpl.h" + +#ifdef WINCE +static HANDLE OpenSemaphore(DWORD inDesiredAccess, + BOOL inInheritHandle, + const char *inName) +{ + HANDLE retval = NULL; + HANDLE semaphore = NULL; + PRUnichar wideName[MAX_PATH]; /* name size is limited to MAX_PATH */ + + MultiByteToWideChar(CP_ACP, 0, inName, -1, wideName, MAX_PATH); + /* 0x7fffffff is the max count for our semaphore */ + semaphore = CreateSemaphoreW(NULL, 0, 0x7fffffff, wideName); + if (NULL != semaphore) { + DWORD lastErr = GetLastError(); + + if (ERROR_ALREADY_EXISTS != lastErr) { + CloseHandle(semaphore); + } + else { + retval = semaphore; + } + } + return retval; +} +#endif + +/* + * NSPR-to-NT access right mapping table for semaphore objects. + * + * The SYNCHRONIZE access is required by WaitForSingleObject. + * The SEMAPHORE_MODIFY_STATE access is required by ReleaseSemaphore. + * The OR of these three access masks must equal SEMAPHORE_ALL_ACCESS. + * This is because if a semaphore object with the specified name + * exists, CreateSemaphore requests SEMAPHORE_ALL_ACCESS access to + * the existing object. + */ +static DWORD semAccessTable[] = { + STANDARD_RIGHTS_REQUIRED|0x1, /* read (0x1 is "query state") */ + STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, /* write */ + 0 /* execute */ +}; + +#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; + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + sem = PR_NEW(PRSem); + if (sem == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + if (flags & PR_SEM_CREATE) { + if (_PR_NT_MakeSecurityDescriptorACL(mode, semAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } +#ifdef WINCE + { + /* The size of a sem's name is limited to MAX_PATH. */ + PRUnichar wosname[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, osname, -1, wosname, MAX_PATH); + sem->sem = CreateSemaphoreW(lpSA, value, 0x7fffffff, wosname); + } +#else + sem->sem = CreateSemaphoreA(lpSA, value, 0x7fffffff, osname); +#endif + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + 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_MODIFY_STATE|SYNCHRONIZE, 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/nsprpub/pr/src/md/windows/w32poll.c b/nsprpub/pr/src/md/windows/w32poll.c new file mode 100644 index 0000000000..241c7e3e47 --- /dev/null +++ b/nsprpub/pr/src/md/windows/w32poll.c @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file implements _PR_MD_PR_POLL for Win32. + */ + +/* The default value of FD_SETSIZE is 64. */ +#define FD_SETSIZE 1024 + +#include "primpl.h" + +#if !defined(_PR_GLOBAL_THREADS_ONLY) + +struct select_data_s { + PRInt32 status; + PRInt32 error; + fd_set *rd, *wt, *ex; + const struct timeval *tv; +}; + +static void +_PR_MD_select_thread(void *cdata) +{ + struct select_data_s *cd = (struct select_data_s *)cdata; + + cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv); + + if (cd->status == SOCKET_ERROR) { + cd->error = WSAGetLastError(); + } +} + +int _PR_NTFiberSafeSelect( + int nfds, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + const struct timeval *timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + int ready; + + if (_PR_IS_NATIVE_THREAD(me)) { + ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout); + } + else + { + /* + ** Creating a new thread on each call!! + ** I guess web server doesn't use non-block I/O. + */ + PRThread *selectThread; + struct select_data_s data; + data.status = 0; + data.error = 0; + data.rd = readfds; + data.wt = writefds; + data.ex = exceptfds; + data.tv = timeout; + + selectThread = PR_CreateThread( + PR_USER_THREAD, _PR_MD_select_thread, &data, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (selectThread == NULL) { + return -1; + } + + PR_JoinThread(selectThread); + ready = data.status; + if (ready == SOCKET_ERROR) { + WSASetLastError(data.error); + } + } + return ready; +} + +#endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */ + +PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + int ready, err; + fd_set rd, wt, ex; + fd_set *rdp, *wtp, *exp; + int nrd, nwt, nex; + PRFileDesc *bottom; + PRPollDesc *pd, *epd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + struct timeval tv, *tvp = NULL; + + if (_PR_PENDING_INTERRUPT(me)) + { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + + /* + ** Is it an empty set? If so, just sleep for the timeout and return + */ + if (0 == npds) + { + PR_Sleep(timeout); + return 0; + } + + nrd = nwt = nex = 0; + FD_ZERO(&rd); + FD_ZERO(&wt); + FD_ZERO(&ex); + + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + SOCKET osfd; + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + if (pd->in_flags & PR_POLL_READ) + { + in_flags_read = (pd->fd->methods->poll)( + pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE), + &out_flags_read); + } + if (pd->in_flags & PR_POLL_WRITE) + { + in_flags_write = (pd->fd->methods->poll)( + pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ), + &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one's ready right now (buffered input) */ + if (0 == ready) + { + /* + * We will have to return without calling the + * system poll/select function. So zero the + * out_flags fields of all the poll descriptors + * before this one. + */ + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; + pd->out_flags = out_flags_read | out_flags_write; + } + else + { + pd->out_flags = 0; /* pre-condition */ + /* make sure this is an NSPR supported stack */ + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + /* ignore a socket without PR_NSPR_IO_LAYER available */ + + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + osfd = (SOCKET) bottom->secret->md.osfd; + if (in_flags_read & PR_POLL_READ) + { + pd->out_flags |= _PR_POLL_READ_SYS_READ; + FD_SET(osfd, &rd); + nrd++; + } + if (in_flags_read & PR_POLL_WRITE) + { + pd->out_flags |= _PR_POLL_READ_SYS_WRITE; + FD_SET(osfd, &wt); + nwt++; + } + if (in_flags_write & PR_POLL_READ) + { + pd->out_flags |= _PR_POLL_WRITE_SYS_READ; + FD_SET(osfd, &rd); + nrd++; + } + if (in_flags_write & PR_POLL_WRITE) + { + pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE; + FD_SET(osfd, &wt); + nwt++; + } + if (pd->in_flags & PR_POLL_EXCEPT) { + FD_SET(osfd, &ex); + nex++; + } + } + } + else + { + if (0 == ready) + { + PRPollDesc *prev; + for (prev = pds; prev < pd; prev++) + { + prev->out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pd->out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + pd->out_flags = 0; + } + } + + if (0 != ready) { + return ready; /* no need to block */ + } + + /* + * FD_SET does nothing if the fd_set's internal fd_array is full. If + * nrd, nwt, or nex is greater than FD_SETSIZE, we know FD_SET must + * have failed to insert an osfd into the corresponding fd_set, and + * therefore we should fail. + */ + if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + rdp = (0 == nrd) ? NULL : &rd; + wtp = (0 == nwt) ? NULL : &wt; + exp = (0 == nex) ? NULL : &ex; + + if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) { + PR_Sleep(timeout); + return 0; + } + + if (timeout != PR_INTERVAL_NO_TIMEOUT) + { + PRInt32 ticksPerSecond = PR_TicksPerSecond(); + tv.tv_sec = timeout / ticksPerSecond; + tv.tv_usec = PR_IntervalToMicroseconds( timeout % ticksPerSecond ); + tvp = &tv; + } + +#if defined(_PR_GLOBAL_THREADS_ONLY) + ready = _MD_SELECT(0, rdp, wtp, exp, tvp); +#else + ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp); +#endif + + /* + ** Now to unravel the select sets back into the client's poll + ** descriptor list. Is this possibly an area for pissing away + ** a few cycles or what? + */ + if (ready > 0) + { + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + PRInt16 out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + SOCKET osfd; + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + PR_ASSERT(NULL != bottom); + + osfd = (SOCKET) bottom->secret->md.osfd; + + if (FD_ISSET(osfd, &rd)) + { + if (pd->out_flags & _PR_POLL_READ_SYS_READ) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(osfd, &wt)) + { + if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) { + out_flags |= PR_POLL_READ; + } + if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(osfd, &ex)) { + out_flags |= PR_POLL_EXCEPT; + } + } + pd->out_flags = out_flags; + if (out_flags) { + ready++; + } + } + PR_ASSERT(ready > 0); + } + else if (ready == SOCKET_ERROR) + { + err = WSAGetLastError(); + if (err == WSAENOTSOCK) + { + /* Find the bad fds */ + int optval; + int optlen = sizeof(optval); + ready = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) + { + pd->out_flags = 0; + if ((NULL != pd->fd) && (0 != pd->in_flags)) + { + bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET, + SO_TYPE, (char *) &optval, &optlen) == -1) + { + PR_ASSERT(WSAGetLastError() == WSAENOTSOCK); + if (WSAGetLastError() == WSAENOTSOCK) + { + pd->out_flags = PR_POLL_NVAL; + ready++; + } + } + } + } + PR_ASSERT(ready > 0); + } + else { + _PR_MD_MAP_SELECT_ERROR(err); + } + } + + return ready; +} diff --git a/nsprpub/pr/src/md/windows/w32rng.c b/nsprpub/pr/src/md/windows/w32rng.c new file mode 100644 index 0000000000..c07625eccb --- /dev/null +++ b/nsprpub/pr/src/md/windows/w32rng.c @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <windows.h> +#include <time.h> +#include <io.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <primpl.h> + +static BOOL +CurrentClockTickTime(LPDWORD lpdwHigh, LPDWORD lpdwLow) +{ + LARGE_INTEGER liCount; + + if (!QueryPerformanceCounter(&liCount)) { + return FALSE; + } + + *lpdwHigh = liCount.u.HighPart; + *lpdwLow = liCount.u.LowPart; + return TRUE; +} + +extern PRSize _PR_MD_GetRandomNoise( void *buf, PRSize size ) +{ + DWORD dwHigh, dwLow, dwVal; + size_t n = 0; + size_t nBytes; + time_t sTime; + + if (size <= 0) { + return 0; + } + + CurrentClockTickTime(&dwHigh, &dwLow); + + // get the maximally changing bits first + nBytes = sizeof(dwLow) > size ? size : sizeof(dwLow); + memcpy((char *)buf, &dwLow, nBytes); + n += nBytes; + size -= nBytes; + + if (size <= 0) { + return n; + } + + nBytes = sizeof(dwHigh) > size ? size : sizeof(dwHigh); + memcpy(((char *)buf) + n, &dwHigh, nBytes); + n += nBytes; + size -= nBytes; + + if (size <= 0) { + return n; + } + + // get the number of milliseconds that have elapsed since Windows started + dwVal = GetTickCount(); + + nBytes = sizeof(dwVal) > size ? size : sizeof(dwVal); + memcpy(((char *)buf) + n, &dwVal, nBytes); + n += nBytes; + size -= nBytes; + + if (size <= 0) { + return n; + } + + // get the time in seconds since midnight Jan 1, 1970 + time(&sTime); + nBytes = sizeof(sTime) > size ? size : sizeof(sTime); + memcpy(((char *)buf) + n, &sTime, nBytes); + n += nBytes; + + return n; +} + diff --git a/nsprpub/pr/src/md/windows/w32shm.c b/nsprpub/pr/src/md/windows/w32shm.c new file mode 100644 index 0000000000..b0d38b9d70 --- /dev/null +++ b/nsprpub/pr/src/md/windows/w32shm.c @@ -0,0 +1,348 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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; + +/* + * NSPR-to-NT access right mapping table for file-mapping objects. + * + * The OR of these three access masks must equal FILE_MAP_ALL_ACCESS. + * This is because if a file-mapping object with the specified name + * exists, CreateFileMapping requests full access to the existing + * object. + */ +static DWORD filemapAccessTable[] = { + FILE_MAP_ALL_ACCESS & ~FILE_MAP_WRITE, /* read */ + FILE_MAP_ALL_ACCESS & ~FILE_MAP_READ, /* write */ + 0 /* execute */ +}; + +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 ); + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + 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( (PRUint32) (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 ) { + dwHi = (DWORD) (((PRUint64) shm->size >> 32) & 0xffffffff); + dwLo = (DWORD) (shm->size & 0xffffffff); + + if (_PR_NT_MakeSecurityDescriptorACL(mode, filemapAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } +#ifdef WINCE + { + /* + * This is assuming that the name will never be larger than + * MAX_PATH. Should we dynamically allocate? + */ + PRUnichar wideIpcName[MAX_PATH]; + MultiByteToWideChar(CP_ACP, 0, shm->ipcname, -1, + wideIpcName, MAX_PATH); + shm->handle = CreateFileMappingW( + (HANDLE)-1, + lpSA, + flProtect, + dwHi, + dwLo, + wideIpcName); + } +#else + shm->handle = CreateFileMappingA( + (HANDLE)-1, + lpSA, + flProtect, + dwHi, + dwLo, + shm->ipcname); +#endif + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + + if ( NULL == shm->handle ) { + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ( "PR_OpenSharedMemory: CreateFileMapping() failed: %s", + shm->ipcname )); + _PR_MD_MAP_DEFAULT_ERROR( GetLastError()); + 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 { +#ifdef WINCE + PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 ); + shm->handle = NULL; /* OpenFileMapping not supported */ +#else + shm->handle = OpenFileMapping( FILE_MAP_WRITE, TRUE, shm->ipcname ); +#endif + 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, (PRUint32) bufSize, "%d:%" PR_PRIdOSFD ":%ld", + (PRIntn)fm->prot, (PROsfd)fm->md.hFileMap, (PRInt32)fm->md.dwAccess ); + + 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; + PROsfd hFileMap; + PRInt32 dwAccess; + PRFileMap *fm = NULL; + + PR_sscanf( fmstring, "%d:%" PR_SCNdOSFD ":%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; + 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/nsprpub/pr/src/md/windows/w95cv.c b/nsprpub/pr/src/md/windows/w95cv.c new file mode 100644 index 0000000000..5cf2966ffd --- /dev/null +++ b/nsprpub/pr/src/md/windows/w95cv.c @@ -0,0 +1,371 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * w95cv.c -- Windows 95 Machine-Dependent Code for Condition Variables + * + * We implement our own condition variable wait queue. Each thread + * has a semaphore object (thread->md.blocked_sema) to block on while + * waiting on a condition variable. + * + * We use a deferred condition notify algorithm. When PR_NotifyCondVar + * or PR_NotifyAllCondVar is called, the condition notifies are simply + * recorded in the _MDLock structure. We defer the condition notifies + * until right after we unlock the lock. This way the awakened threads + * have a better chance to reaquire the lock. + */ + +#include "primpl.h" + +/* + * AddThreadToCVWaitQueueInternal -- + * + * Add the thread to the end of the condition variable's wait queue. + * The CV's lock must be locked when this function is called. + */ + +static void +AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv) +{ + PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) + || (cv->waitTail == NULL && cv->waitHead == NULL)); + cv->nwait += 1; + thred->md.inCVWaitQueue = PR_TRUE; + thred->md.next = NULL; + thred->md.prev = cv->waitTail; + if (cv->waitHead == NULL) { + cv->waitHead = thred; + } else { + cv->waitTail->md.next = thred; + } + cv->waitTail = thred; +} + +/* + * md_UnlockAndPostNotifies -- + * + * Unlock the lock, and then do the deferred condition notifies. + * If waitThred and waitCV are not NULL, waitThred is added to + * the wait queue of waitCV before the lock is unlocked. + * + * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK, + * the two places where a lock is unlocked. + */ +static void +md_UnlockAndPostNotifies( + _MDLock *lock, + PRThread *waitThred, + _MDCVar *waitCV) +{ + PRIntn index; + _MDNotified post; + _MDNotified *notified, *prev = NULL; + + /* + * Time to actually notify any conditions that were affected + * while the lock was held. Get a copy of the list that's in + * the lock structure and then zero the original. If it's + * linked to other such structures, we own that storage. + */ + post = lock->notified; /* a safe copy; we own the lock */ + +#if defined(DEBUG) + ZeroMemory(&lock->notified, sizeof(_MDNotified)); /* reset */ +#else + lock->notified.length = 0; /* these are really sufficient */ + lock->notified.link = NULL; +#endif + + /* + * Figure out how many threads we need to wake up. + */ + notified = &post; /* this is where we start */ + do { + for (index = 0; index < notified->length; ++index) { + _MDCVar *cv = notified->cv[index].cv; + PRThread *thred; + int i; + + /* Fast special case: no waiting threads */ + if (cv->waitHead == NULL) { + notified->cv[index].notifyHead = NULL; + continue; + } + + /* General case */ + if (-1 == notified->cv[index].times) { + /* broadcast */ + thred = cv->waitHead; + while (thred != NULL) { + thred->md.inCVWaitQueue = PR_FALSE; + thred = thred->md.next; + } + notified->cv[index].notifyHead = cv->waitHead; + cv->waitHead = cv->waitTail = NULL; + cv->nwait = 0; + } else { + thred = cv->waitHead; + i = notified->cv[index].times; + while (thred != NULL && i > 0) { + thred->md.inCVWaitQueue = PR_FALSE; + thred = thred->md.next; + i--; + } + notified->cv[index].notifyHead = cv->waitHead; + cv->waitHead = thred; + if (cv->waitHead == NULL) { + cv->waitTail = NULL; + } else { + if (cv->waitHead->md.prev != NULL) { + cv->waitHead->md.prev->md.next = NULL; + cv->waitHead->md.prev = NULL; + } + } + cv->nwait -= notified->cv[index].times - i; + } + } + notified = notified->link; + } while (NULL != notified); + + if (waitThred) { + AddThreadToCVWaitQueueInternal(waitThred, waitCV); + } + + /* Release the lock before notifying */ + LeaveCriticalSection(&lock->mutex); + + notified = &post; /* this is where we start */ + do { + for (index = 0; index < notified->length; ++index) { + PRThread *thred; + PRThread *next; + + thred = notified->cv[index].notifyHead; + while (thred != NULL) { + BOOL rv; + + next = thred->md.next; + thred->md.prev = thred->md.next = NULL; + + rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL); + PR_ASSERT(rv != 0); + thred = next; + } + } + prev = notified; + notified = notified->link; + if (&post != prev) { + PR_DELETE(prev); + } + } while (NULL != notified); +} + +/* + * Notifies just get posted to the protecting mutex. The + * actual notification is done when the lock is released so that + * MP systems don't contend for a lock that they can't have. + */ +static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock, + PRBool broadcast) +{ + PRIntn index = 0; + _MDNotified *notified = &lock->notified; + + while (1) { + for (index = 0; index < notified->length; ++index) { + if (notified->cv[index].cv == cvar) { + if (broadcast) { + notified->cv[index].times = -1; + } else if (-1 != notified->cv[index].times) { + notified->cv[index].times += 1; + } + return; + } + } + /* if not full, enter new CV in this array */ + if (notified->length < _MD_CV_NOTIFIED_LENGTH) { + break; + } + + /* if there's no link, create an empty array and link it */ + if (NULL == notified->link) { + notified->link = PR_NEWZAP(_MDNotified); + } + + notified = notified->link; + } + + /* A brand new entry in the array */ + notified->cv[index].times = (broadcast) ? -1 : 1; + notified->cv[index].cv = cvar; + notified->length += 1; +} + +/* + * _PR_MD_NEW_CV() -- Creating new condition variable + * ... Solaris uses cond_init() in similar function. + * + * returns: -1 on failure + * 0 when it succeeds. + * + */ +PRInt32 +_PR_MD_NEW_CV(_MDCVar *cv) +{ + cv->magic = _MD_MAGIC_CV; + /* + * The waitHead, waitTail, and nwait fields are zeroed + * when the PRCondVar structure is created. + */ + return 0; +} + +void _PR_MD_FREE_CV(_MDCVar *cv) +{ + cv->magic = (PRUint32)-1; + return; +} + +/* + * _PR_MD_WAIT_CV() -- Wait on condition variable + */ +void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout ) +{ + PRThread *thred = _PR_MD_CURRENT_THREAD(); + DWORD rv; + DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ? + INFINITE : PR_IntervalToMilliseconds(timeout); + + /* + * If we have pending notifies, post them now. + */ + if (0 != lock->notified.length) { + md_UnlockAndPostNotifies(lock, thred, cv); + } else { + AddThreadToCVWaitQueueInternal(thred, cv); + LeaveCriticalSection(&lock->mutex); + } + + /* Wait for notification or timeout; don't really care which */ + rv = WaitForSingleObject(thred->md.blocked_sema, msecs); + + EnterCriticalSection(&(lock->mutex)); + + PR_ASSERT(rv != WAIT_ABANDONED); + PR_ASSERT(rv != WAIT_FAILED); + PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE); + + if (rv == WAIT_TIMEOUT) { + if (thred->md.inCVWaitQueue) { + PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) + || (cv->waitTail == NULL && cv->waitHead == NULL)); + cv->nwait -= 1; + thred->md.inCVWaitQueue = PR_FALSE; + if (cv->waitHead == thred) { + cv->waitHead = thred->md.next; + if (cv->waitHead == NULL) { + cv->waitTail = NULL; + } else { + cv->waitHead->md.prev = NULL; + } + } else { + PR_ASSERT(thred->md.prev != NULL); + thred->md.prev->md.next = thred->md.next; + if (thred->md.next != NULL) { + thred->md.next->md.prev = thred->md.prev; + } else { + PR_ASSERT(cv->waitTail == thred); + cv->waitTail = thred->md.prev; + } + } + thred->md.next = thred->md.prev = NULL; + } else { + /* + * This thread must have been notified, but the + * ReleaseSemaphore call happens after WaitForSingleObject + * times out. Wait on the semaphore again to make it + * non-signaled. We assume this wait won't take long. + */ + rv = WaitForSingleObject(thred->md.blocked_sema, INFINITE); + PR_ASSERT(rv == WAIT_OBJECT_0); + } + } + PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE); + return; +} /* --- end _PR_MD_WAIT_CV() --- */ + +void _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock) +{ + md_PostNotifyToCvar(cv, lock, PR_FALSE); + return; +} + +void _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock) +{ + md_PostNotifyToCvar(cv, lock, PR_TRUE); + return; +} + +typedef BOOL (WINAPI *INITIALIZECRITICALSECTIONEX)( + CRITICAL_SECTION *lpCriticalSection, + DWORD dwSpinCount, + DWORD Flags); + +static INITIALIZECRITICALSECTIONEX sInitializeCriticalSectionEx; + +void _PR_MD_INIT_LOCKS(void) +{ + /* + * Starting with Windows Vista, every CRITICAL_SECTION allocates an extra + * RTL_CRITICAL_SECTION_DEBUG object. Unfortunately, this debug object is + * not reclaimed by DeleteCriticalSection(), causing an apparent memory + * leak. This is a debugging "feature", not a bug. If we are running on + * Vista or later, use InitializeCriticalSectionEx() to allocate + * CRITICAL_SECTIONs without debug objects. + */ + HMODULE hKernel32 = GetModuleHandle("kernel32.dll"); + PR_ASSERT(hKernel32); + PR_ASSERT(!sInitializeCriticalSectionEx); + sInitializeCriticalSectionEx = (INITIALIZECRITICALSECTIONEX) + GetProcAddress(hKernel32, "InitializeCriticalSectionEx"); +} + +/* + * By default, CRITICAL_SECTIONs are initialized with a spin count of 0. + * Joe Duffy's "Concurrent Programming on Windows" book suggests 1500 is + * a "reasonable starting point". On single-processor systems, the spin + * count is ignored and the critical section spin count is set to 0. + */ +#define LOCK_SPIN_COUNT 1500 + +PRStatus _PR_MD_NEW_LOCK(_MDLock *lock) +{ + CRITICAL_SECTION *cs = &lock->mutex; + BOOL ok; + + if (sInitializeCriticalSectionEx) { + ok = sInitializeCriticalSectionEx(cs, LOCK_SPIN_COUNT, + CRITICAL_SECTION_NO_DEBUG_INFO); + } else { + ok = InitializeCriticalSectionAndSpinCount(cs, LOCK_SPIN_COUNT); + } + if (!ok) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + + lock->notified.length = 0; + lock->notified.link = NULL; + return PR_SUCCESS; +} + +void _PR_MD_UNLOCK(_MDLock *lock) +{ + if (0 != lock->notified.length) { + md_UnlockAndPostNotifies(lock, NULL, NULL); + } else { + LeaveCriticalSection(&lock->mutex); + } +} diff --git a/nsprpub/pr/src/md/windows/w95dllmain.c b/nsprpub/pr/src/md/windows/w95dllmain.c new file mode 100644 index 0000000000..c9ab87aec5 --- /dev/null +++ b/nsprpub/pr/src/md/windows/w95dllmain.c @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * The DLL entry point (DllMain) for NSPR. + * + * This is used to detach threads that were automatically attached by + * nspr. + */ + +#include <windows.h> +#include <primpl.h> + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + PRThread *me; + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + if (_pr_initialized) { + me = _MD_GET_ATTACHED_THREAD(); + if ((me != NULL) && (me->flags & _PR_ATTACHED)) { + _PRI_DetachThread(); + } + } + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/nsprpub/pr/src/md/windows/w95io.c b/nsprpub/pr/src/md/windows/w95io.c new file mode 100644 index 0000000000..2ad52e19ba --- /dev/null +++ b/nsprpub/pr/src/md/windows/w95io.c @@ -0,0 +1,1416 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Windows 95 IO module + * + * Assumes synchronous I/O. + * + */ + +#include "primpl.h" +#include <direct.h> +#include <mbstring.h> +#ifdef MOZ_UNICODE +#include <wchar.h> +#endif /* MOZ_UNICODE */ + +struct _MDLock _pr_ioq_lock; + +/* + * NSPR-to-NT access right mapping table for files. + */ +static DWORD fileAccessTable[] = { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE +}; + +/* + * NSPR-to-NT access right mapping table for directories. + */ +static DWORD dirAccessTable[] = { + FILE_GENERIC_READ, + FILE_GENERIC_WRITE|FILE_DELETE_CHILD, + FILE_GENERIC_EXECUTE +}; + +static PRBool IsPrevCharSlash(const char *str, const char *current); + +void +_PR_MD_INIT_IO() +{ + WORD WSAVersion = 0x0101; + WSADATA WSAData; + int err; + + err = WSAStartup( WSAVersion, &WSAData ); + PR_ASSERT(0 == err); + +#ifdef DEBUG + /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */ + { + SYSTEMTIME systime; + union { + PRTime prt; + FILETIME ft; + } filetime; + BOOL rv; + + systime.wYear = 1970; + systime.wMonth = 1; + /* wDayOfWeek is ignored */ + systime.wDay = 1; + systime.wHour = 0; + systime.wMinute = 0; + systime.wSecond = 0; + systime.wMilliseconds = 0; + + rv = SystemTimeToFileTime(&systime, &filetime.ft); + PR_ASSERT(0 != rv); + PR_ASSERT(filetime.prt == _pr_filetime_offset); + } +#endif /* DEBUG */ + + _PR_NT_InitSids(); + + _PR_MD_InitSockets(); +} + +PRStatus +_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + DWORD rv; + + PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? + INFINITE : PR_IntervalToMilliseconds(ticks); + rv = WaitForSingleObject(thread->md.blocked_sema, msecs); + switch(rv) + { + case WAIT_OBJECT_0: + return PR_SUCCESS; + case WAIT_TIMEOUT: + _PR_THREAD_LOCK(thread); + if (thread->state == _PR_IO_WAIT) { + ; + } else { + if (thread->wait.cvar != NULL) { + thread->wait.cvar = NULL; + _PR_THREAD_UNLOCK(thread); + } else { + /* The CVAR was notified just as the timeout + * occurred. This led to us being notified twice. + * call WaitForSingleObject() to clear the semaphore. + */ + _PR_THREAD_UNLOCK(thread); + rv = WaitForSingleObject(thread->md.blocked_sema, 0); + PR_ASSERT(rv == WAIT_OBJECT_0); + } + } + return PR_SUCCESS; + default: + return PR_FAILURE; + } +} +PRStatus +_PR_MD_WAKEUP_WAITER(PRThread *thread) +{ + if ( _PR_IS_NATIVE_THREAD(thread) ) + { + if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) { + return PR_FAILURE; + } + else { + return PR_SUCCESS; + } + } +} + + +/* --- FILE IO ----------------------------------------------------------- */ +/* + * _PR_MD_OPEN() -- Open a file + * + * returns: a fileHandle + * + * The NSPR open flags (osflags) are translated into flags for Win95 + * + * Mode seems to be passed in as a unix style file permissions argument + * as in 0666, in the case of opening the logFile. + * + */ +PROsfd +_PR_MD_OPEN(const char *name, PRIntn osflags, int mode) +{ + HANDLE file; + PRInt32 access = 0; + PRInt32 flags = 0; + PRInt32 flag6 = 0; + + if (osflags & PR_SYNC) { + flag6 = FILE_FLAG_WRITE_THROUGH; + } + + if (osflags & PR_RDONLY || osflags & PR_RDWR) { + access |= GENERIC_READ; + } + if (osflags & PR_WRONLY || osflags & PR_RDWR) { + access |= GENERIC_WRITE; + } + + if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) { + flags = CREATE_NEW; + } + else if (osflags & PR_CREATE_FILE) { + if (osflags & PR_TRUNCATE) { + flags = CREATE_ALWAYS; + } + else { + flags = OPEN_ALWAYS; + } + } else { + if (osflags & PR_TRUNCATE) { + flags = TRUNCATE_EXISTING; + } + else { + flags = OPEN_EXISTING; + } + } + + file = CreateFileA(name, + access, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + flags, + flag6, + NULL); + if (file == INVALID_HANDLE_VALUE) { + _PR_MD_MAP_OPEN_ERROR(GetLastError()); + return -1; + } + + return (PROsfd)file; +} + +PROsfd +_PR_MD_OPEN_FILE(const char *name, PRIntn osflags, int mode) +{ + HANDLE file; + PRInt32 access = 0; + PRInt32 flags = 0; + PRInt32 flag6 = 0; + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + if (osflags & PR_CREATE_FILE) { + if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } + } + + if (osflags & PR_SYNC) { + flag6 = FILE_FLAG_WRITE_THROUGH; + } + + if (osflags & PR_RDONLY || osflags & PR_RDWR) { + access |= GENERIC_READ; + } + if (osflags & PR_WRONLY || osflags & PR_RDWR) { + access |= GENERIC_WRITE; + } + + if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) { + flags = CREATE_NEW; + } + else if (osflags & PR_CREATE_FILE) { + if (osflags & PR_TRUNCATE) { + flags = CREATE_ALWAYS; + } + else { + flags = OPEN_ALWAYS; + } + } else { + if (osflags & PR_TRUNCATE) { + flags = TRUNCATE_EXISTING; + } + else { + flags = OPEN_EXISTING; + } + } + + file = CreateFileA(name, + access, + FILE_SHARE_READ|FILE_SHARE_WRITE, + lpSA, + flags, + flag6, + NULL); + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + if (file == INVALID_HANDLE_VALUE) { + _PR_MD_MAP_OPEN_ERROR(GetLastError()); + return -1; + } + + return (PROsfd)file; +} + +PRInt32 +_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) +{ + PRUint32 bytes; + int rv, err; + + rv = ReadFile((HANDLE)fd->secret->md.osfd, + (LPVOID)buf, + len, + &bytes, + NULL); + + if (rv == 0) + { + err = GetLastError(); + /* ERROR_HANDLE_EOF can only be returned by async io */ + PR_ASSERT(err != ERROR_HANDLE_EOF); + if (err == ERROR_BROKEN_PIPE) { + return 0; + } + else { + _PR_MD_MAP_READ_ERROR(err); + return -1; + } + } + return bytes; +} + +PRInt32 +_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len) +{ + PROsfd f = fd->secret->md.osfd; + PRInt32 bytes; + int rv; + + rv = WriteFile((HANDLE)f, + buf, + len, + &bytes, + NULL ); + + if (rv == 0) + { + _PR_MD_MAP_WRITE_ERROR(GetLastError()); + return -1; + } + return bytes; +} /* --- end _PR_MD_WRITE() --- */ + +PROffset32 +_PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence) +{ + DWORD moveMethod; + PROffset32 rv; + + switch (whence) { + case PR_SEEK_SET: + moveMethod = FILE_BEGIN; + break; + case PR_SEEK_CUR: + moveMethod = FILE_CURRENT; + break; + case PR_SEEK_END: + moveMethod = FILE_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod); + + /* + * If the lpDistanceToMoveHigh argument (third argument) is + * NULL, SetFilePointer returns 0xffffffff on failure. + */ + if (-1 == rv) { + _PR_MD_MAP_LSEEK_ERROR(GetLastError()); + } + return rv; +} + +PROffset64 +_PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence) +{ + DWORD moveMethod; + LARGE_INTEGER li; + DWORD err; + + switch (whence) { + case PR_SEEK_SET: + moveMethod = FILE_BEGIN; + break; + case PR_SEEK_CUR: + moveMethod = FILE_CURRENT; + break; + case PR_SEEK_END: + moveMethod = FILE_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + li.QuadPart = offset; + li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, + li.LowPart, &li.HighPart, moveMethod); + + if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(err); + li.QuadPart = -1; + } + return li.QuadPart; +} + +/* + * This is documented to succeed on read-only files, but Win32's + * FlushFileBuffers functions fails with "access denied" in such a + * case. So we only signal an error if the error is *not* "access + * denied". + */ +PRInt32 +_PR_MD_FSYNC(PRFileDesc *fd) +{ + /* + * From the documentation: + * + * On Windows NT, the function FlushFileBuffers fails if hFile + * is a handle to console output. That is because console + * output is not buffered. The function returns FALSE, and + * GetLastError returns ERROR_INVALID_HANDLE. + * + * On the other hand, on Win95, it returns without error. I cannot + * assume that 0, 1, and 2 are console, because if someone closes + * System.out and then opens a file, they might get file descriptor + * 1. An error on *that* version of 1 should be reported, whereas + * an error on System.out (which was the original 1) should be + * ignored. So I use isatty() to ensure that such an error was due + * to this bogosity, and if it was, I ignore the error. + */ + + BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd); + + if (!ok) { + DWORD err = GetLastError(); + if (err != ERROR_ACCESS_DENIED) { // from winerror.h + _PR_MD_MAP_FSYNC_ERROR(err); + return -1; + } + } + return 0; +} + +PRInt32 +_MD_CloseFile(PROsfd osfd) +{ + PRInt32 rv; + + rv = (CloseHandle((HANDLE)osfd))?0:-1; + if (rv == -1) { + _PR_MD_MAP_CLOSE_ERROR(GetLastError()); + } + return rv; +} + + +/* --- DIR IO ------------------------------------------------------------ */ +#define GetFileFromDIR(d) (d)->d_entry.cFileName +#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) + +static void FlipSlashes(char *cp, size_t len) +{ + while (len-- > 0) { + if (cp[0] == '/') { + cp[0] = PR_DIRECTORY_SEPARATOR; + } + cp = _mbsinc(cp); + } +} /* end FlipSlashes() */ + + +/* +** +** Local implementations of standard Unix RTL functions which are not provided +** by the VC RTL. +** +*/ + +PRInt32 +_PR_MD_CLOSE_DIR(_MDDir *d) +{ + if ( d ) { + if (FindClose(d->d_hdl)) { + d->magic = (PRUint32)-1; + return 0; + } else { + _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); + return -1; + } + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; +} + + +PRStatus +_PR_MD_OPEN_DIR(_MDDir *d, const char *name) +{ + char filename[ MAX_PATH ]; + size_t len; + + len = strlen(name); + /* Need 5 bytes for \*.* and the trailing null byte. */ + if (len + 5 > MAX_PATH) { + PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); + return PR_FAILURE; + } + strcpy(filename, name); + + /* + * If 'name' ends in a slash or backslash, do not append + * another backslash. + */ + if (IsPrevCharSlash(filename, filename + len)) { + len--; + } + strcpy(&filename[len], "\\*.*"); + FlipSlashes( filename, strlen(filename) ); + + d->d_hdl = FindFirstFileA( filename, &(d->d_entry) ); + if ( d->d_hdl == INVALID_HANDLE_VALUE ) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return PR_FAILURE; + } + d->firstEntry = PR_TRUE; + d->magic = _MD_MAGIC_DIR; + return PR_SUCCESS; +} + +char * +_PR_MD_READ_DIR(_MDDir *d, PRIntn flags) +{ + PRInt32 err; + BOOL rv; + char *fileName; + + if ( d ) { + while (1) { + if (d->firstEntry) { + d->firstEntry = PR_FALSE; + rv = 1; + } else { + rv = FindNextFileA(d->d_hdl, &(d->d_entry)); + } + if (rv == 0) { + break; + } + fileName = GetFileFromDIR(d); + if ( (flags & PR_SKIP_DOT) && + (fileName[0] == '.') && (fileName[1] == '\0')) { + continue; + } + if ( (flags & PR_SKIP_DOT_DOT) && + (fileName[0] == '.') && (fileName[1] == '.') && + (fileName[2] == '\0')) { + continue; + } + if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) { + continue; + } + return fileName; + } + err = GetLastError(); + PR_ASSERT(NO_ERROR != err); + _PR_MD_MAP_READDIR_ERROR(err); + return NULL; + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; +} + +PRInt32 +_PR_MD_DELETE(const char *name) +{ + if (DeleteFileA(name)) { + return 0; + } else { + _PR_MD_MAP_DELETE_ERROR(GetLastError()); + return -1; + } +} + +void +_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm) +{ + PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); + CopyMemory(prtm, filetime, sizeof(PRTime)); +#if defined(__MINGW32__) + *prtm = (*prtm - _pr_filetime_offset) / 10LL; +#else + *prtm = (*prtm - _pr_filetime_offset) / 10i64; +#endif + +#ifdef DEBUG + /* Doublecheck our calculation. */ + { + SYSTEMTIME systime; + PRExplodedTime etm; + PRTime cmp; /* for comparison */ + BOOL rv; + + rv = FileTimeToSystemTime(filetime, &systime); + PR_ASSERT(0 != rv); + + /* + * PR_ImplodeTime ignores wday and yday. + */ + etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC; + etm.tm_sec = systime.wSecond; + etm.tm_min = systime.wMinute; + etm.tm_hour = systime.wHour; + etm.tm_mday = systime.wDay; + etm.tm_month = systime.wMonth - 1; + etm.tm_year = systime.wYear; + /* + * It is not well-documented what time zone the FILETIME's + * are in. WIN32_FIND_DATA is documented to be in UTC (GMT). + * But BY_HANDLE_FILE_INFORMATION is unclear about this. + * By our best judgement, we assume that FILETIME is in UTC. + */ + etm.tm_params.tp_gmt_offset = 0; + etm.tm_params.tp_dst_offset = 0; + cmp = PR_ImplodeTime(&etm); + + /* + * SYSTEMTIME is in milliseconds precision, so we convert PRTime's + * microseconds to milliseconds before doing the comparison. + */ + PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC)); + } +#endif /* DEBUG */ +} + +PRInt32 +_PR_MD_STAT(const char *fn, struct stat *info) +{ + PRInt32 rv; + + rv = _stat(fn, (struct _stat *)info); + if (-1 == rv) { + /* + * Check for MSVC runtime library _stat() bug. + * (It's really a bug in FindFirstFile().) + * If a pathname ends in a backslash or slash, + * e.g., c:\temp\ or c:/temp/, _stat() will fail. + * Note: a pathname ending in a slash (e.g., c:/temp/) + * can be handled by _stat() on NT but not on Win95. + * + * We remove the backslash or slash at the end and + * try again. + */ + + size_t len = strlen(fn); + if (len > 0 && len <= _MAX_PATH + && IsPrevCharSlash(fn, fn + len)) { + char newfn[_MAX_PATH + 1]; + + strcpy(newfn, fn); + newfn[len - 1] = '\0'; + rv = _stat(newfn, (struct _stat *)info); + } + } + + if (-1 == rv) { + _PR_MD_MAP_STAT_ERROR(errno); + } + return rv; +} + +#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\') + +static PRBool +IsPrevCharSlash(const char *str, const char *current) +{ + const char *prev; + + if (str >= current) { + return PR_FALSE; + } + prev = _mbsdec(str, current); + return (prev == current - 1) && _PR_IS_SLASH(*prev); +} + +/* + * IsRootDirectory -- + * + * Return PR_TRUE if the pathname 'fn' is a valid root directory, + * else return PR_FALSE. The char buffer pointed to by 'fn' must + * be writable. During the execution of this function, the contents + * of the buffer pointed to by 'fn' may be modified, but on return + * the original contents will be restored. 'buflen' is the size of + * the buffer pointed to by 'fn'. + * + * Root directories come in three formats: + * 1. / or \, meaning the root directory of the current drive. + * 2. C:/ or C:\, where C is a drive letter. + * 3. \\<server name>\<share point name>\ or + * \\<server name>\<share point name>, meaning the root directory + * of a UNC (Universal Naming Convention) name. + */ + +static PRBool +IsRootDirectory(char *fn, size_t buflen) +{ + char *p; + PRBool slashAdded = PR_FALSE; + PRBool rv = PR_FALSE; + + if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') { + return PR_TRUE; + } + + if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) + && fn[3] == '\0') { + rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; + return rv; + } + + /* The UNC root directory */ + + if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) { + /* The 'server' part should have at least one character. */ + p = &fn[2]; + if (*p == '\0' || _PR_IS_SLASH(*p)) { + return PR_FALSE; + } + + /* look for the next slash */ + do { + p = _mbsinc(p); + } while (*p != '\0' && !_PR_IS_SLASH(*p)); + if (*p == '\0') { + return PR_FALSE; + } + + /* The 'share' part should have at least one character. */ + p++; + if (*p == '\0' || _PR_IS_SLASH(*p)) { + return PR_FALSE; + } + + /* look for the final slash */ + do { + p = _mbsinc(p); + } while (*p != '\0' && !_PR_IS_SLASH(*p)); + if (_PR_IS_SLASH(*p) && p[1] != '\0') { + return PR_FALSE; + } + if (*p == '\0') { + /* + * GetDriveType() doesn't work correctly if the + * path is of the form \\server\share, so we add + * a final slash temporarily. + */ + if ((p + 1) < (fn + buflen)) { + *p++ = '\\'; + *p = '\0'; + slashAdded = PR_TRUE; + } else { + return PR_FALSE; /* name too long */ + } + } + rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE; + /* restore the 'fn' buffer */ + if (slashAdded) { + *--p = '\0'; + } + } + return rv; +} + +PRInt32 +_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info) +{ + WIN32_FILE_ATTRIBUTE_DATA findFileData; + BOOL rv; + + if (NULL == fn || '\0' == *fn) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + rv = GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData); + if (!rv) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return -1; + } + + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = PR_FILE_DIRECTORY; + } else { + info->type = PR_FILE_FILE; + } + + info->size = findFileData.nFileSizeHigh; + info->size = (info->size << 32) + findFileData.nFileSizeLow; + + _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); + + if (0 == findFileData.ftCreationTime.dwLowDateTime && + 0 == findFileData.ftCreationTime.dwHighDateTime) { + info->creationTime = info->modifyTime; + } else { + _PR_FileTimeToPRTime(&findFileData.ftCreationTime, + &info->creationTime); + } + + return 0; +} + +PRInt32 +_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info) +{ + PRFileInfo64 info64; + PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64); + if (0 == rv) + { + info->type = info64.type; + info->size = (PRUint32) info64.size; + info->modifyTime = info64.modifyTime; + info->creationTime = info64.creationTime; + } + return rv; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info) +{ + int rv; + + BY_HANDLE_FILE_INFORMATION hinfo; + + rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo); + if (rv == FALSE) { + _PR_MD_MAP_FSTAT_ERROR(GetLastError()); + return -1; + } + + if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = PR_FILE_DIRECTORY; + } + else { + info->type = PR_FILE_FILE; + } + + info->size = hinfo.nFileSizeHigh; + info->size = (info->size << 32) + hinfo.nFileSizeLow; + + _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) ); + _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) ); + + return 0; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info) +{ + PRFileInfo64 info64; + int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64); + if (0 == rv) + { + info->type = info64.type; + info->modifyTime = info64.modifyTime; + info->creationTime = info64.creationTime; + LL_L2I(info->size, info64.size); + } + return rv; +} + +PRStatus +_PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable) +{ + BOOL rv; + + /* + * The SetHandleInformation function fails with the + * ERROR_CALL_NOT_IMPLEMENTED error on Win95. + */ + rv = SetHandleInformation( + (HANDLE)fd->secret->md.osfd, + HANDLE_FLAG_INHERIT, + inheritable ? HANDLE_FLAG_INHERIT : 0); + if (0 == rv) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +void +_PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported) +{ + if (imported) { + fd->secret->inheritable = _PR_TRI_UNKNOWN; + } else { + fd->secret->inheritable = _PR_TRI_FALSE; + } +} + +void +_PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd) +{ + DWORD flags; + + PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable); + if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) { + if (flags & HANDLE_FLAG_INHERIT) { + fd->secret->inheritable = _PR_TRI_TRUE; + } else { + fd->secret->inheritable = _PR_TRI_FALSE; + } + } +} + +PRInt32 +_PR_MD_RENAME(const char *from, const char *to) +{ + /* Does this work with dot-relative pathnames? */ + if (MoveFileA(from, to)) { + return 0; + } else { + _PR_MD_MAP_RENAME_ERROR(GetLastError()); + return -1; + } +} + +PRInt32 +_PR_MD_ACCESS(const char *name, PRAccessHow how) +{ + PRInt32 rv; + switch (how) { + case PR_ACCESS_WRITE_OK: + rv = _access(name, 02); + break; + case PR_ACCESS_READ_OK: + rv = _access(name, 04); + break; + case PR_ACCESS_EXISTS: + return _access(name, 00); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + if (rv < 0) { + _PR_MD_MAP_ACCESS_ERROR(errno); + } + return rv; +} + +PRInt32 +_PR_MD_MKDIR(const char *name, PRIntn mode) +{ + /* XXXMB - how to translate the "mode"??? */ + if (CreateDirectoryA(name, NULL)) { + return 0; + } else { + _PR_MD_MAP_MKDIR_ERROR(GetLastError()); + return -1; + } +} + +PRInt32 +_PR_MD_MAKE_DIR(const char *name, PRIntn mode) +{ + BOOL rv; + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } + rv = CreateDirectoryA(name, lpSA); + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + if (rv) { + return 0; + } else { + _PR_MD_MAP_MKDIR_ERROR(GetLastError()); + return -1; + } +} + +PRInt32 +_PR_MD_RMDIR(const char *name) +{ + if (RemoveDirectoryA(name)) { + return 0; + } else { + _PR_MD_MAP_RMDIR_ERROR(GetLastError()); + return -1; + } +} + +PRStatus +_PR_MD_LOCKFILE(PROsfd f) +{ + PRStatus rc = PR_SUCCESS; + DWORD rv; + + rv = LockFile( (HANDLE)f, + 0l, 0l, + 0x0l, 0xffffffffl ); + if ( rv == 0 ) { + DWORD err = GetLastError(); + _PR_MD_MAP_DEFAULT_ERROR(err); + PR_LOG( _pr_io_lm, PR_LOG_ERROR, + ("_PR_MD_LOCKFILE() failed. Error: %d", err )); + rc = PR_FAILURE; + } + + return rc; +} /* end _PR_MD_LOCKFILE() */ + +PRStatus +_PR_MD_TLOCKFILE(PROsfd f) +{ + PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 ); + return PR_FAILURE; +} /* end _PR_MD_TLOCKFILE() */ + + +PRStatus +_PR_MD_UNLOCKFILE(PROsfd f) +{ + PRInt32 rv; + + rv = UnlockFile( (HANDLE) f, + 0l, 0l, + 0x0l, 0xffffffffl ); + + if ( rv ) + { + return PR_SUCCESS; + } + else + { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } +} /* end _PR_MD_UNLOCKFILE() */ + +PRInt32 +_PR_MD_PIPEAVAILABLE(PRFileDesc *fd) +{ + if (NULL == fd) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + } + else { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + } + return -1; +} + +#ifdef MOZ_UNICODE + +typedef HANDLE (WINAPI *CreateFileWFn) (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); +static CreateFileWFn createFileW = CreateFileW; +typedef HANDLE (WINAPI *FindFirstFileWFn) (LPCWSTR, LPWIN32_FIND_DATAW); +static FindFirstFileWFn findFirstFileW = FindFirstFileW; +typedef BOOL (WINAPI *FindNextFileWFn) (HANDLE, LPWIN32_FIND_DATAW); +static FindNextFileWFn findNextFileW = FindNextFileW; +typedef DWORD (WINAPI *GetFullPathNameWFn) (LPCWSTR, DWORD, LPWSTR, LPWSTR *); +static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW; +typedef UINT (WINAPI *GetDriveTypeWFn) (LPCWSTR); +static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW; + +#endif /* MOZ_UNICODE */ + +#ifdef MOZ_UNICODE + +/* ================ UTF16 Interfaces ================================ */ +static void FlipSlashesW(PRUnichar *cp, size_t len) +{ + while (len-- > 0) { + if (cp[0] == L'/') { + cp[0] = L'\\'; + } + cp++; + } +} /* end FlipSlashesW() */ + +PROsfd +_PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, int mode) +{ + HANDLE file; + PRInt32 access = 0; + PRInt32 flags = 0; + PRInt32 flag6 = 0; + SECURITY_ATTRIBUTES sa; + LPSECURITY_ATTRIBUTES lpSA = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + + if (osflags & PR_CREATE_FILE) { + if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, + &pSD, &pACL) == PR_SUCCESS) { + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + lpSA = &sa; + } + } + + if (osflags & PR_SYNC) { + flag6 = FILE_FLAG_WRITE_THROUGH; + } + + if (osflags & PR_RDONLY || osflags & PR_RDWR) { + access |= GENERIC_READ; + } + if (osflags & PR_WRONLY || osflags & PR_RDWR) { + access |= GENERIC_WRITE; + } + + if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL ) { + flags = CREATE_NEW; + } + else if (osflags & PR_CREATE_FILE) { + if (osflags & PR_TRUNCATE) { + flags = CREATE_ALWAYS; + } + else { + flags = OPEN_ALWAYS; + } + } else { + if (osflags & PR_TRUNCATE) { + flags = TRUNCATE_EXISTING; + } + else { + flags = OPEN_EXISTING; + } + } + + file = createFileW(name, + access, + FILE_SHARE_READ|FILE_SHARE_WRITE, + lpSA, + flags, + flag6, + NULL); + if (lpSA != NULL) { + _PR_NT_FreeSecurityDescriptorACL(pSD, pACL); + } + if (file == INVALID_HANDLE_VALUE) { + _PR_MD_MAP_OPEN_ERROR(GetLastError()); + return -1; + } + + return (PROsfd)file; +} + +PRStatus +_PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *d, const PRUnichar *name) +{ + PRUnichar filename[ MAX_PATH ]; + int len; + + len = wcslen(name); + /* Need 5 bytes for \*.* and the trailing null byte. */ + if (len + 5 > MAX_PATH) { + PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); + return PR_FAILURE; + } + wcscpy(filename, name); + + /* + * If 'name' ends in a slash or backslash, do not append + * another backslash. + */ + if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') { + len--; + } + wcscpy(&filename[len], L"\\*.*"); + FlipSlashesW( filename, wcslen(filename) ); + + d->d_hdl = findFirstFileW( filename, &(d->d_entry) ); + if ( d->d_hdl == INVALID_HANDLE_VALUE ) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return PR_FAILURE; + } + d->firstEntry = PR_TRUE; + d->magic = _MD_MAGIC_DIR; + return PR_SUCCESS; +} + +PRUnichar * +_PR_MD_READ_DIR_UTF16(_MDDirUTF16 *d, PRIntn flags) +{ + PRInt32 err; + BOOL rv; + PRUnichar *fileName; + + if ( d ) { + while (1) { + if (d->firstEntry) { + d->firstEntry = PR_FALSE; + rv = 1; + } else { + rv = findNextFileW(d->d_hdl, &(d->d_entry)); + } + if (rv == 0) { + break; + } + fileName = GetFileFromDIR(d); + if ( (flags & PR_SKIP_DOT) && + (fileName[0] == L'.') && (fileName[1] == L'\0')) { + continue; + } + if ( (flags & PR_SKIP_DOT_DOT) && + (fileName[0] == L'.') && (fileName[1] == L'.') && + (fileName[2] == L'\0')) { + continue; + } + if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) { + continue; + } + return fileName; + } + err = GetLastError(); + PR_ASSERT(NO_ERROR != err); + _PR_MD_MAP_READDIR_ERROR(err); + return NULL; + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; +} + +PRInt32 +_PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *d) +{ + if ( d ) { + if (FindClose(d->d_hdl)) { + d->magic = (PRUint32)-1; + return 0; + } else { + _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError()); + return -1; + } + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; +} + +#define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\') + +/* + * IsRootDirectoryW -- + * + * Return PR_TRUE if the pathname 'fn' is a valid root directory, + * else return PR_FALSE. The PRUnichar buffer pointed to by 'fn' must + * be writable. During the execution of this function, the contents + * of the buffer pointed to by 'fn' may be modified, but on return + * the original contents will be restored. 'buflen' is the size of + * the buffer pointed to by 'fn', in PRUnichars. + * + * Root directories come in three formats: + * 1. / or \, meaning the root directory of the current drive. + * 2. C:/ or C:\, where C is a drive letter. + * 3. \\<server name>\<share point name>\ or + * \\<server name>\<share point name>, meaning the root directory + * of a UNC (Universal Naming Convention) name. + */ + +static PRBool +IsRootDirectoryW(PRUnichar *fn, size_t buflen) +{ + PRUnichar *p; + PRBool slashAdded = PR_FALSE; + PRBool rv = PR_FALSE; + + if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') { + return PR_TRUE; + } + + if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2]) + && fn[3] == L'\0') { + rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; + return rv; + } + + /* The UNC root directory */ + + if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) { + /* The 'server' part should have at least one character. */ + p = &fn[2]; + if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { + return PR_FALSE; + } + + /* look for the next slash */ + do { + p++; + } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); + if (*p == L'\0') { + return PR_FALSE; + } + + /* The 'share' part should have at least one character. */ + p++; + if (*p == L'\0' || _PR_IS_W_SLASH(*p)) { + return PR_FALSE; + } + + /* look for the final slash */ + do { + p++; + } while (*p != L'\0' && !_PR_IS_W_SLASH(*p)); + if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') { + return PR_FALSE; + } + if (*p == L'\0') { + /* + * GetDriveType() doesn't work correctly if the + * path is of the form \\server\share, so we add + * a final slash temporarily. + */ + if ((p + 1) < (fn + buflen)) { + *p++ = L'\\'; + *p = L'\0'; + slashAdded = PR_TRUE; + } else { + return PR_FALSE; /* name too long */ + } + } + rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE; + /* restore the 'fn' buffer */ + if (slashAdded) { + *--p = L'\0'; + } + } + return rv; +} + +PRInt32 +_PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info) +{ + HANDLE hFindFile; + WIN32_FIND_DATAW findFileData; + PRUnichar pathbuf[MAX_PATH + 1]; + + if (NULL == fn || L'\0' == *fn) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } + + /* + * FindFirstFile() expands wildcard characters. So + * we make sure the pathname contains no wildcard. + */ + if (NULL != wcspbrk(fn, L"?*")) { + PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0); + return -1; + } + + hFindFile = findFirstFileW(fn, &findFileData); + if (INVALID_HANDLE_VALUE == hFindFile) { + DWORD len; + PRUnichar *filePart; + + /* + * FindFirstFile() does not work correctly on root directories. + * It also doesn't work correctly on a pathname that ends in a + * slash. So we first check to see if the pathname specifies a + * root directory. If not, and if the pathname ends in a slash, + * we remove the final slash and try again. + */ + + /* + * If the pathname does not contain ., \, and /, it cannot be + * a root directory or a pathname that ends in a slash. + */ + if (NULL == wcspbrk(fn, L".\\/")) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return -1; + } + len = getFullPathNameW(fn, sizeof(pathbuf)/sizeof(pathbuf[0]), pathbuf, + &filePart); + if (0 == len) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return -1; + } + if (len > sizeof(pathbuf)/sizeof(pathbuf[0])) { + PR_SetError(PR_NAME_TOO_LONG_ERROR, 0); + return -1; + } + if (IsRootDirectoryW(pathbuf, sizeof(pathbuf)/sizeof(pathbuf[0]))) { + info->type = PR_FILE_DIRECTORY; + info->size = 0; + /* + * These timestamps don't make sense for root directories. + */ + info->modifyTime = 0; + info->creationTime = 0; + return 0; + } + if (!_PR_IS_W_SLASH(pathbuf[len - 1])) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return -1; + } else { + pathbuf[len - 1] = L'\0'; + hFindFile = findFirstFileW(pathbuf, &findFileData); + if (INVALID_HANDLE_VALUE == hFindFile) { + _PR_MD_MAP_OPENDIR_ERROR(GetLastError()); + return -1; + } + } + } + + FindClose(hFindFile); + + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + info->type = PR_FILE_DIRECTORY; + } else { + info->type = PR_FILE_FILE; + } + + info->size = findFileData.nFileSizeHigh; + info->size = (info->size << 32) + findFileData.nFileSizeLow; + + _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime); + + if (0 == findFileData.ftCreationTime.dwLowDateTime && + 0 == findFileData.ftCreationTime.dwHighDateTime) { + info->creationTime = info->modifyTime; + } else { + _PR_FileTimeToPRTime(&findFileData.ftCreationTime, + &info->creationTime); + } + + return 0; +} +/* ================ end of UTF16 Interfaces ================================ */ +#endif /* MOZ_UNICODE */ diff --git a/nsprpub/pr/src/md/windows/w95sock.c b/nsprpub/pr/src/md/windows/w95sock.c new file mode 100644 index 0000000000..abe462fe02 --- /dev/null +++ b/nsprpub/pr/src/md/windows/w95sock.c @@ -0,0 +1,851 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Win95 Sockets module + * + */ + +#if defined(_WIN64) +#include <winsock2.h> +#endif +#include "primpl.h" + +#define READ_FD 1 +#define WRITE_FD 2 +#define CONNECT_FD 3 + +static PRInt32 socket_io_wait( + PROsfd osfd, + PRInt32 fd_type, + PRIntervalTime timeout); + + +/* --- SOCKET IO --------------------------------------------------------- */ + +static PRBool socketFixInet6RcvBuf = PR_FALSE; + +void _PR_MD_InitSockets(void) +{ + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + GetVersionEx(&osvi); + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + { + /* if Windows XP (32-bit) */ + socketFixInet6RcvBuf = PR_TRUE; + } +} + +void _PR_MD_CleanupSockets(void) +{ + socketFixInet6RcvBuf = PR_FALSE; +} + +PROsfd +_PR_MD_SOCKET(int af, int type, int flags) +{ + SOCKET sock; + u_long one = 1; + + sock = socket(af, type, flags); + + if (sock == INVALID_SOCKET ) + { + _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError()); + return (PROsfd)sock; + } + + /* + ** Make the socket Non-Blocking + */ + if (ioctlsocket( sock, FIONBIO, &one) != 0) + { + PR_SetError(PR_UNKNOWN_ERROR, WSAGetLastError()); + closesocket(sock); + return -1; + } + + if (af == AF_INET6 && socketFixInet6RcvBuf) + { + int bufsize; + int len = sizeof(bufsize); + int rv; + + /* Windows XP 32-bit returns an error on getpeername() for AF_INET6 + * sockets if the receive buffer size is greater than 65535 before + * the connection is initiated. The default receive buffer size may + * be 128000 so fix it here to always be <= 65535. See bug 513659 + * and IBM DB2 support technote "Receive/Send IPv6 Socket Size + * Problem in Windows XP SP2 & SP3". + */ + rv = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, &len); + if (rv == 0 && bufsize > 65535) + { + bufsize = 65535; + setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, len); + } + } + + return (PROsfd)sock; +} + +/* +** _MD_CloseSocket() -- Close a socket +** +*/ +PRInt32 +_MD_CloseSocket(PROsfd osfd) +{ + PRInt32 rv; + + rv = closesocket((SOCKET) osfd ); + if (rv < 0) { + _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError()); + } + + return rv; +} + +PRInt32 +_MD_SocketAvailable(PRFileDesc *fd) +{ + PRInt32 result; + + if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError()); + return -1; + } + return result; +} + +PROsfd _MD_Accept( + PRFileDesc *fd, + PRNetAddr *raddr, + PRUint32 *rlen, + PRIntervalTime timeout ) +{ + PROsfd osfd = fd->secret->md.osfd; + SOCKET sock; + PRInt32 rv, err; + + while ((sock = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1) + { + err = WSAGetLastError(); + if ((err == WSAEWOULDBLOCK) && (!fd->secret->nonblocking)) + { + if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) + { + break; + } + } + else + { + _PR_MD_MAP_ACCEPT_ERROR(err); + break; + } + } + return(sock); +} /* end _MD_accept() */ + +PRInt32 +_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, + PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv; + int err; + + if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) + { + err = WSAGetLastError(); + if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK)) + { + rv = socket_io_wait(osfd, CONNECT_FD, timeout); + if ( rv < 0 ) + { + return(-1); + } + else + { + PR_ASSERT(rv > 0); + /* it's connected */ + return(0); + } + } + _PR_MD_MAP_CONNECT_ERROR(err); + } + return rv; +} + +PRInt32 +_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) +{ + PRInt32 rv; + + rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen); + + if (rv == SOCKET_ERROR) { + _PR_MD_MAP_BIND_ERROR(WSAGetLastError()); + return -1; + } + + return 0; +} + +PRInt32 +_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 rv; + + rv = listen(fd->secret->md.osfd, backlog); + + if (rv == SOCKET_ERROR) { + _PR_MD_MAP_DEFAULT_ERROR(WSAGetLastError()); + return -1; + } + + return 0; +} + +PRInt32 +_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + int osflags; + + if (0 == flags) { + osflags = 0; + } else { + PR_ASSERT(PR_MSG_PEEK == flags); + osflags = MSG_PEEK; + } + while ((rv = recv( osfd, buf, amount, osflags)) == -1) + { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) + { + rv = socket_io_wait(osfd, READ_FD, timeout); + if ( rv < 0 ) + { + return -1; + } + } + else + { + _PR_MD_MAP_RECV_ERROR(err); + break; + } + } /* end while() */ + return(rv); +} + +PRInt32 +_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRInt32 bytesSent = 0; + + while(bytesSent < amount ) + { + while ((rv = send( osfd, buf, amount, 0 )) == -1) + { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) + { + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if ( rv < 0 ) + { + return -1; + } + } + else + { + _PR_MD_MAP_SEND_ERROR(err); + return -1; + } + } + bytesSent += rv; + if (fd->secret->nonblocking) + { + break; + } + if (bytesSent < amount) + { + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if ( rv < 0 ) + { + return -1; + } + } + } + return bytesSent; +} + +PRInt32 +_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRInt32 bytesSent = 0; + + do { + while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr, + addrlen)) == -1) + { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) + { + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if ( rv < 0 ) + { + return -1; + } + } + else + { + _PR_MD_MAP_SENDTO_ERROR(err); + return -1; + } + } + bytesSent += rv; + if (fd->secret->nonblocking) + { + break; + } + if (bytesSent < amount) + { + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if (rv < 0) + { + return -1; + } + } + } while(bytesSent < amount); + return bytesSent; +} + +#if defined(_WIN64) + +static PRCallOnceType _pr_has_connectex_once; +typedef BOOL (PASCAL FAR * _pr_win_connectex_ptr)(_In_ SOCKET s, _In_reads_bytes_(namelen) const struct sockaddr FAR *name, _In_ int namelen, _In_reads_bytes_opt_(dwSendDataLength) PVOID lpSendBuffer, _In_ DWORD dwSendDataLength, _Out_ LPDWORD lpdwBytesSent, _Inout_ LPOVERLAPPED lpOverlapped); + +#ifndef WSAID_CONNECTEX +#define WSAID_CONNECTEX \ + {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} +#endif +#ifndef SIO_GET_EXTENSION_FUNCTION_POINTER +#define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006 +#endif +#ifndef TCP_FASTOPEN +#define TCP_FASTOPEN 15 +#endif + +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + +static _pr_win_connectex_ptr _pr_win_connectex = NULL; + +static PRStatus PR_CALLBACK _pr_set_connectex(void) +{ + _pr_win_connectex = NULL; + SOCKET sock; + PRInt32 dwBytes; + int rc; + + /* Dummy socket needed for WSAIoctl */ + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + return PR_SUCCESS; + } + + GUID guid = WSAID_CONNECTEX; + rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), + &_pr_win_connectex, sizeof(_pr_win_connectex), + &dwBytes, NULL, NULL); + if (rc != 0) { + _pr_win_connectex = NULL; + return PR_SUCCESS; + } + + rc = closesocket(sock); + return PR_SUCCESS; +} + +PRInt32 +_PR_MD_TCPSENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + if (!_fd_waiting_for_overlapped_done_lock) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + + if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + + if (_pr_win_connectex == NULL) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } + + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRInt32 bytesSent = 0; + DWORD rvSent; + + BOOL option = 1; + rv = setsockopt((SOCKET)osfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&option, sizeof(option)); + if (rv != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO error set opt TCP_FASTOPEN failed %d\n", err)); + if (err == WSAENOPROTOOPT) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + } else { + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + } + return -1; + } + + /* ConnectEx requires the socket to be initially bound. We will use INADDR_ANY. */ + PRNetAddr bindAddr; + memset(&bindAddr, 0, sizeof(bindAddr)); + bindAddr.raw.family = addr->raw.family; + + rv = bind((SOCKET)osfd, (const struct sockaddr *)&(bindAddr.inet), PR_NETADDR_SIZE(&bindAddr)); + if (rv != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO error bind failed %d\n", err)); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + return -1; + } + + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO calling _pr_win_connectex %d %p\n", amount, (char*)buf)); + + rvSent = 0; + memset(&fd->secret->ol, 0, sizeof(fd->secret->ol)); + /* ConnectEx return TRUE on a success and FALSE on an error. */ + if (_pr_win_connectex( (SOCKET)osfd, (struct sockaddr *) addr, + addrlen, buf, amount, + &rvSent, &fd->secret->ol) == TRUE) { + /* When ConnectEx is used, all previously set socket options and + * property are not enabled and to enable them + * SO_UPDATE_CONNECT_CONTEXT option need to be set. */ + rv = setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0); + if (rv != 0) { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err)); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + return -1; + } + /* We imitate Linux here. SendTo will return number of bytes send but + * it can not return connection success at the same time, so we return + * number of bytes send and "connection success" will be return on the + * connectcontinue. */ + fd->secret->alreadyConnected = PR_TRUE; + return rvSent; + } else { + err = WSAGetLastError(); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("_PR_MD_TCPSENDTO error _pr_win_connectex failed %d\n", err)); + if (err != ERROR_IO_PENDING) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } else if (fd->secret->nonblocking) { + /* Remember that overlapped structure is set. We will need to get + * the final result of ConnectEx call. */ + fd->secret->overlappedActive = PR_TRUE; + + /* ConnectEx will copy supplied data to a internal buffer and send + * them during Fast Open or after connect. Therefore we can assumed + * this data already send. */ + if (amount > 0) { + return amount; + } + + _PR_MD_MAP_CONNECT_ERROR(WSAEWOULDBLOCK); + return -1; + } + // err is ERROR_IO_PENDING and socket is blocking, so query + // GetOverlappedResult. + err = ERROR_IO_INCOMPLETE; + while (err == ERROR_IO_INCOMPLETE) { + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if ( rv < 0 ) { + return -1; + } + rv = GetOverlappedResult((HANDLE)osfd, &fd->secret->ol, &rvSent, FALSE); + if ( rv == TRUE ) { + return rvSent; + } else { + err = WSAGetLastError(); + if (err != ERROR_IO_INCOMPLETE) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } + } + } + } + return -1; +} +#endif + +PRInt32 +_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, + PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) +{ + PROsfd osfd = fd->secret->md.osfd; + PRInt32 rv, err; + + while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr, + addrlen)) == -1) + { + if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) + && (!fd->secret->nonblocking)) + { + rv = socket_io_wait(osfd, READ_FD, timeout); + if ( rv < 0) + { + return -1; + } + } + else + { + _PR_MD_MAP_RECVFROM_ERROR(err); + break; + } + } + return(rv); +} + +PRInt32 +_PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) +{ + int index; + int sent = 0; + int rv; + + for (index=0; index < iov_size; index++) + { + rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout); + if (rv > 0) { + sent += rv; + } + if ( rv != iov[index].iov_len ) + { + if (rv < 0) + { + if (fd->secret->nonblocking + && (PR_GetError() == PR_WOULD_BLOCK_ERROR) + && (sent > 0)) + { + return sent; + } + else + { + return -1; + } + } + /* Only a nonblocking socket can have partial sends */ + PR_ASSERT(fd->secret->nonblocking); + return sent; + } + } + return sent; +} + +PRInt32 +_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how) +{ + PRInt32 rv; + + rv = shutdown(fd->secret->md.osfd, how); + if (rv < 0) { + _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError()); + } + return rv; +} + +PRStatus +_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) +{ + PRInt32 rv; + + rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len); + if (rv==0) { + return PR_SUCCESS; + } else { + _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) +{ + PRInt32 rv; + + rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len); + if (rv==0) { + return PR_SUCCESS; + } else { + _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) +{ + PRInt32 rv; + + rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen); + if (rv==0) { + return PR_SUCCESS; + } else { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) +{ + PRInt32 rv; + + rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen); + if (rv==0) { + return PR_SUCCESS; + } else { + _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } +} + +void +_MD_MakeNonblock(PRFileDesc *f) +{ + return; /* do nothing */ +} + + + +/* + * socket_io_wait -- + * + * Wait for socket i/o, periodically checking for interrupt. + * + * This function returns 1 on success. On failure, it returns + * -1 and sets the error codes. It never returns 0. + */ +#define _PR_INTERRUPT_CHECK_INTERVAL_SECS 5 + +static PRInt32 socket_io_wait( + PROsfd osfd, + PRInt32 fd_type, + PRIntervalTime timeout) +{ + PRInt32 rv = -1; + struct timeval tv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntervalTime elapsed, remaining; + PRBool wait_for_remaining; + fd_set rd_wr, ex; + int err, len; + + switch (timeout) { + case PR_INTERVAL_NO_WAIT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_INTERVAL_NO_TIMEOUT: + /* + * This is a special case of the 'default' case below. + * Please see the comments there. + */ + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + FD_ZERO(&rd_wr); + FD_ZERO(&ex); + do { + FD_SET(osfd, &rd_wr); + FD_SET(osfd, &ex); + switch( fd_type ) + { + case READ_FD: + rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv); + break; + case WRITE_FD: + rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv); + break; + case CONNECT_FD: + rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv); + break; + default: + PR_ASSERT(0); + break; + } /* end switch() */ + if (rv == -1 ) + { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + break; + } + if ( rv > 0 && fd_type == CONNECT_FD ) + { + /* + * Call Sleep(0) to work around a Winsock timing bug. + */ + Sleep(0); + if (FD_ISSET((SOCKET)osfd, &ex)) + { + len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, + (char *) &err, &len) == SOCKET_ERROR) + { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return -1; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } + else { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } + return -1; + } + if (FD_ISSET((SOCKET)osfd, &rd_wr)) + { + /* it's connected */ + return 1; + } + PR_ASSERT(0); + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + } while (rv == 0); + break; + default: + remaining = timeout; + FD_ZERO(&rd_wr); + FD_ZERO(&ex); + do { + /* + * We block in _MD_SELECT for at most + * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, + * so that there is an upper limit on the delay + * before the interrupt bit is checked. + */ + wait_for_remaining = PR_TRUE; + tv.tv_sec = PR_IntervalToSeconds(remaining); + if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) { + wait_for_remaining = PR_FALSE; + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + } else { + tv.tv_usec = PR_IntervalToMicroseconds( + remaining - + PR_SecondsToInterval(tv.tv_sec)); + } + FD_SET(osfd, &rd_wr); + FD_SET(osfd, &ex); + switch( fd_type ) + { + case READ_FD: + rv = _MD_SELECT(0, &rd_wr, NULL, NULL, &tv); + break; + case WRITE_FD: + rv = _MD_SELECT(0, NULL, &rd_wr, NULL, &tv); + break; + case CONNECT_FD: + rv = _MD_SELECT(0, NULL, &rd_wr, &ex, &tv); + break; + default: + PR_ASSERT(0); + break; + } /* end switch() */ + if (rv == -1) + { + _PR_MD_MAP_SELECT_ERROR(WSAGetLastError()); + break; + } + if ( rv > 0 && fd_type == CONNECT_FD ) + { + /* + * Call Sleep(0) to work around a Winsock timing bug. + */ + Sleep(0); + if (FD_ISSET((SOCKET)osfd, &ex)) + { + len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, + (char *) &err, &len) == SOCKET_ERROR) + { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return -1; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } + else { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } + return -1; + } + if (FD_ISSET((SOCKET)osfd, &rd_wr)) + { + /* it's connected */ + return 1; + } + PR_ASSERT(0); + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + /* + * We loop again if _MD_SELECT timed out and the + * timeout deadline has not passed yet. + */ + if (rv == 0 ) + { + if (wait_for_remaining) { + elapsed = remaining; + } else { + elapsed = PR_SecondsToInterval(tv.tv_sec) + + PR_MicrosecondsToInterval(tv.tv_usec); + } + if (elapsed >= remaining) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } else { + remaining = remaining - elapsed; + } + } + } while (rv == 0 ); + break; + } + return(rv); +} /* end socket_io_wait() */ diff --git a/nsprpub/pr/src/md/windows/w95thred.c b/nsprpub/pr/src/md/windows/w95thred.c new file mode 100644 index 0000000000..fb9c457d73 --- /dev/null +++ b/nsprpub/pr/src/md/windows/w95thred.c @@ -0,0 +1,381 @@ + +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include <process.h> /* for _beginthreadex() */ + +#if defined(_MSC_VER) && _MSC_VER <= 1200 +/* + * VC++ 6.0 doesn't have DWORD_PTR. + */ + +typedef DWORD DWORD_PTR; +#endif /* _MSC_VER <= 1200 */ + +/* --- globals ------------------------------------------------ */ +#ifdef _PR_USE_STATIC_TLS +__declspec(thread) struct PRThread *_pr_thread_last_run; +__declspec(thread) struct PRThread *_pr_currentThread; +__declspec(thread) struct _PRCPU *_pr_currentCPU; +#else +DWORD _pr_currentThreadIndex; +DWORD _pr_lastThreadIndex; +DWORD _pr_currentCPUIndex; +#endif +int _pr_intsOff = 0; +_PRInterruptTable _pr_interruptTable[] = { { 0 } }; + +typedef HRESULT (WINAPI *SETTHREADDESCRIPTION)(HANDLE, PCWSTR); +static SETTHREADDESCRIPTION sSetThreadDescription = NULL; + +void +_PR_MD_EARLY_INIT() +{ + HMODULE hModule; + +#ifndef _PR_USE_STATIC_TLS + _pr_currentThreadIndex = TlsAlloc(); + _pr_lastThreadIndex = TlsAlloc(); + _pr_currentCPUIndex = TlsAlloc(); +#endif + +#if defined(_WIN64) && defined(WIN95) + _fd_waiting_for_overlapped_done_lock = PR_NewLock(); +#endif + + // SetThreadDescription is Windows 10 build 1607+ + hModule = GetModuleHandleW(L"kernel32.dll"); + if (hModule) { + sSetThreadDescription = + (SETTHREADDESCRIPTION) GetProcAddress( + hModule, + "SetThreadDescription"); + } +} + +void _PR_MD_CLEANUP_BEFORE_EXIT(void) +{ + _PR_NT_FreeSids(); + + _PR_MD_CleanupSockets(); + + WSACleanup(); + +#ifndef _PR_USE_STATIC_TLS + TlsFree(_pr_currentThreadIndex); + TlsFree(_pr_lastThreadIndex); + TlsFree(_pr_currentCPUIndex); +#endif + +#if defined(_WIN64) && defined(WIN95) + // For each iteration check if TFO overlapped IOs are down. + if (_fd_waiting_for_overlapped_done_lock) { + PRIntervalTime delay = PR_MillisecondsToInterval(1000); + PRFileDescList *cur; + do { + CheckOverlappedPendingSocketsAreDone(); + + PR_Lock(_fd_waiting_for_overlapped_done_lock); + cur = _fd_waiting_for_overlapped_done; + PR_Unlock(_fd_waiting_for_overlapped_done_lock); +#if defined(DO_NOT_WAIT_FOR_CONNECT_OVERLAPPED_OPERATIONS) + cur = NULL; +#endif + if (cur) { + PR_Sleep(delay); // wait another 1s. + } + } while (cur); + + PR_DestroyLock(_fd_waiting_for_overlapped_done_lock); + } +#endif +} + +PRStatus +_PR_MD_INIT_THREAD(PRThread *thread) +{ + if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { + /* + ** Warning: + ** -------- + ** NSPR requires a real handle to every thread. + ** GetCurrentThread() returns a pseudo-handle which + ** is not suitable for some thread operations (e.g., + ** suspending). Therefore, get a real handle from + ** the pseudo handle via DuplicateHandle(...) + */ + BOOL ok = DuplicateHandle( + GetCurrentProcess(), /* Process of source handle */ + GetCurrentThread(), /* Pseudo Handle to dup */ + GetCurrentProcess(), /* Process of handle */ + &(thread->md.handle), /* resulting handle */ + 0L, /* access flags */ + FALSE, /* Inheritable */ + DUPLICATE_SAME_ACCESS); /* Options */ + if (!ok) { + return PR_FAILURE; + } + thread->id = GetCurrentThreadId(); + thread->md.id = thread->id; + } + + /* Create the blocking IO semaphore */ + thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL); + if (thread->md.blocked_sema == NULL) { + return PR_FAILURE; + } + else { + return PR_SUCCESS; + } +} + +static unsigned __stdcall +pr_root(void *arg) +{ + PRThread *thread = (PRThread *)arg; + thread->md.start(thread); + return 0; +} + +PRStatus +_PR_MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + + thread->md.start = start; + thread->md.handle = (HANDLE) _beginthreadex( + NULL, + thread->stack->stackSize, + pr_root, + (void *)thread, + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + &(thread->id)); + if(!thread->md.handle) { + return PR_FAILURE; + } + + thread->md.id = thread->id; + /* + * On windows, a thread is created with a thread priority of + * THREAD_PRIORITY_NORMAL. + */ + if (priority != PR_PRIORITY_NORMAL) { + _PR_MD_SET_PRIORITY(&(thread->md), priority); + } + + /* Activate the thread */ + if ( ResumeThread( thread->md.handle ) != -1) { + return PR_SUCCESS; + } + + return PR_FAILURE; +} + +void +_PR_MD_YIELD(void) +{ + /* Can NT really yield at all? */ + Sleep(0); +} + +void +_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) +{ + int nativePri; + BOOL rv; + + if (newPri < PR_PRIORITY_FIRST) { + newPri = PR_PRIORITY_FIRST; + } else if (newPri > PR_PRIORITY_LAST) { + newPri = PR_PRIORITY_LAST; + } + switch (newPri) { + case PR_PRIORITY_LOW: + nativePri = THREAD_PRIORITY_BELOW_NORMAL; + break; + case PR_PRIORITY_NORMAL: + nativePri = THREAD_PRIORITY_NORMAL; + break; + case PR_PRIORITY_HIGH: + nativePri = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case PR_PRIORITY_URGENT: + nativePri = THREAD_PRIORITY_HIGHEST; + } + rv = SetThreadPriority(thread->handle, nativePri); + PR_ASSERT(rv); + if (!rv) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_SetThreadPriority: can't set thread priority\n")); + } + return; +} + +const DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push,8) +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; +#pragma pack(pop) + +void +_PR_MD_SET_CURRENT_THREAD_NAME(const char *name) +{ +#ifdef _MSC_VER + THREADNAME_INFO info; +#endif + + if (sSetThreadDescription) { + WCHAR wideName[MAX_PATH]; + if (MultiByteToWideChar(CP_ACP, 0, name, -1, wideName, MAX_PATH)) { + sSetThreadDescription(GetCurrentThread(), wideName); + } + } + +#ifdef _MSC_VER + if (!IsDebuggerPresent()) { + return; + } + + info.dwType = 0x1000; + info.szName = (char*) name; + info.dwThreadID = -1; + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, + 0, + sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR*)&info); + } __except(EXCEPTION_CONTINUE_EXECUTION) { + } +#endif +} + +void +_PR_MD_CLEAN_THREAD(PRThread *thread) +{ + BOOL rv; + + if (thread->md.blocked_sema) { + rv = CloseHandle(thread->md.blocked_sema); + PR_ASSERT(rv); + thread->md.blocked_sema = 0; + } + + if (thread->md.handle) { + rv = CloseHandle(thread->md.handle); + PR_ASSERT(rv); + thread->md.handle = 0; + } +} + +void +_PR_MD_EXIT_THREAD(PRThread *thread) +{ + _PR_MD_CLEAN_THREAD(thread); + _PR_MD_SET_CURRENT_THREAD(NULL); +} + + +void +_PR_MD_EXIT(PRIntn status) +{ + _exit(status); +} + +PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) +{ +#ifdef WINCE + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return -1; +#else + DWORD_PTR rv; + + rv = SetThreadAffinityMask(thread->md.handle, mask); + + return rv?0:-1; +#endif +} + +PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) +{ +#ifdef WINCE + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return -1; +#else + BOOL rv; + DWORD_PTR process_mask; + DWORD_PTR system_mask; + + rv = GetProcessAffinityMask(GetCurrentProcess(), + &process_mask, &system_mask); + if (rv) { + *mask = (PRUint32)process_mask; + } + + return rv?0:-1; +#endif +} + +void +_PR_MD_SUSPEND_CPU(_PRCPU *cpu) +{ + _PR_MD_SUSPEND_THREAD(cpu->thread); +} + +void +_PR_MD_RESUME_CPU(_PRCPU *cpu) +{ + _PR_MD_RESUME_THREAD(cpu->thread); +} + +void +_PR_MD_SUSPEND_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + DWORD previousSuspendCount; + /* XXXMB - SuspendThread() is not a blocking call; how do we + * know when the thread is *REALLY* suspended? + */ + previousSuspendCount = SuspendThread(thread->md.handle); + PR_ASSERT(previousSuspendCount == 0); + } +} + +void +_PR_MD_RESUME_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + DWORD previousSuspendCount; + previousSuspendCount = ResumeThread(thread->md.handle); + PR_ASSERT(previousSuspendCount == 1); + } +} + +PRThread* +_MD_CURRENT_THREAD(void) +{ + PRThread *thread; + + thread = _MD_GET_ATTACHED_THREAD(); + + if (NULL == thread) { + thread = _PRI_AttachThread( + PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0); + } + PR_ASSERT(thread != NULL); + return thread; +} diff --git a/nsprpub/pr/src/md/windows/win32_errors.c b/nsprpub/pr/src/md/windows/win32_errors.c new file mode 100644 index 0000000000..b0118146b6 --- /dev/null +++ b/nsprpub/pr/src/md/windows/win32_errors.c @@ -0,0 +1,541 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prerror.h" +#include "prlog.h" +#include <errno.h> +#include <windows.h> + +/* + * On Win32, we map three kinds of error codes: + * - GetLastError(): for Win32 functions + * - WSAGetLastError(): for Winsock functions + * - errno: for standard C library functions + * + * GetLastError() and WSAGetLastError() return error codes in + * non-overlapping ranges, so their error codes (ERROR_* and + * WSAE*) can be mapped by the same function. On the other hand, + * errno and GetLastError() have overlapping ranges, so we need + * to use a separate function to map errno. + * + * We do not check for WSAEINPROGRESS and WSAEINTR because we do not + * use blocking Winsock 1.1 calls. + * + * Except for the 'socket' call, we do not check for WSAEINITIALISED. + * It is assumed that if Winsock is not initialized, that fact will + * be detected at the time we create new sockets. + */ + +static void _MD_win32_map_default_errno(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case EACCES: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case ENOENT: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + default: + prError = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_default_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case ERROR_ACCESS_DENIED: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case ERROR_ALREADY_EXISTS: + prError = PR_FILE_EXISTS_ERROR; + break; + case ERROR_CALL_NOT_IMPLEMENTED: + prError = PR_NOT_IMPLEMENTED_ERROR; + break; + case ERROR_DISK_CORRUPT: + prError = PR_IO_ERROR; + break; + case ERROR_DISK_FULL: + prError = PR_NO_DEVICE_SPACE_ERROR; + break; + case ERROR_DISK_OPERATION_FAILED: + prError = PR_IO_ERROR; + break; + case ERROR_DRIVE_LOCKED: + prError = PR_FILE_IS_LOCKED_ERROR; + break; + case ERROR_FILENAME_EXCED_RANGE: + prError = PR_NAME_TOO_LONG_ERROR; + break; + case ERROR_FILE_CORRUPT: + prError = PR_IO_ERROR; + break; + case ERROR_FILE_EXISTS: + prError = PR_FILE_EXISTS_ERROR; + break; + case ERROR_FILE_INVALID: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; + case ERROR_FILE_NOT_FOUND: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ERROR_HANDLE_DISK_FULL: + prError = PR_NO_DEVICE_SPACE_ERROR; + break; + case ERROR_INVALID_ADDRESS: + prError = PR_ACCESS_FAULT_ERROR; + break; + case ERROR_INVALID_HANDLE: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; + case ERROR_INVALID_NAME: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case ERROR_INVALID_PARAMETER: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case ERROR_INVALID_USER_BUFFER: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case ERROR_LOCKED: + prError = PR_FILE_IS_LOCKED_ERROR; + break; + case ERROR_NETNAME_DELETED: + prError = PR_CONNECT_RESET_ERROR; + break; + case ERROR_NOACCESS: + prError = PR_ACCESS_FAULT_ERROR; + break; + case ERROR_NOT_ENOUGH_MEMORY: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case ERROR_NOT_ENOUGH_QUOTA: + prError = PR_OUT_OF_MEMORY_ERROR; + break; + case ERROR_NOT_READY: + prError = PR_IO_ERROR; + break; + case ERROR_NO_MORE_FILES: + prError = PR_NO_MORE_FILES_ERROR; + break; + case ERROR_OPEN_FAILED: + prError = PR_IO_ERROR; + break; + case ERROR_OPEN_FILES: + prError = PR_IO_ERROR; + break; + case ERROR_OPERATION_ABORTED: + prError = PR_OPERATION_ABORTED_ERROR; + break; + case ERROR_OUTOFMEMORY: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case ERROR_PATH_BUSY: + prError = PR_IO_ERROR; + break; + case ERROR_PATH_NOT_FOUND: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ERROR_SEEK_ON_DEVICE: + prError = PR_IO_ERROR; + break; + case ERROR_SHARING_VIOLATION: + prError = PR_FILE_IS_BUSY_ERROR; + break; + case ERROR_STACK_OVERFLOW: + prError = PR_ACCESS_FAULT_ERROR; + break; + case ERROR_TOO_MANY_OPEN_FILES: + prError = PR_SYS_DESC_TABLE_FULL_ERROR; + break; + case ERROR_WRITE_PROTECT: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case WSAEACCES: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case WSAEADDRINUSE: + prError = PR_ADDRESS_IN_USE_ERROR; + break; + case WSAEADDRNOTAVAIL: + case ERROR_INVALID_NETNAME: + prError = PR_ADDRESS_NOT_AVAILABLE_ERROR; + break; + case WSAEAFNOSUPPORT: + case ERROR_INCORRECT_ADDRESS: + prError = PR_ADDRESS_NOT_SUPPORTED_ERROR; + break; + case WSAEALREADY: + case ERROR_ALREADY_INITIALIZED: + prError = PR_ALREADY_INITIATED_ERROR; + break; + case WSAEBADF: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; + case WSAECONNABORTED: + case ERROR_CONNECTION_ABORTED: + prError = PR_CONNECT_ABORTED_ERROR; + break; + case WSAECONNREFUSED: + case ERROR_CONNECTION_REFUSED: + prError = PR_CONNECT_REFUSED_ERROR; + break; + case WSAECONNRESET: + prError = PR_CONNECT_RESET_ERROR; + break; + case WSAEDESTADDRREQ: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case WSAEFAULT: + prError = PR_ACCESS_FAULT_ERROR; + break; + case WSAEHOSTUNREACH: + case ERROR_HOST_UNREACHABLE: + prError = PR_HOST_UNREACHABLE_ERROR; + break; + case WSAEINVAL: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case WSAEISCONN: + prError = PR_IS_CONNECTED_ERROR; + break; + case WSAEMFILE: + prError = PR_PROC_DESC_TABLE_FULL_ERROR; + break; + case WSAEMSGSIZE: + prError = PR_BUFFER_OVERFLOW_ERROR; + break; + case WSAENETDOWN: + case ERROR_NO_NETWORK: + prError = PR_NETWORK_DOWN_ERROR; + break; + case WSAENETRESET: + prError = PR_CONNECT_ABORTED_ERROR; + break; + case WSAENETUNREACH: + case ERROR_NETWORK_UNREACHABLE: + prError = PR_NETWORK_UNREACHABLE_ERROR; + break; + case WSAENOBUFS: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case WSAENOPROTOOPT: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case WSAENOTCONN: + prError = PR_NOT_CONNECTED_ERROR; + break; + case WSAENOTSOCK: + prError = PR_NOT_SOCKET_ERROR; + break; + case WSAEOPNOTSUPP: + prError = PR_OPERATION_NOT_SUPPORTED_ERROR; + break; + case WSAEPROTONOSUPPORT: + prError = PR_PROTOCOL_NOT_SUPPORTED_ERROR; + break; + case WSAEPROTOTYPE: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case WSAESHUTDOWN: + prError = PR_SOCKET_SHUTDOWN_ERROR; + break; + case WSAESOCKTNOSUPPORT: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case WSAETIMEDOUT: + prError = PR_CONNECT_ABORTED_ERROR; + break; + case WSAEWOULDBLOCK: + prError = PR_WOULD_BLOCK_ERROR; + break; + default: + prError = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_opendir_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_closedir_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_unix_readdir_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_delete_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +/* The error code for stat() is in errno. */ +void _MD_win32_map_stat_error(PRInt32 err) +{ + _MD_win32_map_default_errno(err); +} + +void _MD_win32_map_fstat_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_rename_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +/* The error code for access() is in errno. */ +void _MD_win32_map_access_error(PRInt32 err) +{ + _MD_win32_map_default_errno(err); +} + +void _MD_win32_map_mkdir_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_rmdir_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_read_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_transmitfile_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_write_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_lseek_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_fsync_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +/* + * For both CloseHandle() and closesocket(). + */ +void _MD_win32_map_close_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_socket_error(PRInt32 err) +{ + PR_ASSERT(err != WSANOTINITIALISED); + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_recv_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_recvfrom_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_send_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEMSGSIZE: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_sendto_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEMSGSIZE: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_accept_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEOPNOTSUPP: + prError = PR_NOT_TCP_SOCKET_ERROR; + break; + case WSAEINVAL: + prError = PR_INVALID_STATE_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_acceptex_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_connect_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEWOULDBLOCK: + prError = PR_IN_PROGRESS_ERROR; + break; + case WSAEINVAL: + prError = PR_ALREADY_INITIATED_ERROR; + break; + case WSAETIMEDOUT: + prError = PR_IO_TIMEOUT_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_bind_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEINVAL: + prError = PR_SOCKET_ADDRESS_IS_BOUND_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_listen_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEOPNOTSUPP: + prError = PR_NOT_TCP_SOCKET_ERROR; + break; + case WSAEINVAL: + prError = PR_INVALID_STATE_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_shutdown_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_getsockname_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAEINVAL: + prError = PR_INVALID_STATE_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_getpeername_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_getsockopt_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_setsockopt_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_open_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +void _MD_win32_map_gethostname_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} + +/* Win32 select() only works on sockets. So in this +** context, WSAENOTSOCK is equivalent to EBADF on Unix. +*/ +void _MD_win32_map_select_error(PRInt32 err) +{ + PRErrorCode prError; + + switch (err) { + case WSAENOTSOCK: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; + default: + _MD_win32_map_default_error(err); + return; + } + PR_SetError(prError, err); +} + +void _MD_win32_map_lockf_error(PRInt32 err) +{ + _MD_win32_map_default_error(err); +} diff --git a/nsprpub/pr/src/memory/Makefile.in b/nsprpub/pr/src/memory/Makefile.in new file mode 100644 index 0000000000..3272cadb4d --- /dev/null +++ b/nsprpub/pr/src/memory/Makefile.in @@ -0,0 +1,29 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = prseg.c prshm.c prshma.c + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + + diff --git a/nsprpub/pr/src/memory/prseg.c b/nsprpub/pr/src/memory/prseg.c new file mode 100644 index 0000000000..0b70f2d4ea --- /dev/null +++ b/nsprpub/pr/src/memory/prseg.c @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#if defined(_PR_PTHREADS) + +/* +** The pthreads version doesn't use these functions. +*/ +void _PR_InitSegs(void) +{ +} + +#else /* _PR_PTHREADS */ + +void _PR_InitSegs(void) +{ + _PR_MD_INIT_SEGS(); +} + +/* +** Allocate a memory segment. The size value is rounded up to the native +** system page size and a page aligned portion of memory is returned. +** This memory is not part of the malloc heap. If "vaddr" is not NULL +** then PR tries to allocate the segment at the desired virtual address. +*/ +PRSegment* _PR_NewSegment(PRUint32 size, void *vaddr) +{ + PRSegment *seg; + + /* calloc the data structure for the segment */ + seg = PR_NEWZAP(PRSegment); + + if (seg) { + size = ((size + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift; + /* + ** Now, allocate the actual segment memory (or map under some OS) + ** The OS specific code decides from where or how to allocate memory. + */ + if (_PR_MD_ALLOC_SEGMENT(seg, size, vaddr) != PR_SUCCESS) { + PR_DELETE(seg); + return NULL; + } + } + + return seg; +} + +/* +** Free a memory segment. +*/ +void _PR_DestroySegment(PRSegment *seg) +{ + _PR_MD_FREE_SEGMENT(seg); + PR_DELETE(seg); +} + +#endif /* _PR_PTHREADS */ diff --git a/nsprpub/pr/src/memory/prshm.c b/nsprpub/pr/src/memory/prshm.c new file mode 100644 index 0000000000..7a587cd5d1 --- /dev/null +++ b/nsprpub/pr/src/memory/prshm.c @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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/nsprpub/pr/src/memory/prshma.c b/nsprpub/pr/src/memory/prshma.c new file mode 100644 index 0000000000..830b6df51d --- /dev/null +++ b/nsprpub/pr/src/memory/prshma.c @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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/nsprpub/pr/src/misc/Makefile.in b/nsprpub/pr/src/misc/Makefile.in new file mode 100644 index 0000000000..3d87da2091 --- /dev/null +++ b/nsprpub/pr/src/misc/Makefile.in @@ -0,0 +1,80 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = \ + pralarm.c \ + pratom.c \ + prcountr.c \ + prdtoa.c \ + prenv.c \ + prerr.c \ + prerror.c \ + prerrortable.c \ + prinit.c \ + prinrval.c \ + pripc.c \ + prlog2.c \ + prlong.c \ + prnetdb.c \ + praton.c \ + prolock.c \ + prrng.c \ + prsystem.c \ + prtime.c \ + prthinfo.c \ + prtpool.c \ + prtrace.c \ + $(NULL) + +ifndef USE_PTHREADS +CSRCS += \ + pripcsem.c \ + $(NULL) +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +RELEASE_BINS = $(srcdir)/compile-et.pl $(srcdir)/prerr.properties + +include $(topsrcdir)/config/rules.mk + +# Prevent floating point errors caused by MSVC 6.0 Processor Pack +# optimizations (bug 207421). This disables optimizations that +# could change the precision of floating-point calculations for +# this single compilation unit. +ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT) +$(OBJDIR)/prdtoa.$(OBJ_SUFFIX): prdtoa.c + @$(MAKE_OBJDIR) +ifeq (,$(filter-out 1100 1200 1300 1310,$(MSC_VER))) + $(CC) -Fo$@ -c $(CFLAGS) -Op $(call pr_abspath,$<) +else + $(CC) -Fo$@ -c $(CFLAGS) -fp:precise $(call pr_abspath,$<) +endif +endif + +# +# Generate prerr.h, prerr.c, and prerr.properties from prerr.et. +# +build_prerr: + cd $(srcdir); $(PERL) compile-et.pl prerr.et + +export:: $(TARGETS) + + diff --git a/nsprpub/pr/src/misc/compile-et.pl b/nsprpub/pr/src/misc/compile-et.pl new file mode 100644 index 0000000000..50855298a4 --- /dev/null +++ b/nsprpub/pr/src/misc/compile-et.pl @@ -0,0 +1,108 @@ +#!/usr/bin/perl + +# usage: compile-et input.et + +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +sub header +{ + local($filename, $comment) = @_; + +<<EOF +$comment +$comment $filename +$comment This file is automatically generated; please do not edit it. +EOF +} + +sub table_base +{ + local($name) = @_; + local($base) = 0; + + for ($i = 0; $i < length($name); $i++) { + $base *= 64; + $base += index("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_", substr($name, $i, 1)) + 1; + } + $base -= 0x1000000 if ($base > 0x7fffff); + $base*256; +} + +sub code { + local($macro, $text) = @_; + $code = $table_base + $table_item_count; + + print H "\n"; + print H "/* ", $text, " */\n"; + printf H "#define %-40s (%dL)\n", $macro, $code; + + print C "\t{\"", $macro, "\", \"", $text, "\"},\n"; + + print PROPERTIES $macro, "=", $text, "\n"; + + $table_item_count++; +} + + +$filename = $ARGV[0]; +open(INPUT, "< $filename") || die "Can't read $filename: $!\n"; + +$base = "$filename"; +$base =~ s/\.et$//; +$base =~ s#.*/##; + +open(H, "> ${base}.h") || die "Can't write ${base}.h\n"; +open(C, "> ${base}.c") || die "Can't write ${base}.c\n"; +open(PROPERTIES, "> ${base}.properties") || die "Can't write ${base}.properties\n"; + +print H "/*\n", &header("${base}.h", " *"), " */\n"; +print C "/*\n", &header("${base}.c", " *"), " */\n"; +print PROPERTIES &header("${base}.properties", "#"); + +$skipone = 0; + +while ($_ = <INPUT>) { + next if /^#/; + + if (/^[ \t]*(error_table|et)[ \t]+([a-zA-Z][a-zA-Z0-9_]+) *(-?[0-9]*)/) { + $table_name = $2; + if ($3) { + $table_base = $3; + } + else { + $table_base = &table_base($table_name); + } + $table_item_count = 0; + + print C "#include \"prerror.h\"\n"; + print C "static const struct PRErrorMessage text[] = {\n"; + } + elsif (/^[ \t]*(error_code|ec)[ \t]+([A-Z_0-9]+),[ \t]*$/) { + $skipone = 1; + $macro = $2; + } + elsif (/^[ \t]*(error_code|ec)[ \t]+([A-Z_0-9]+),[ \t]*"(.*)"[ \t]*$/) { + &code($2, $3); + } + elsif ($skipone && /^[ \t]*"(.*)"[ \t]*$/) { + &code($macro, $1); + } +} + +print H "\n"; +print H "extern void ", $table_name, "_InitializePRErrorTable","(void);\n"; +printf H "#define ERROR_TABLE_BASE_%s (%dL)\n", $table_name, $table_base; + +print C "\t{0, 0}\n"; +print C "};\n\n"; +printf C "static const struct PRErrorTable et = { text, \"%s\", %dL, %d };\n", + $base, $table_base, $table_item_count; +print C "\n"; +print C "void ", $table_name, "_InitializePRErrorTable", "(void) {\n"; +print C " PR_ErrorInstallTable(&et);\n"; +print C "}\n"; + +0; diff --git a/nsprpub/pr/src/misc/dtoa.c b/nsprpub/pr/src/misc/dtoa.c new file mode 100644 index 0000000000..43883aba26 --- /dev/null +++ b/nsprpub/pr/src/misc/dtoa.c @@ -0,0 +1,4610 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. This will cause dtoa modes 4 and 5 to be + * treated the same as modes 2 and 3 for some inputs. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS + * is also #defined, fegetround() will be queried for the rounding mode. + * Note that both FLT_ROUNDS and fegetround() are specified by the C99 + * standard (and are specified to be consistent, with fesetround() + * affecting the value of FLT_ROUNDS), but that some (Linux) systems + * do not work correctly in this regard, so using fegetround() is more + * portable than using FLT_ROUNDS directly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic + * that rounds toward +Infinity. + * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased + * rounding when the underlying floating-point arithmetic uses + * unbiased rounding. This prevent using ordinary floating-point + * arithmetic when the result could be computed with one rounding error. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). On some systems + * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + * #define NO_HEX_FP to omit recognition of hexadecimal floating-point + * values by strtod. + * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now) + * to disable logic for "fast" testing of very long input strings + * to strtod. This testing proceeds by initially truncating the + * input string, then if necessary comparing the whole string with + * a decimal expansion to decide close cases. This logic is only + * used for input more than STRTOD_DIGLIM digits long (default 40). + */ + +#ifndef Long +#define Long long +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef Honor_FLT_ROUNDS +#ifndef Trust_FLT_ROUNDS +#include <fenv.h> +#endif +#endif + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#else +#undef INFNAN_CHECK +#define NO_STRTOD_BIGCOMP +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union { + double d; + ULong L[2]; +} U; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +#ifndef STRTOD_DIGLIM +#define STRTOD_DIGLIM 40 +#endif + +#ifdef DIGLIM_DEBUG +extern int strtod_diglim; +#else +#define strtod_diglim STRTOD_DIGLIM +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Nbits 53 +#define Bias 1023 +#define Emax 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Nbits 56 +#define Bias 65 +#define Emax 248 +#define Emin (-260) +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Nbits 56 +#define Bias 129 +#define Emax 126 +#define Emin (-129) +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#else +#ifdef ROUND_BIASED_without_Round_Up +#undef ROUND_BIASED +#define ROUND_BIASED +#endif +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +typedef struct BCinfo BCinfo; +struct + BCinfo { + int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; +}; + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 7 + +#ifdef __cplusplus +extern "C" double strtod(const char *s00, char **se); +extern "C" char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +#endif + +struct + Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; +}; + +typedef struct Bigint Bigint; + +static Bigint *freelist[Kmax+1]; + +static Bigint * +Balloc +#ifdef KR_headers +(k) int k; +#else +(int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) { + freelist[k] = rv->next; + } + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else { + rv = (Bigint*)MALLOC(len*sizeof(double)); + } +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; +} + +static void +Bfree +#ifdef KR_headers +(v) Bigint *v; +#else +(Bigint *v) +#endif +{ + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } +} + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + +static Bigint * +multadd +#ifdef KR_headers +(b, m, a) Bigint *b; int m, a; +#else +(Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; +} + +static Bigint * +s2b +#ifdef KR_headers +(s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9; +#else +(const char *s, int nd0, int nd, ULong y9, int dplen) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do { + b = multadd(b, 10, *s++ - '0'); + } + while(++i < nd0); + s += dplen; + } + else { + s += dplen + 9; + } + for(; i < nd; i++) { + b = multadd(b, 10, *s++ - '0'); + } + return b; +} + +static int +hi0bits +#ifdef KR_headers +(x) ULong x; +#else +(ULong x) +#endif +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) { + return 32; + } + } + return k; +} + +static int +lo0bits +#ifdef KR_headers +(y) ULong *y; +#else +(ULong *y) +#endif +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) { + return 0; + } + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) { + return 32; + } + } + *y = x; + return k; +} + +static Bigint * +i2b +#ifdef KR_headers +(i) int i; +#else +(int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; +} + +static Bigint * +mult +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) { + k++; + } + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) { + *x = 0; + } + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +static Bigint *p5s; + +static Bigint * +pow5mult +#ifdef KR_headers +(b, k) Bigint *b; int k; +#else +(Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) { + b = multadd(b, p05[i-1], 0); + } + + if (!(k >>= 2)) { + return b; + } + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) { + break; + } + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; +} + +static Bigint * +lshift +#ifdef KR_headers +(b, k) Bigint *b; int k; +#else +(Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) { + k1++; + } + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) { + *x1++ = 0; + } + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) { + ++n1; + } + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) { + ++n1; + } + } +#endif + else do { + *x1++ = *x++; + } + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; +} + +static int +cmp +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) { + Bug("cmp called with a->x[a->wds-1] == 0"); + } + if (j > 1 && !b->x[j-1]) { + Bug("cmp called with b->x[b->wds-1] == 0"); + } +#endif + if (i -= j) { + return i; + } + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) { + return *xa < *xb ? -1 : 1; + } + if (xa <= xa0) { + break; + } + } + return 0; +} + +static Bigint * +diff +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else { + i = 0; + } + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) { + wa--; + } + c->wds = wa; + return c; +} + +static double +ulp +#ifdef KR_headers +(x) U *x; +#else +(U *x) +#endif +{ + Long L; + U u; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(&u) = L; + word1(&u) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&u) = 0x80000 >> L; + word1(&u) = 0; + } + else { + word0(&u) = 0; + L -= Exp_shift; + word1(&u) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(&u); +} + +static double +b2d +#ifdef KR_headers +(a, e) Bigint *a; int *e; +#else +(Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) { + Bug("zero y in b2d"); + } +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif +ret_d: +#ifdef VAX + word0(&d) = d0 >> 16 | d0 << 16; + word1(&d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(&d); +} + +static Bigint * +d2b +#ifdef KR_headers +(d, e, bits) U *d; int *e, *bits; +#else +(U *d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) { + z |= Exp_msk1; + } +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << (32 - k); + z >>= k; + } + else { + x[0] = y; + } +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) { + Bug("Zero passed to d2b"); + } +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) { + --i; + } + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; +} +#undef d0 +#undef d1 + +static double +ratio +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(&da) += (k >> 2)*Exp_msk1; + if (k &= 3) { + dval(&da) *= 1 << k; + } + } + else { + k = -k; + word0(&db) += (k >> 2)*Exp_msk1; + if (k &= 3) { + dval(&db) *= 1 << k; + } + } +#else + if (k > 0) { + word0(&da) += k*Exp_msk1; + } + else { + k = -k; + word0(&db) += k*Exp_msk1; + } +#endif + return dval(&da) / dval(&db); +} + +static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif +}; + +static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-256 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#undef Need_Hexdig +#ifdef INFNAN_CHECK +#ifndef No_Hex_NaN +#define Need_Hexdig +#endif +#endif + +#ifndef Need_Hexdig +#ifndef NO_HEX_FP +#define Need_Hexdig +#endif +#endif + +#ifdef Need_Hexdig /*{*/ +static unsigned char hexdig[256]; + +static void +#ifdef KR_headers +htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc; +#else +htinit(unsigned char *h, unsigned char *s, int inc) +#endif +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) { + h[j] = i + inc; + } +} + +static void +#ifdef KR_headers +hexdig_init() +#else +hexdig_init(void) +#endif +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); +} +#endif /* } Need_Hexdig */ + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + +static int +match +#ifdef KR_headers +(sp, t) char **sp, *t; +#else +(const char **sp, const char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') { + c += 'a' - 'A'; + } + if (c != d) { + return 0; + } + } + *sp = s + 1; + return 1; +} + +#ifndef No_Hex_NaN +static void +hexnan +#ifdef KR_headers +(rvp, sp) U *rvp; CONST char **sp; +#else +(U *rvp, const char **sp) +#endif +{ + ULong c, x[2]; + CONST char *s; + int c1, havedig, udx0, xshift; + + if (!hexdig['0']) { + hexdig_init(); + } + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') { + ++s; + } + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) { + s += 2; + } + while((c = *(CONST unsigned char*)++s)) { + if ((c1 = hexdig[c])) { + c = c1 & 0xf; + } + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } +#ifdef GDTOA_NON_PEDANTIC_NANCHECK + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else { + return; /* invalid form: don't change *sp */ + } +#else + else { + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); + break; + } +#endif + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) { + x[0] = (x[0] << 4) | (x[1] >> 28); + } + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(rvp) = Exp_mask | x[0]; + word1(rvp) = x[1]; + } +} +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#endif + +#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/ +static Bigint * +#ifdef KR_headers +increment(b) Bigint *b; +#else +increment(Bigint *b) +#endif +{ + ULong *x, *xe; + Bigint *b1; + + x = b->x; + xe = x + b->wds; + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1,b); + Bfree(b); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; +} + +#endif /*}*/ + +#ifndef NO_HEX_FP /*{*/ + +static void +#ifdef KR_headers +rshift(b, k) Bigint *b; int k; +#else +rshift(Bigint *b, int k) +#endif +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = 32 - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & 0xffffffff; + y = *x++ >> k; + } + if ((*x1 = y) !=0) { + x1++; + } + } + else + while(x < xe) { + *x1++ = *x++; + } + } + if ((b->wds = x1 - b->x) == 0) { + b->x[0] = 0; + } +} + +static ULong +#ifdef KR_headers +any_on(b, k) Bigint *b; int k; +#else +any_on(Bigint *b, int k) +#endif +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) { + n = nwds; + } + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) { + return 1; + } + } + x0 = x; + x += n; + while(x > x0) + if (*--x) { + return 1; + } + return 0; +} + +enum { /* rounding values: same as FLT_ROUNDS */ + Round_zero = 0, + Round_near = 1, + Round_up = 2, + Round_down = 3 +}; + +void +#ifdef KR_headers +gethex(sp, rvp, rounding, sign) +CONST char **sp; U *rvp; int rounding, sign; +#else +gethex( CONST char **sp, U *rvp, int rounding, int sign) +#endif +{ + Bigint *b; + CONST unsigned char *decpt, *s0, *s, *s1; + Long e, e1; + ULong L, lostbits, *x; + int big, denorm, esign, havedig, k, n, nbits, up, zret; +#ifdef IBM + int j; +#endif + enum { +#ifdef IEEE_Arith /*{{*/ + emax = 0x7fe - Bias - P + 1, + emin = Emin - P + 1 +#else /*}{*/ + emin = Emin - P, +#ifdef VAX + emax = 0x7ff - Bias - P + 1 +#endif +#ifdef IBM + emax = 0x7f - Bias - P +#endif +#endif /*}}*/ + }; +#ifdef USE_LOCALE + int i; +#ifdef NO_LOCALE_CACHE + const unsigned char *decimalpoint = (unsigned char*) + localeconv()->decimal_point; +#else + const unsigned char *decimalpoint; + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char*)localeconv()->decimal_point; + if ((decimalpoint_cache = (unsigned char*) + MALLOC(strlen((CONST char*)s0) + 1))) { + strcpy((char*)decimalpoint_cache, (CONST char*)s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + if (!hexdig['0']) { + hexdig_init(); + } + havedig = 0; + s0 = *(CONST unsigned char **)sp + 2; + while(s0[havedig] == '0') { + havedig++; + } + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) { + havedig++; + } + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) { + goto pcheck; + } + } + decpt = s += i; +#else + if (*s != '.') { + goto pcheck; + } + decpt = ++s; +#endif + if (!hexdig[*s]) { + goto pcheck; + } + while(*s == '0') { + s++; + } + if (hexdig[*s]) { + zret = 0; + } + havedig = 1; + s0 = s; + } + while(hexdig[*s]) { + s++; + } +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) { + goto pcheck; + } + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) { + s++; + } + }/*}*/ + if (decpt) { + e = -(((Long)(s-decpt)) << 2); + } +pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* no break */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) { + big = 1; + } + e1 = 10*e1 + n - 0x10; + } + if (esign) { + e1 = -e1; + } + e += e1; + } + *sp = (char*)s; + if (!havedig) { + *sp = (char*)s0 - 1; + } + if (zret) { + goto retz1; + } + if (big) { + if (esign) { +#ifdef IEEE_Arith + switch(rounding) { + case Round_up: + if (sign) { + break; + } + goto ret_tiny; + case Round_down: + if (!sign) { + break; + } + goto ret_tiny; + } +#endif + goto retz; +#ifdef IEEE_Arith +ret_tiny: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = 0; + word1(rvp) = 1; + return; +#endif /* IEEE_Arith */ + } + switch(rounding) { + case Round_near: + goto ovfl1; + case Round_up: + if (!sign) { + goto ovfl1; + } + goto ret_big; + case Round_down: + if (sign) { + goto ovfl1; + } + goto ret_big; + } +ret_big: + word0(rvp) = Big0; + word1(rvp) = Big1; + return; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) { + k++; + } + b = Balloc(k); + x = b->x; + n = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') { + continue; + } +#endif + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (hexdig[*s1] & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + n = ULbits*n - hi0bits(L); + nbits = Nbits; + lostbits = 0; + x = b->x; + if (n > nbits) { + n -= nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) { + lostbits = 3; + } + } + } + rshift(b, n); + e += n; + } + else if (n < nbits) { + n = nbits - n; + b = lshift(b, n); + e -= n; + x = b->x; + } + if (e > Emax) { +ovfl: + Bfree(b); +ovfl1: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = Exp_mask; + word1(rvp) = 0; + return; + } + denorm = 0; + if (e < emin) { + denorm = 1; + n = emin - e; + if (n >= nbits) { +#ifdef IEEE_Arith /*{*/ + switch (rounding) { + case Round_near: + if (n == nbits && (n < 2 || any_on(b,n-1))) { + goto ret_tiny; + } + break; + case Round_up: + if (!sign) { + goto ret_tiny; + } + break; + case Round_down: + if (sign) { + goto ret_tiny; + } + } +#endif /* } IEEE_Arith */ + Bfree(b); +retz: +#ifndef NO_ERRNO + errno = ERANGE; +#endif +retz1: + rvp->d = 0.; + return; + } + k = n - 1; + if (lostbits) { + lostbits = 1; + } + else if (k > 0) { + lostbits = any_on(b,k); + } + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits |= 2; + } + nbits -= n; + rshift(b,n); + e = emin; + } + if (lostbits) { + up = 0; + switch(rounding) { + case Round_zero: + break; + case Round_near: + if (lostbits & 2 + && (lostbits & 1) | (x[0] & 1)) { + up = 1; + } + break; + case Round_up: + up = 1 - sign; + break; + case Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b); + x = b->x; + if (denorm) { +#if 0 + if (nbits == Nbits - 1 + && x[nbits >> kshift] & 1 << (nbits & kmask)) { + denorm = 0; /* not currently used */ + } +#endif + } + else if (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n)) { + rshift(b,1); + if (++e > Emax) { + goto ovfl; + } + } + } + } +#ifdef IEEE_Arith + if (denorm) { + word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; + } + else { + word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); + } + word1(rvp) = b->x[0]; +#endif +#ifdef IBM + if ((j = e & 3)) { + k = b->x[0] & ((1 << j) - 1); + rshift(b,j); + if (k) { + switch(rounding) { + case Round_up: + if (!sign) { + increment(b); + } + break; + case Round_down: + if (sign) { + increment(b); + } + break; + case Round_near: + j = 1 << (j-1); + if (k & j && ((k & (j-1)) | lostbits)) { + increment(b); + } + } + } + } + e >>= 2; + word0(rvp) = b->x[1] | ((e + 65 + 13) << 24); + word1(rvp) = b->x[0]; +#endif +#ifdef VAX + /* The next two lines ignore swap of low- and high-order 2 bytes. */ + /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */ + /* word1(rvp) = b->x[0]; */ + word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16); + word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16); +#endif + Bfree(b); +} +#endif /*!NO_HEX_FP}*/ + +static int +#ifdef KR_headers +dshift(b, p2) Bigint *b; int p2; +#else +dshift(Bigint *b, int p2) +#endif +{ + int rv = hi0bits(b->x[b->wds-1]) - 4; + if (p2 > 0) { + rv -= p2; + } + return rv & kmask; +} + +static int +quorem +#ifdef KR_headers +(b, S) Bigint *b, *S; +#else +(Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/{ + Bug("oversize b in quorem"); + } +#endif + if (b->wds < n) { + return 0; + } + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG +#ifdef NO_STRTOD_BIGCOMP + /*debug*/ if (q > 9) +#else + /* An oversized q is possible when quorem is called from bigcomp and */ + /* the input is near, e.g., twice the smallest denormalized number. */ + /*debug*/ if (q > 15) +#endif + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) { + --n; + } + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) { + --n; + } + b->wds = n; + } + } + return q; +} + +#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/ +static double +sulp +#ifdef KR_headers +(x, bc) U *x; BCinfo *bc; +#else +(U *x, BCinfo *bc) +#endif +{ + U u; + double rv; + int i; + + rv = ulp(x); + if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) { + return rv; /* Is there an example where i <= 0 ? */ + } + word0(&u) = Exp_1 + (i << Exp_shift); + word1(&u) = 0; + return rv * u.d; +} +#endif /*}*/ + +#ifndef NO_STRTOD_BIGCOMP +static void +bigcomp +#ifdef KR_headers +(rv, s0, bc) +U *rv; CONST char *s0; BCinfo *bc; +#else +(U *rv, const char *s0, BCinfo *bc) +#endif +{ + Bigint *b, *d; + int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase; + + dsign = bc->dsign; + nd = bc->nd; + nd0 = bc->nd0; + p5 = nd + bc->e0 - 1; + speccase = 0; +#ifndef Sudden_Underflow + if (rv->d == 0.) { /* special case: value near underflow-to-zero */ + /* threshold was rounded to zero */ + b = i2b(1); + p2 = Emin - P + 1; + bbits = 1; +#ifdef Avoid_Underflow + word0(rv) = (P+2) << Exp_shift; +#else + word1(rv) = 1; +#endif + i = 0; +#ifdef Honor_FLT_ROUNDS + if (bc->rounding == 1) +#endif + { + speccase = 1; + --p2; + dsign = 0; + goto have_i; + } + } + else +#endif + b = d2b(rv, &p2, &bbits); +#ifdef Avoid_Underflow + p2 -= bc->scale; +#endif + /* floor(log2(rv)) == bbits - 1 + p2 */ + /* Check for denormal case. */ + i = P - bbits; + if (i > (j = P - Emin - 1 + p2)) { +#ifdef Sudden_Underflow + Bfree(b); + b = i2b(1); + p2 = Emin; + i = P - 1; +#ifdef Avoid_Underflow + word0(rv) = (1 + bc->scale) << Exp_shift; +#else + word0(rv) = Exp_msk1; +#endif + word1(rv) = 0; +#else + i = j; +#endif + } +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (i > 0) { + b = lshift(b, i); + } + if (dsign) { + b = increment(b); + } + } + else +#endif + { + b = lshift(b, ++i); + b->x[0] |= 1; + } +#ifndef Sudden_Underflow +have_i: +#endif + p2 -= p5 + i; + d = i2b(1); + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + */ + if (p5 > 0) { + d = pow5mult(d, p5); + } + else if (p5 < 0) { + b = pow5mult(b, -p5); + } + if (p2 > 0) { + b2 = p2; + d2 = 0; + } + else { + b2 = 0; + d2 = -p2; + } + i = dshift(d, d2); + if ((b2 += i) > 0) { + b = lshift(b, b2); + } + if ((d2 += i) > 0) { + d = lshift(d, d2); + } + + /* Now b/d = exactly half-way between the two floating-point values */ + /* on either side of the input string. Compute first digit of b/d. */ + + if (!(dig = quorem(b,d))) { + b = multadd(b, 10, 0); /* very unlikely */ + dig = quorem(b,d); + } + + /* Compare b/d with s0 */ + + for(i = 0; i < nd0; ) { + if ((dd = s0[i++] - '0' - dig)) { + goto ret; + } + if (!b->x[0] && b->wds == 1) { + if (i < nd) { + dd = 1; + } + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + for(j = bc->dp1; i++ < nd;) { + if ((dd = s0[j++] - '0' - dig)) { + goto ret; + } + if (!b->x[0] && b->wds == 1) { + if (i < nd) { + dd = 1; + } + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + if (b->x[0] || b->wds > 1 || dig > 0) { + dd = -1; + } +ret: + Bfree(b); + Bfree(d); +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (dd < 0) { + if (bc->rounding == 0) { + if (!dsign) { + goto retlow1; + } + } + else if (dsign) { + goto rethi1; + } + } + else if (dd > 0) { + if (bc->rounding == 0) { + if (dsign) { + goto rethi1; + } + goto ret1; + } + if (!dsign) { + goto rethi1; + } + dval(rv) += 2.*sulp(rv,bc); + } + else { + bc->inexact = 0; + if (dsign) { + goto rethi1; + } + } + } + else +#endif + if (speccase) { + if (dd <= 0) { + rv->d = 0.; + } + } + else if (dd < 0) { + if (!dsign) /* does not happen for round-near */ +retlow1: + dval(rv) -= sulp(rv,bc); + } + else if (dd > 0) { + if (dsign) { +rethi1: + dval(rv) += sulp(rv,bc); + } + } + else { + /* Exact half-way case: apply round-even rule. */ + if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) { + i = 1 - j; + if (i <= 31) { + if (word1(rv) & (0x1 << i)) { + goto odd; + } + } + else if (word0(rv) & (0x1 << (i-32))) { + goto odd; + } + } + else if (word1(rv) & 1) { +odd: + if (dsign) { + goto rethi1; + } + goto retlow1; + } + } + +#ifdef Honor_FLT_ROUNDS +ret1: +#endif + return; +} +#endif /* NO_STRTOD_BIGCOMP */ + +double +strtod +#ifdef KR_headers +(s00, se) CONST char *s00; char **se; +#else +(const char *s00, char **se) +#endif +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1; + int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1; + Long L; + U aadj2, adj, rv, rv0; + ULong y, z; + BCinfo bc; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef Avoid_Underflow + ULong Lsb, Lsb1; +#endif +#ifdef SET_INEXACT + int oldinexact; +#endif +#ifndef NO_STRTOD_BIGCOMP + int req_bigcomp = 0; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + bc.rounding = Flt_Rounds; +#else /*}{*/ + bc.rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: bc.rounding = 0; break; + case FE_UPWARD: bc.rounding = 2; break; + case FE_DOWNWARD: bc.rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ +#ifdef USE_LOCALE + CONST char *s2; +#endif + + sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0; + dval(&rv) = 0.; + for(s = s00;; s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) { + goto break2; + } + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } +break2: + if (*s == '0') { +#ifndef NO_HEX_FP /*{*/ + switch(s[1]) { + case 'x': + case 'X': +#ifdef Honor_FLT_ROUNDS + gethex(&s, &rv, bc.rounding, sign); +#else + gethex(&s, &rv, 1, sign); +#endif + goto ret; + } +#endif /*}*/ + nz0 = 1; + while(*++s == '0') ; + if (!*s) { + goto ret; + } + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) { + y = 10*y + c - '0'; + } + else if (nd < 16) { + z = 10*z + c - '0'; + } + nd0 = nd; + bc.dp0 = bc.dp1 = s - s0; + for(s1 = s; s1 > s0 && *--s1 == '0'; ) { + ++nz1; + } +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + bc.dp1 = s - s0; + bc.dplen = bc.dp1 - bc.dp0; + if (!nd) { + for(; c == '0'; c = *++s) { + nz++; + } + if (c > '0' && c <= '9') { + bc.dp0 = s0 - s; + bc.dp1 = bc.dp0 + bc.dplen; + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { +have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) { + y *= 10; + } + else if (nd <= DBL_DIG + 1) { + z *= 10; + } + if (nd++ < 9) { + y = 10*y + c; + } + else if (nd <= DBL_DIG + 1) { + z = 10*z + c; + } + nz = nz1 = 0; + } + } + } +dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') { + c = *++s; + } + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') { + L = 10*L + c - '0'; + } + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + { + e = 19999; /* safe for 16 bit ints */ + } + else { + e = (int)L; + } + if (esign) { + e = -e; + } + } + else { + e = 0; + } + } + else { + s = s00; + } + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + if (!bc.dplen) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) { + ++s; + } + word0(&rv) = 0x7ff00000; + word1(&rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') { /*)*/ + hexnan(&rv, &s); + } +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ +ret0: + s = s00; + sign = 0; + } + goto ret; + } + bc.e0 = e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) { + nd0 = nd; + } + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(&rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) { + oldinexact = get_inexact(); + } +#endif + dval(&rv) = tens[k - 9] * dval(&rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) { + goto ret; + } +#ifndef ROUND_BIASED_without_Round_Up + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + e -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ +vax_ovfl_check: + word0(&rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if ((word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + goto ovfl; + } + word0(&rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(&rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } +#endif +#endif /* ROUND_BIASED_without_Round_Up */ + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + bc.inexact = 1; + if (k <= DBL_DIG) { + oldinexact = get_inexact(); + } +#endif +#ifdef Avoid_Underflow + bc.scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if (bc.rounding >= 2) { + if (sign) { + bc.rounding = bc.rounding == 2 ? 0 : 2; + } + else if (bc.rounding != 2) { + bc.rounding = 0; + } + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) { + dval(&rv) *= tens[i]; + } + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { +ovfl: + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(&rv) = Big0; + word1(&rv) = Big1; + break; + default: + word0(&rv) = Exp_mask; + word1(&rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(&rv) = Exp_mask; + word1(&rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(&rv0) = 1e300; + dval(&rv0) *= dval(&rv0); +#endif +#else /*IEEE_Arith*/ + word0(&rv) = Big0; + word1(&rv) = Big1; +#endif /*IEEE_Arith*/ +range_err: + if (bd0) { + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + } +#ifndef NO_ERRNO + errno = ERANGE; +#endif + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) { + dval(&rv) *= bigtens[j]; + } + /* The last multiplication could overflow. */ + word0(&rv) -= P*Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + goto ovfl; + } + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } + else { + word0(&rv) += P*Exp_msk1; + } + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) { + dval(&rv) /= tens[i]; + } + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) { + goto undfl; + } +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) { + bc.scale = 2*P; + } + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) { + dval(&rv) *= tinytens[j]; + } + if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; clear j low bits */ + if (j >= 32) { + if (j > 54) { + goto undfl; + } + word1(&rv) = 0; + if (j >= 53) { + word0(&rv) = (P+2)*Exp_msk1; + } + else { + word0(&rv) &= 0xffffffff << (j-32); + } + } + else { + word1(&rv) &= 0xffffffff << j; + } + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) { + dval(&rv) *= tinytens[j]; + } + /* The last multiplication could underflow. */ + dval(&rv0) = dval(&rv); + dval(&rv) *= tinytens[j]; + if (!dval(&rv)) { + dval(&rv) = 2.*dval(&rv0); + dval(&rv) *= tinytens[j]; +#endif + if (!dval(&rv)) { +undfl: + dval(&rv) = 0.; + goto range_err; + } +#ifndef Avoid_Underflow + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } +} + +/* Now the hard part -- adjusting rv to the correct value.*/ + +/* Put digits into bd: true value = bd * 10^e */ + +bc.nd = nd - nz1; +#ifndef NO_STRTOD_BIGCOMP +bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */ +/* to silence an erroneous warning about bc.nd0 */ +/* possibly not being initialized. */ +if (nd > strtod_diglim) { + /* ASSERT(strtod_diglim >= 18); 18 == one more than the */ + /* minimum number of decimal digits to distinguish double values */ + /* in IEEE arithmetic. */ + i = j = 18; + if (i > nd0) { + j += bc.dplen; + } + for(;;) { + if (--j < bc.dp1 && j >= bc.dp0) { + j = bc.dp0 - 1; + } + if (s0[j] != '0') { + break; + } + --i; + } + e += nd - i; + nd = i; + if (nd0 > nd) { + nd0 = nd; + } + if (nd < 9) { /* must recompute y */ + y = 0; + for(i = 0; i < nd0; ++i) { + y = 10*y + s0[i] - '0'; + } + for(j = bc.dp1; i < nd; ++i) { + y = 10*y + s0[j++] - '0'; + } + } +} +#endif +bd0 = s2b(s0, nd0, nd, y, bc.dplen); + +for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) { + bb2 += bbe; + } + else { + bd2 -= bbe; + } + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + bs2++; + } +#endif +#ifdef Avoid_Underflow + Lsb = LSB; + Lsb1 = 0; + j = bbe - bc.scale; + i = j + bbbits - 1; /* logb(rv) */ + j = P + 1 - bbbits; + if (i < Emin) { /* denormal */ + i = Emin - i; + j -= i; + if (i < 32) { + Lsb <<= i; + } + else if (i < 52) { + Lsb1 = Lsb << (i-32); + } + else { + Lsb1 = Exp_mask; + } + } +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) { /* denormal */ + j += P - Emin; + } + else { + j = P + 1 - bbbits; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += bc.scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) { + i = bs2; + } + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) { + bb = lshift(bb, bb2); + } + if (bd5 > 0) { + bd = pow5mult(bd, bd5); + } + if (bd2 > 0) { + bd = lshift(bd, bd2); + } + if (bs2 > 0) { + bs = lshift(bs, bs2); + } + delta = diff(bb, bd); + bc.dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifndef NO_STRTOD_BIGCOMP /*{*/ + if (bc.nd > nd && i <= 0) { + if (bc.dsign) { + /* Must use bigcomp(). */ + req_bigcomp = 1; + break; + } +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + if (i < 0) { + req_bigcomp = 1; + break; + } + } + else +#endif + i = -1; /* Discarded digits make delta smaller. */ + } +#endif /*}*/ +#ifdef Honor_FLT_ROUNDS /*{*/ + if (bc.rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + if (bc.rounding) { + if (bc.dsign) { + adj.d = 1.; + goto apply_adj; + } + } + else if (!bc.dsign) { + adj.d = -1.; + if (!word1(&rv) + && !(word0(&rv) & Frac_mask)) { + y = word0(&rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!bc.scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) { + adj.d = -0.5; + } + } + } +apply_adj: +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) + <= 2*P*Exp_msk1) { + word0(&adj) += (2*P+1)*Exp_msk1 - y; + } +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= + P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&rv) += adj.d*ulp(dval(&rv)); + word0(&rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + dval(&rv) += adj.d*ulp(&rv); + } + break; + } + adj.d = ratio(delta, bs); + if (adj.d < 1.) { + adj.d = 1.; + } + if (adj.d <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj.d; + if (y != adj.d) { + if (!((bc.rounding>>1) ^ bc.dsign)) { + y++; + } + adj.d = y; + } + } +#ifdef Avoid_Underflow /*{*/ + if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) { + word0(&adj) += (2*P+1)*Exp_msk1 - y; + } +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + adj.d *= ulp(dval(&rv)); + if (bc.dsign) { + dval(&rv) += adj.d; + } + else { + dval(&rv) -= adj.d; + } + word0(&rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow}*/ + adj.d *= ulp(&rv); + if (bc.dsign) { + if (word0(&rv) == Big0 && word1(&rv) == Big1) { + goto ovfl; + } + dval(&rv) += adj.d; + } + else { + dval(&rv) -= adj.d; + } + goto cont; + } +#endif /*}Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask +#ifdef IEEE_Arith /*{*/ +#ifdef Avoid_Underflow + || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(&rv) & Exp_mask) <= Exp_msk1 +#endif +#endif /*}*/ + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) { + bc.inexact = 0; + } +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) { + goto drop_down; + } + break; + } + if (i == 0) { + /* exactly half-way between */ + if (bc.dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( +#ifdef Avoid_Underflow + (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + if (word0(&rv) == Big0 && word1(&rv) == Big1) { + goto ovfl; + } + word0(&rv) = (word0(&rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(&rv) = 0; +#ifdef Avoid_Underflow + bc.dsign = 0; +#endif + break; + } + } + else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { +drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(&rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (bc.scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + { + break; + } + /* rv = smallest denormal */ + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(&rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else +#ifndef NO_STRTOD_BIGCOMP + if (bc.nd > nd) { + goto cont; + } +#endif + break; +#endif + } +#ifndef ROUND_BIASED +#ifdef Avoid_Underflow + if (Lsb1) { + if (!(word0(&rv) & Lsb1)) { + break; + } + } + else if (!(word1(&rv) & Lsb)) { + break; + } +#else + if (!(word1(&rv) & LSB)) { + break; + } +#endif +#endif + if (bc.dsign) +#ifdef Avoid_Underflow + dval(&rv) += sulp(&rv, &bc); +#else + dval(&rv) += ulp(&rv); +#endif +#ifndef ROUND_BIASED + else { +#ifdef Avoid_Underflow + dval(&rv) -= sulp(&rv, &bc); +#else + dval(&rv) -= ulp(&rv); +#endif +#ifndef Sudden_Underflow + if (!dval(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + } +#ifdef Avoid_Underflow + bc.dsign = 1 - bc.dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (bc.dsign) { + aadj = aadj1 = 1.; + } + else if (word1(&rv) || word0(&rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(&rv) == Tiny1 && !word0(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) { + aadj = 1./FLT_RADIX; + } + else { + aadj *= 0.5; + } + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = bc.dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(bc.rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) { + aadj1 += 0.5; + } +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if ((word0(&rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) { + goto ovfl; + } + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + else { + word0(&rv) += P*Exp_msk1; + } + } + else { +#ifdef Avoid_Underflow + if (bc.scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) { + z = 1; + } + aadj = z; + aadj1 = bc.dsign ? aadj : -aadj; + } + dval(&aadj2) = aadj1; + word0(&aadj2) += (2*P+1)*Exp_msk1 - y; + aadj1 = dval(&aadj2); + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if (rv.d == 0.) +#ifdef NO_STRTOD_BIGCOMP + goto undfl; +#else + { + if (bc.nd > nd) { + bc.dsign = 1; + } + break; + } +#endif + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + dval(&rv0) = dval(&rv); + word0(&rv) += P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#ifdef IBM + if ((word0(&rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(&rv0) == Tiny0 + && word1(&rv0) == Tiny1) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + goto cont; + } + else { + word0(&rv) -= P*Exp_msk1; + } + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!bc.dsign) { + aadj1 = -aadj1; + } + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(&rv) & Exp_mask; +#ifndef SET_INEXACT + if (bc.nd == nd) { +#ifdef Avoid_Underflow + if (!bc.scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) { + break; + } + } + else if (aadj < .4999999/FLT_RADIX) { + break; + } + } + } +#endif +cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); +} +Bfree(bb); +Bfree(bd); +Bfree(bs); +Bfree(bd0); +Bfree(delta); +#ifndef NO_STRTOD_BIGCOMP +if (req_bigcomp) { + bd0 = 0; + bc.e0 += nz1; + bigcomp(&rv, s0, &bc); + y = word0(&rv) & Exp_mask; + if (y == Exp_mask) { + goto ovfl; + } + if (y == 0 && rv.d == 0.) { + goto undfl; + } +} +#endif +#ifdef SET_INEXACT +if (bc.inexact) { + if (!oldinexact) { + word0(&rv0) = Exp_1 + (70 << Exp_shift); + word1(&rv0) = 0; + dval(&rv0) += 1.; + } +} +else if (!oldinexact) { + clear_inexact(); +} +#endif +#ifdef Avoid_Underflow +if (bc.scale) { + word0(&rv0) = Exp_1 - 2*P*Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ +#ifdef IEEE_Arith + if (!(word0(&rv) & Exp_mask)) +#else + if (word0(&rv) == 0 && word1(&rv) == 0) +#endif + errno = ERANGE; +#endif +} +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT +if (bc.inexact && !(word0(&rv) & Exp_mask)) { + /* set underflow bit */ + dval(&rv0) = 1e-300; + dval(&rv0) *= dval(&rv0); +} +#endif +ret: +if (se) { + *se = (char *)s; +} +return sign ? -dval(&rv) : dval(&rv); +} + +#ifndef MULTIPLE_THREADS +static char *dtoa_result; +#endif + +static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; + j <<= 1) { + k++; + } + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); +} + +static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(const char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++)) { + t++; + } + if (rve) { + *rve = t; + } + return rv; +} + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + +void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) { + dtoa_result = 0; + } +#endif +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +char * +dtoa +#ifdef KR_headers +(dd, mode, ndigits, decpt, sign, rve) +double dd; int mode, ndigits, *decpt, *sign; char **rve; +#else +(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d2, eps, u; + double ds; + char *s, *s0; +#ifndef No_leftright +#ifdef IEEE_Arith + U eps1; +#endif +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + u.d = dd; + if (word0(&u) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&u) &= ~Sign_bit; /* clear sign bit */ + } + else { + *sign = 0; + } + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(&u) & Exp_mask) == Exp_mask) +#else + if (word0(&u) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(&u) && !(word0(&u) & 0xfffff)) { + return nrv_alloc("Infinity", rve, 8); + } +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(&u) += 0; /* normalize */ +#endif + if (!dval(&u)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) { + Rounding = Rounding == 2 ? 0 : 2; + } + else if (Rounding != 2) { + Rounding = 0; + } + } +#endif + + b = d2b(&u, &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) { + dval(&d2) /= 1 << j; + } +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; +} +else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; +} +#endif +ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; +k = (int)ds; +if (ds < 0. && ds != k) { + k--; /* want k = floor(ds) */ +} +k_check = 1; +if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) { + k--; + } + k_check = 0; +} +j = bbits - i - 1; +if (j >= 0) { + b2 = 0; + s2 = j; +} +else { + b2 = -j; + s2 = 0; +} +if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; +} +else { + b2 -= k; + b5 = -k; + s5 = 0; +} +if (mode < 0 || mode > 9) { + mode = 0; +} + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS +try_quick = Rounding == 1; +#else +try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + +if (mode > 5) { + mode -= 4; + try_quick = 0; +} +leftright = 1; +ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ +/* silence erroneous "gcc -Wall" warning. */ +switch(mode) { +case 0: +case 1: + i = 18; + ndigits = 0; + break; +case 2: + leftright = 0; +/* no break */ +case 4: + if (ndigits <= 0) { + ndigits = 1; + } + ilim = ilim1 = i = ndigits; + break; +case 3: + leftright = 0; +/* no break */ +case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) { + i = 1; + } +} +s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS +if (mode > 1 && Rounding != 1) { + leftright = 0; +} +#endif + +if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&u) /= ds; + } + else if ((j1 = -k)) { + dval(&u) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) { + goto fast_failed; + } + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&u) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) { + goto one_digit; + } + if (dval(&u) < -dval(&eps)) { + goto no_digits; + } + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); +#ifdef IEEE_Arith + if (k0 < 0 && j1 >= 307) { + eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ + word0(&eps1) -= Exp_msk1 * (Bias+P-1); + dval(&eps1) *= tens[j1 & 0xf]; + for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) + if (j & 1) { + dval(&eps1) *= bigtens[i]; + } + if (eps.d < eps1.d) { + eps.d = eps1.d; + } + } +#endif + for(i = 0;;) { + L = dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (1. - dval(&u) < dval(&eps)) { + goto bump_up; + } + if (dval(&u) < dval(&eps)) { + goto ret1; + } + if (++i >= ilim) { + break; + } + dval(&eps) *= 10.; + dval(&u) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u)); + if (!(dval(&u) -= L)) { + ilim = i; + } + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) { + goto bump_up; + } + else if (dval(&u) < 0.5 - dval(&eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif +fast_failed: + s = s0; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; +} + +/* Do we have a "small" integer? */ + +if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&u) <= 5*ds) { + goto no_digits; + } + goto one_digit; + } + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u) / ds); + dval(&u) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&u) < 0) { + L--; + dval(&u) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&u)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(&u) += dval(&u); +#ifdef ROUND_BIASED + if (dval(&u) >= ds) +#else + if (dval(&u) > ds || (dval(&u) == ds && L & 1)) +#endif + { +bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; +} + +m2 = b2; +m5 = b5; +mhi = mlo = 0; +if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); +} +if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; +} +if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) { + b = pow5mult(b, j); + } + } + else { + b = pow5mult(b, b5); + } +} +S = i2b(1); +if (s5 > 0) { + S = pow5mult(S, s5); +} + +/* Check for special case that d is a normalized power of 2. */ + +spec_case = 0; +if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + ) { + if (!word1(&u) && !(word0(&u) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&u) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } +} + +/* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +i = dshift(S, s2); +b2 += i; +m2 += i; +s2 += i; +if (b2 > 0) { + b = lshift(b, b2); +} +if (s2 > 0) { + S = lshift(S, s2); +} +if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) { + mhi = multadd(mhi, 10, 0); + } + ilim = ilim1; + } +} +if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ +no_digits: + k = -1 - ndigits; + goto ret; + } +one_digit: + *s++ = '1'; + k++; + goto ret; +} +if (leftright) { + if (m2 > 0) { + mhi = lshift(mhi, m2); + } + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;; i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(&u) & 1) +#ifdef Honor_FLT_ROUNDS + && Rounding >= 1 +#endif + ) { + if (dig == '9') { + goto round_9_up; + } + if (j > 0) { + dig++; + } +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) { + inexact = 0; + } +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&u) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); +#ifdef ROUND_BIASED + if (j1 >= 0 /*)*/ +#else + if ((j1 > 0 || (j1 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + } +accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding) { + goto accept_dig; + } +#endif + if (dig == '9') { /* possible if i == 1 */ +round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS +keep_dig: +#endif + *s++ = dig; + if (i == ilim) { + break; + } + b = multadd(b, 10, 0); + if (mlo == mhi) { + mlo = mhi = multadd(mhi, 10, 0); + } + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } +} +else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) { + break; + } + b = multadd(b, 10, 0); + } + +/* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS +switch(Rounding) { +case 0: goto trimzeros; +case 2: goto roundoff; +} +#endif +b = lshift(b, 1); +j = cmp(b, S); +#ifdef ROUND_BIASED +if (j >= 0) +#else +if (j > 0 || (j == 0 && dig & 1)) +#endif +{ +roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; +} +else { +#ifdef Honor_FLT_ROUNDS +trimzeros: +#endif + while(*--s == '0'); + s++; +} +ret: +Bfree(S); +if (mhi) { + if (mlo && mlo != mhi) { + Bfree(mlo); + } + Bfree(mhi); +} +ret1: +#ifdef SET_INEXACT +if (inexact) { + if (!oldinexact) { + word0(&u) = Exp_1 + (70 << Exp_shift); + word1(&u) = 0; + dval(&u) += 1.; + } +} +else if (!oldinexact) { + clear_inexact(); +} +#endif +Bfree(b); +*s = 0; +*decpt = k + 1; +if (rve) { + *rve = s; +} +return s0; +} +#ifdef __cplusplus +} +#endif diff --git a/nsprpub/pr/src/misc/pralarm.c b/nsprpub/pr/src/misc/pralarm.c new file mode 100644 index 0000000000..8130215c24 --- /dev/null +++ b/nsprpub/pr/src/misc/pralarm.c @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/**********************************************************************/ +/******************************* PRALARM ******************************/ +/**********************************************************************/ + +#include "obsolete/pralarm.h" + +struct PRAlarmID { /* typedef'd in pralarm.h */ + PRCList list; /* circular list linkage */ + PRAlarm *alarm; /* back pointer to owning alarm */ + PRPeriodicAlarmFn function; /* function to call for notify */ + void *clientData; /* opaque client context */ + PRIntervalTime period; /* the client defined period */ + PRUint32 rate; /* rate of notification */ + + PRUint32 accumulator; /* keeps track of # notifies */ + PRIntervalTime epoch; /* when timer was started */ + PRIntervalTime nextNotify; /* when we'll next do our thing */ + PRIntervalTime lastNotify; /* when we last did our thing */ +}; + +typedef enum {alarm_active, alarm_inactive} _AlarmState; + +struct PRAlarm { /* typedef'd in pralarm.h */ + PRCList timers; /* base of alarm ids list */ + PRLock *lock; /* lock used to protect data */ + PRCondVar *cond; /* condition that used to wait */ + PRThread *notifier; /* thread to deliver notifies */ + PRAlarmID *current; /* current alarm being served */ + _AlarmState state; /* used to delete the alarm */ +}; + +static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id) +{ + /* + * Puts 'id' back into the sorted list iff it's not NULL. + * Removes the first element from the list and returns it (or NULL). + * List is "assumed" to be short. + * + * NB: Caller is providing locking + */ + PRCList *timer; + PRAlarmID *result = id; + PRIntervalTime now = PR_IntervalNow(); + + if (!PR_CLIST_IS_EMPTY(&alarm->timers)) + { + if (id != NULL) /* have to put this id back in */ + { + PRIntervalTime idDelta = now - id->nextNotify; + timer = alarm->timers.next; + do + { + result = (PRAlarmID*)timer; + if ((PRIntervalTime)(now - result->nextNotify) > idDelta) + { + PR_INSERT_BEFORE(&id->list, &alarm->timers); + break; + } + timer = timer->next; + } while (timer != &alarm->timers); + } + result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers)); + PR_REMOVE_LINK(timer); /* remove it from the list */ + } + + return result; +} /* pr_getNextAlarm */ + +static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID *id) +{ + PRIntervalTime delta; + PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate; + PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate; + + id->accumulator += 1; /* every call advances to next period */ + id->lastNotify = id->nextNotify; /* just keeping track of things */ + id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5); + + delta = id->nextNotify - id->lastNotify; + return delta; +} /* pr_PredictNextNotifyTime */ + +static void PR_CALLBACK pr_alarmNotifier(void *arg) +{ + /* + * This is the root of the notifier thread. There is one such thread + * for each PRAlarm. It may service an arbitrary (though assumed to be + * small) number of alarms using the same thread and structure. It + * continues to run until the alarm is destroyed. + */ + PRAlarmID *id = NULL; + PRAlarm *alarm = (PRAlarm*)arg; + enum {notify, abort, scan} why = scan; + + while (why != abort) + { + PRIntervalTime pause; + + PR_Lock(alarm->lock); + while (why == scan) + { + alarm->current = NULL; /* reset current id */ + if (alarm->state == alarm_inactive) { + why = abort; /* we're toast */ + } + else if (why == scan) /* the dominant case */ + { + id = pr_getNextAlarm(alarm, id); /* even if it's the same */ + if (id == NULL) { /* there are no alarms set */ + (void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT); + } + else + { + pause = id->nextNotify - (PR_IntervalNow() - id->epoch); + if ((PRInt32)pause <= 0) /* is this one's time up? */ + { + why = notify; /* set up to do our thing */ + alarm->current = id; /* id we're about to schedule */ + } + else { + (void)PR_WaitCondVar(alarm->cond, pause); /* dally */ + } + } + } + } + PR_Unlock(alarm->lock); + + if (why == notify) + { + (void)pr_PredictNextNotifyTime(id); + if (!id->function(id, id->clientData, ~pause)) + { + /* + * Notified function decided not to continue. Free + * the alarm id to make sure it doesn't get back on + * the list. + */ + PR_DELETE(id); /* free notifier object */ + id = NULL; /* so it doesn't get back into the list */ + } + why = scan; /* so we can cycle through the loop again */ + } + } + +} /* pr_alarm_notifier */ + +PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm(void) +{ + PRAlarm *alarm = PR_NEWZAP(PRAlarm); + if (alarm != NULL) + { + if ((alarm->lock = PR_NewLock()) == NULL) { + goto done; + } + if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) { + goto done; + } + alarm->state = alarm_active; + PR_INIT_CLIST(&alarm->timers); + alarm->notifier = PR_CreateThread( + PR_USER_THREAD, pr_alarmNotifier, alarm, + PR_GetThreadPriority(PR_GetCurrentThread()), + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + if (alarm->notifier == NULL) { + goto done; + } + } + return alarm; + +done: + if (alarm->cond != NULL) { + PR_DestroyCondVar(alarm->cond); + } + if (alarm->lock != NULL) { + PR_DestroyLock(alarm->lock); + } + PR_DELETE(alarm); + return NULL; +} /* CreateAlarm */ + +PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm *alarm) +{ + PRStatus rv; + + PR_Lock(alarm->lock); + alarm->state = alarm_inactive; + rv = PR_NotifyCondVar(alarm->cond); + PR_Unlock(alarm->lock); + + if (rv == PR_SUCCESS) { + rv = PR_JoinThread(alarm->notifier); + } + if (rv == PR_SUCCESS) + { + PR_DestroyCondVar(alarm->cond); + PR_DestroyLock(alarm->lock); + PR_DELETE(alarm); + } + return rv; +} /* PR_DestroyAlarm */ + +PR_IMPLEMENT(PRAlarmID*) PR_SetAlarm( + PRAlarm *alarm, PRIntervalTime period, PRUint32 rate, + PRPeriodicAlarmFn function, void *clientData) +{ + /* + * Create a new periodic alarm an existing current structure. + * Set up the context and compute the first notify time (immediate). + * Link the new ID into the head of the list (since it's notifying + * immediately). + */ + + PRAlarmID *id = PR_NEWZAP(PRAlarmID); + + if (!id) { + return NULL; + } + + id->alarm = alarm; + PR_INIT_CLIST(&id->list); + id->function = function; + id->clientData = clientData; + id->period = period; + id->rate = rate; + id->epoch = id->nextNotify = PR_IntervalNow(); + (void)pr_PredictNextNotifyTime(id); + + PR_Lock(alarm->lock); + PR_INSERT_BEFORE(&id->list, &alarm->timers); + PR_NotifyCondVar(alarm->cond); + PR_Unlock(alarm->lock); + + return id; +} /* PR_SetAlarm */ + +PR_IMPLEMENT(PRStatus) PR_ResetAlarm( + PRAlarmID *id, PRIntervalTime period, PRUint32 rate) +{ + /* + * Can only be called from within the notify routine. Doesn't + * need locking because it can only be called from within the + * notify routine. + */ + if (id != id->alarm->current) { + return PR_FAILURE; + } + id->period = period; + id->rate = rate; + id->accumulator = 1; + id->epoch = PR_IntervalNow(); + (void)pr_PredictNextNotifyTime(id); + return PR_SUCCESS; +} /* PR_ResetAlarm */ + + + diff --git a/nsprpub/pr/src/misc/pratom.c b/nsprpub/pr/src/misc/pratom.c new file mode 100644 index 0000000000..4f0e3da303 --- /dev/null +++ b/nsprpub/pr/src/misc/pratom.c @@ -0,0 +1,384 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** PR Atomic operations +*/ + + +#include "pratom.h" +#include "primpl.h" + +#include <string.h> + +/* + * The following is a fallback implementation that emulates + * atomic operations for platforms without atomic operations. + * If a platform has atomic operations, it should define the + * macro _PR_HAVE_ATOMIC_OPS, and the following will not be + * compiled in. + */ + +#if !defined(_PR_HAVE_ATOMIC_OPS) + +#if defined(_PR_PTHREADS) +/* + * PR_AtomicDecrement() is used in NSPR's thread-specific data + * destructor. Because thread-specific data destructors may be + * invoked after a PR_Cleanup() call, we need an implementation + * of the atomic routines that doesn't need NSPR to be initialized. + */ + +/* + * We use a set of locks for all the emulated atomic operations. + * By hashing on the address of the integer to be locked the + * contention between multiple threads should be lessened. + * + * The number of atomic locks can be set by the environment variable + * NSPR_ATOMIC_HASH_LOCKS + */ + +/* + * lock counts should be a power of 2 + */ +#define DEFAULT_ATOMIC_LOCKS 16 /* should be in sync with the number of initializers + below */ +#define MAX_ATOMIC_LOCKS (4 * 1024) + +static pthread_mutex_t static_atomic_locks[DEFAULT_ATOMIC_LOCKS] = { + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, + PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER +}; + +#ifdef DEBUG +static PRInt32 static_hash_lock_counts[DEFAULT_ATOMIC_LOCKS]; +static PRInt32 *hash_lock_counts = static_hash_lock_counts; +#endif + +static PRUint32 num_atomic_locks = DEFAULT_ATOMIC_LOCKS; +static pthread_mutex_t *atomic_locks = static_atomic_locks; +static PRUint32 atomic_hash_mask = DEFAULT_ATOMIC_LOCKS - 1; + +#define _PR_HASH_FOR_LOCK(ptr) \ + ((PRUint32) (((PRUptrdiff) (ptr) >> 2) ^ \ + ((PRUptrdiff) (ptr) >> 8)) & \ + atomic_hash_mask) + +void _PR_MD_INIT_ATOMIC() +{ + char *eval; + int index; + + + PR_ASSERT(PR_FloorLog2(MAX_ATOMIC_LOCKS) == + PR_CeilingLog2(MAX_ATOMIC_LOCKS)); + + PR_ASSERT(PR_FloorLog2(DEFAULT_ATOMIC_LOCKS) == + PR_CeilingLog2(DEFAULT_ATOMIC_LOCKS)); + + if (((eval = getenv("NSPR_ATOMIC_HASH_LOCKS")) != NULL) && + ((num_atomic_locks = atoi(eval)) != DEFAULT_ATOMIC_LOCKS)) { + + if (num_atomic_locks > MAX_ATOMIC_LOCKS) { + num_atomic_locks = MAX_ATOMIC_LOCKS; + } + else if (num_atomic_locks < 1) { + num_atomic_locks = 1; + } + else { + num_atomic_locks = PR_FloorLog2(num_atomic_locks); + num_atomic_locks = 1L << num_atomic_locks; + } + atomic_locks = (pthread_mutex_t *) PR_Malloc(sizeof(pthread_mutex_t) * + num_atomic_locks); + if (atomic_locks) { + for (index = 0; index < num_atomic_locks; index++) { + if (pthread_mutex_init(&atomic_locks[index], NULL)) { + PR_DELETE(atomic_locks); + atomic_locks = NULL; + break; + } + } + } +#ifdef DEBUG + if (atomic_locks) { + hash_lock_counts = PR_CALLOC(num_atomic_locks * sizeof(PRInt32)); + if (hash_lock_counts == NULL) { + PR_DELETE(atomic_locks); + atomic_locks = NULL; + } + } +#endif + if (atomic_locks == NULL) { + /* + * Use statically allocated locks + */ + atomic_locks = static_atomic_locks; + num_atomic_locks = DEFAULT_ATOMIC_LOCKS; +#ifdef DEBUG + hash_lock_counts = static_hash_lock_counts; +#endif + } + atomic_hash_mask = num_atomic_locks - 1; + } + PR_ASSERT(PR_FloorLog2(num_atomic_locks) == + PR_CeilingLog2(num_atomic_locks)); +} + +PRInt32 +_PR_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + PRInt32 rv; + PRInt32 idx = _PR_HASH_FOR_LOCK(val); + + pthread_mutex_lock(&atomic_locks[idx]); + rv = ++(*val); +#ifdef DEBUG + hash_lock_counts[idx]++; +#endif + pthread_mutex_unlock(&atomic_locks[idx]); + return rv; +} + +PRInt32 +_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) +{ + PRInt32 rv; + PRInt32 idx = _PR_HASH_FOR_LOCK(ptr); + + pthread_mutex_lock(&atomic_locks[idx]); + rv = ((*ptr) += val); +#ifdef DEBUG + hash_lock_counts[idx]++; +#endif + pthread_mutex_unlock(&atomic_locks[idx]); + return rv; +} + +PRInt32 +_PR_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + PRInt32 rv; + PRInt32 idx = _PR_HASH_FOR_LOCK(val); + + pthread_mutex_lock(&atomic_locks[idx]); + rv = --(*val); +#ifdef DEBUG + hash_lock_counts[idx]++; +#endif + pthread_mutex_unlock(&atomic_locks[idx]); + return rv; +} + +PRInt32 +_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + PRInt32 rv; + PRInt32 idx = _PR_HASH_FOR_LOCK(val); + + pthread_mutex_lock(&atomic_locks[idx]); + rv = *val; + *val = newval; +#ifdef DEBUG + hash_lock_counts[idx]++; +#endif + pthread_mutex_unlock(&atomic_locks[idx]); + return rv; +} +#else /* _PR_PTHREADS */ +/* + * We use a single lock for all the emulated atomic operations. + * The lock contention should be acceptable. + */ +static PRLock *atomic_lock = NULL; +void _PR_MD_INIT_ATOMIC(void) +{ + if (atomic_lock == NULL) { + atomic_lock = PR_NewLock(); + } +} + +PRInt32 +_PR_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_Lock(atomic_lock); + rv = ++(*val); + PR_Unlock(atomic_lock); + return rv; +} + +PRInt32 +_PR_MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_Lock(atomic_lock); + rv = ((*ptr) += val); + PR_Unlock(atomic_lock); + return rv; +} + +PRInt32 +_PR_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_Lock(atomic_lock); + rv = --(*val); + PR_Unlock(atomic_lock); + return rv; +} + +PRInt32 +_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_Lock(atomic_lock); + rv = *val; + *val = newval; + PR_Unlock(atomic_lock); + return rv; +} +#endif /* _PR_PTHREADS */ + +#endif /* !_PR_HAVE_ATOMIC_OPS */ + +void _PR_InitAtomic(void) +{ + _PR_MD_INIT_ATOMIC(); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicIncrement(PRInt32 *val) +{ + return _PR_MD_ATOMIC_INCREMENT(val); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicDecrement(PRInt32 *val) +{ + return _PR_MD_ATOMIC_DECREMENT(val); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicSet(PRInt32 *val, PRInt32 newval) +{ + return _PR_MD_ATOMIC_SET(val, newval); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicAdd(PRInt32 *ptr, PRInt32 val) +{ + return _PR_MD_ATOMIC_ADD(ptr, val); +} +/* + * For platforms, which don't support the CAS (compare-and-swap) instruction + * (or an equivalent), the stack operations are implemented by use of PRLock + */ + +PR_IMPLEMENT(PRStack *) +PR_CreateStack(const char *stack_name) +{ + PRStack *stack; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if ((stack = PR_NEW(PRStack)) == NULL) { + return NULL; + } + if (stack_name) { + stack->prstk_name = (char *) PR_Malloc(strlen(stack_name) + 1); + if (stack->prstk_name == NULL) { + PR_DELETE(stack); + return NULL; + } + strcpy(stack->prstk_name, stack_name); + } else { + stack->prstk_name = NULL; + } + +#ifndef _PR_HAVE_ATOMIC_CAS + stack->prstk_lock = PR_NewLock(); + if (stack->prstk_lock == NULL) { + PR_Free(stack->prstk_name); + PR_DELETE(stack); + return NULL; + } +#endif /* !_PR_HAVE_ATOMIC_CAS */ + + stack->prstk_head.prstk_elem_next = NULL; + + return stack; +} + +PR_IMPLEMENT(PRStatus) +PR_DestroyStack(PRStack *stack) +{ + if (stack->prstk_head.prstk_elem_next != NULL) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return PR_FAILURE; + } + + if (stack->prstk_name) { + PR_Free(stack->prstk_name); + } +#ifndef _PR_HAVE_ATOMIC_CAS + PR_DestroyLock(stack->prstk_lock); +#endif /* !_PR_HAVE_ATOMIC_CAS */ + PR_DELETE(stack); + + return PR_SUCCESS; +} + +#ifndef _PR_HAVE_ATOMIC_CAS + +PR_IMPLEMENT(void) +PR_StackPush(PRStack *stack, PRStackElem *stack_elem) +{ + PR_Lock(stack->prstk_lock); + stack_elem->prstk_elem_next = stack->prstk_head.prstk_elem_next; + stack->prstk_head.prstk_elem_next = stack_elem; + PR_Unlock(stack->prstk_lock); + return; +} + +PR_IMPLEMENT(PRStackElem *) +PR_StackPop(PRStack *stack) +{ + PRStackElem *element; + + PR_Lock(stack->prstk_lock); + element = stack->prstk_head.prstk_elem_next; + if (element != NULL) { + stack->prstk_head.prstk_elem_next = element->prstk_elem_next; + element->prstk_elem_next = NULL; /* debugging aid */ + } + PR_Unlock(stack->prstk_lock); + return element; +} +#endif /* !_PR_HAVE_ATOMIC_CAS */ diff --git a/nsprpub/pr/src/misc/praton.c b/nsprpub/pr/src/misc/praton.c new file mode 100644 index 0000000000..3e729f5879 --- /dev/null +++ b/nsprpub/pr/src/misc/praton.c @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/******************************************************************************* + * The following function pr_inet_aton is based on the BSD function inet_aton + * with some modifications. The license and copyright notices applying to this + * function appear below. Modifications are also according to the license below. + ******************************************************************************/ + +#include "prnetdb.h" + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define XX 127 +static const unsigned char index_hex[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX, + XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +static PRBool _isdigit(char c) { + return c >= '0' && c <= '9'; +} +static PRBool _isxdigit(char c) { + return index_hex[(unsigned char) c] != XX; +} +static PRBool _isspace(char c) { + return c == ' ' || (c >= '\t' && c <= '\r'); +} +#undef XX + +int +pr_inet_aton(const char *cp, PRUint32 *addr) +{ + PRUint32 val; + int base, n; + char c; + PRUint8 parts[4]; + PRUint8 *pp = parts; + int digit; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!_isdigit(c)) { + return (0); + } + val = 0; base = 10; digit = 0; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16, c = *++cp; + } + else { + base = 8; + digit = 1; + } + } + for (;;) { + if (_isdigit(c)) { + if (base == 8 && (c == '8' || c == '9')) { + return (0); + } + val = (val * base) + (c - '0'); + c = *++cp; + digit = 1; + } else if (base == 16 && _isxdigit(c)) { + val = (val << 4) + index_hex[(unsigned char) c]; + c = *++cp; + digit = 1; + } else { + break; + } + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xffU) { + return (0); + } + *pp++ = val; + c = *++cp; + } else { + break; + } + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !_isspace(c)) { + return (0); + } + /* + * Did we get a valid digit? + */ + if (!digit) { + return (0); + } + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + case 1: /*%< a -- 32 bits */ + break; + + case 2: /*%< a.b -- 8.24 bits */ + if (val > 0xffffffU) { + return (0); + } + val |= (unsigned int)parts[0] << 24; + break; + + case 3: /*%< a.b.c -- 8.8.16 bits */ + if (val > 0xffffU) { + return (0); + } + val |= ((unsigned int)parts[0] << 24) | ((unsigned int)parts[1] << 16); + break; + + case 4: /*%< a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xffU) { + return (0); + } + val |= ((unsigned int)parts[0] << 24) | + ((unsigned int)parts[1] << 16) | + ((unsigned int)parts[2] << 8); + break; + } + *addr = PR_htonl(val); + return (1); +} + diff --git a/nsprpub/pr/src/misc/prcountr.c b/nsprpub/pr/src/misc/prcountr.c new file mode 100644 index 0000000000..29d7d4ccbe --- /dev/null +++ b/nsprpub/pr/src/misc/prcountr.c @@ -0,0 +1,483 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** prcountr.c -- NSPR Instrumentation Counters +** +** Implement the interface defined in prcountr.h +** +** Design Notes: +** +** The Counter Facility (CF) has a single anchor: qNameList. +** The anchor is a PRCList. qNameList is a list of links in QName +** structures. From qNameList any QName structure and its +** associated RName structure can be located. +** +** For each QName, a list of RName structures is anchored at +** rnLink in the QName structure. +** +** The counter itself is embedded in the RName structure. +** +** For manipulating the counter database, single lock is used to +** protect the entire list: counterLock. +** +** A PRCounterHandle, defined in prcountr.h, is really a pointer +** to a RName structure. References by PRCounterHandle are +** dead-reconed to the RName structure. The PRCounterHandle is +** "overloaded" for traversing the QName structures; only the +** function PR_FindNextQnameHandle() uses this overloading. +** +** +** ToDo (lth): decide on how to lock or atomically update +** individual counters. Candidates are: the global lock; a lock +** per RName structure; Atomic operations (Note that there are +** not adaquate atomic operations (yet) to achieve this goal). At +** this writing (6/19/98) , the update of the counter variable in +** a QName structure is unprotected. +** +*/ + +#include "prcountr.h" +#include "prclist.h" +#include "prlock.h" +#include "prlog.h" +#include "prmem.h" +#include <string.h> + +/* +** +*/ +typedef struct QName +{ + PRCList link; + PRCList rNameList; + char name[PRCOUNTER_NAME_MAX+1]; +} QName; + +/* +** +*/ +typedef struct RName +{ + PRCList link; + QName *qName; + PRLock *lock; + volatile PRUint32 counter; + char name[PRCOUNTER_NAME_MAX+1]; + char desc[PRCOUNTER_DESC_MAX+1]; +} RName; + + +/* +** Define the Counter Facility database +*/ +static PRLock *counterLock; +static PRCList qNameList; +static PRLogModuleInfo *lm; + +/* +** _PR_CounterInitialize() -- Initialize the Counter Facility +** +*/ +static void _PR_CounterInitialize( void ) +{ + /* + ** This function should be called only once + */ + PR_ASSERT( counterLock == NULL ); + + counterLock = PR_NewLock(); + PR_INIT_CLIST( &qNameList ); + lm = PR_NewLogModule("counters"); + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Initialization complete")); + + return; +} /* end _PR_CounterInitialize() */ + +/* +** PR_CreateCounter() -- Create a counter +** +** ValidateArguments +** Lock +** if (qName not already in database) +** NewQname +** if (rName already in database ) +** Assert +** else NewRname +** NewCounter +** link 'em up +** Unlock +** +*/ +PR_IMPLEMENT(PRCounterHandle) +PR_CreateCounter( + const char *qName, + const char *rName, + const char *description +) +{ + QName *qnp; + RName *rnp; + PRBool matchQname = PR_FALSE; + + /* Self initialize, if necessary */ + if ( counterLock == NULL ) { + _PR_CounterInitialize(); + } + + /* Validate input arguments */ + PR_ASSERT( strlen(qName) <= PRCOUNTER_NAME_MAX ); + PR_ASSERT( strlen(rName) <= PRCOUNTER_NAME_MAX ); + PR_ASSERT( strlen(description) <= PRCOUNTER_DESC_MAX ); + + /* Lock the Facility */ + PR_Lock( counterLock ); + + /* Do we already have a matching QName? */ + if (!PR_CLIST_IS_EMPTY( &qNameList )) + { + qnp = (QName *) PR_LIST_HEAD( &qNameList ); + do { + if ( strcmp(qnp->name, qName) == 0) + { + matchQname = PR_TRUE; + break; + } + qnp = (QName *)PR_NEXT_LINK( &qnp->link ); + } while( qnp != (QName *)&qNameList ); + } + /* + ** If we did not find a matching QName, + ** allocate one and initialize it. + ** link it onto the qNameList. + ** + */ + if ( matchQname != PR_TRUE ) + { + qnp = PR_NEWZAP( QName ); + PR_ASSERT( qnp != NULL ); + PR_INIT_CLIST( &qnp->link ); + PR_INIT_CLIST( &qnp->rNameList ); + strcpy( qnp->name, qName ); + PR_APPEND_LINK( &qnp->link, &qNameList ); + } + + /* Do we already have a matching RName? */ + if (!PR_CLIST_IS_EMPTY( &qnp->rNameList )) + { + rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList ); + do { + /* + ** No duplicate RNames are allowed within a QName + ** + */ + PR_ASSERT( strcmp(rnp->name, rName)); + rnp = (RName *)PR_NEXT_LINK( &rnp->link ); + } while( rnp != (RName *)&qnp->rNameList ); + } + + /* Get a new RName structure; initialize its members */ + rnp = PR_NEWZAP( RName ); + PR_ASSERT( rnp != NULL ); + PR_INIT_CLIST( &rnp->link ); + strcpy( rnp->name, rName ); + strcpy( rnp->desc, description ); + rnp->lock = PR_NewLock(); + if ( rnp->lock == NULL ) + { + PR_ASSERT(0); + } + + PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */ + rnp->qName = qnp; /* point the RName to the QName */ + + /* Unlock the Facility */ + PR_Unlock( counterLock ); + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Create: QName: %s %p, RName: %s %p\n\t", + qName, qnp, rName, rnp )); + + return((PRCounterHandle)rnp); +} /* end PR_CreateCounter() */ + + +/* +** +*/ +PR_IMPLEMENT(void) +PR_DestroyCounter( + PRCounterHandle handle +) +{ + RName *rnp = (RName *)handle; + QName *qnp = rnp->qName; + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting: QName: %s, RName: %s", + qnp->name, rnp->name)); + + /* Lock the Facility */ + PR_Lock( counterLock ); + + /* + ** Remove RName from the list of RNames in QName + ** and free RName + */ + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting RName: %s, %p", + rnp->name, rnp)); + PR_REMOVE_LINK( &rnp->link ); + PR_Free( rnp->lock ); + PR_DELETE( rnp ); + + /* + ** If this is the last RName within QName + ** remove QName from the qNameList and free it + */ + if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) ) + { + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Deleting unused QName: %s, %p", + qnp->name, qnp)); + PR_REMOVE_LINK( &qnp->link ); + PR_DELETE( qnp ); + } + + /* Unlock the Facility */ + PR_Unlock( counterLock ); + return; +} /* end PR_DestroyCounter() */ + +/* +** +*/ +PR_IMPLEMENT(PRCounterHandle) +PR_GetCounterHandleFromName( + const char *qName, + const char *rName +) +{ + const char *qn, *rn, *desc; + PRCounterHandle qh, rh = NULL; + RName *rnp = NULL; + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounterHandleFromName:\n\t" + "QName: %s, RName: %s", qName, rName )); + + qh = PR_FindNextCounterQname( NULL ); + while (qh != NULL) + { + rh = PR_FindNextCounterRname( NULL, qh ); + while ( rh != NULL ) + { + PR_GetCounterNameFromHandle( rh, &qn, &rn, &desc ); + if ( (strcmp( qName, qn ) == 0) + && (strcmp( rName, rn ) == 0 )) + { + rnp = (RName *)rh; + goto foundIt; + } + rh = PR_FindNextCounterRname( rh, qh ); + } + qh = PR_FindNextCounterQname( NULL ); + } + +foundIt: + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp )); + return(rh); +} /* end PR_GetCounterHandleFromName() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_GetCounterNameFromHandle( + PRCounterHandle handle, + const char **qName, + const char **rName, + const char **description +) +{ + RName *rnp = (RName *)handle; + QName *qnp = rnp->qName; + + *qName = qnp->name; + *rName = rnp->name; + *description = rnp->desc; + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterNameFromHandle: " + "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s", + qnp, rnp, qnp->name, rnp->name, rnp->desc )); + + return; +} /* end PR_GetCounterNameFromHandle() */ + + +/* +** +*/ +PR_IMPLEMENT(void) +PR_IncrementCounter( + PRCounterHandle handle +) +{ + PR_Lock(((RName *)handle)->lock); + ((RName *)handle)->counter++; + PR_Unlock(((RName *)handle)->lock); + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Increment: %p, %ld", + handle, ((RName *)handle)->counter )); + + return; +} /* end PR_IncrementCounter() */ + + + +/* +** +*/ +PR_IMPLEMENT(void) +PR_DecrementCounter( + PRCounterHandle handle +) +{ + PR_Lock(((RName *)handle)->lock); + ((RName *)handle)->counter--; + PR_Unlock(((RName *)handle)->lock); + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: Decrement: %p, %ld", + handle, ((RName *)handle)->counter )); + + return; +} /* end PR_DecrementCounter() */ + + +/* +** +*/ +PR_IMPLEMENT(void) +PR_AddToCounter( + PRCounterHandle handle, + PRUint32 value +) +{ + PR_Lock(((RName *)handle)->lock); + ((RName *)handle)->counter += value; + PR_Unlock(((RName *)handle)->lock); + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: AddToCounter: %p, %ld", + handle, ((RName *)handle)->counter )); + + return; +} /* end PR_AddToCounter() */ + + +/* +** +*/ +PR_IMPLEMENT(void) +PR_SubtractFromCounter( + PRCounterHandle handle, + PRUint32 value +) +{ + PR_Lock(((RName *)handle)->lock); + ((RName *)handle)->counter -= value; + PR_Unlock(((RName *)handle)->lock); + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SubtractFromCounter: %p, %ld", + handle, ((RName *)handle)->counter )); + + return; +} /* end PR_SubtractFromCounter() */ + +/* +** +*/ +PR_IMPLEMENT(PRUint32) +PR_GetCounter( + PRCounterHandle handle +) +{ + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetCounter: %p, %ld", + handle, ((RName *)handle)->counter )); + + return(((RName *)handle)->counter); +} /* end PR_GetCounter() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_SetCounter( + PRCounterHandle handle, + PRUint32 value +) +{ + ((RName *)handle)->counter = value; + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: SetCounter: %p, %ld", + handle, ((RName *)handle)->counter )); + + return; +} /* end PR_SetCounter() */ + +/* +** +*/ +PR_IMPLEMENT(PRCounterHandle) +PR_FindNextCounterQname( + PRCounterHandle handle +) +{ + QName *qnp = (QName *)handle; + + if ( PR_CLIST_IS_EMPTY( &qNameList )) { + qnp = NULL; + } + else if ( qnp == NULL ) { + qnp = (QName *)PR_LIST_HEAD( &qNameList ); + } + else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList ) { + qnp = NULL; + } + else { + qnp = (QName *)PR_NEXT_LINK( &qnp->link ); + } + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextQname: Handle: %p, Returns: %p", + handle, qnp )); + + return((PRCounterHandle)qnp); +} /* end PR_FindNextCounterQname() */ + + +/* +** +*/ +PR_IMPLEMENT(PRCounterHandle) +PR_FindNextCounterRname( + PRCounterHandle rhandle, + PRCounterHandle qhandle +) +{ + RName *rnp = (RName *)rhandle; + QName *qnp = (QName *)qhandle; + + + if ( PR_CLIST_IS_EMPTY( &qnp->rNameList )) { + rnp = NULL; + } + else if ( rnp == NULL ) { + rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList ); + } + else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList ) { + rnp = NULL; + } + else { + rnp = (RName *)PR_NEXT_LINK( &rnp->link ); + } + + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p", + rhandle, qhandle, rnp )); + + return((PRCounterHandle)rnp); +} /* end PR_FindNextCounterRname() */ diff --git a/nsprpub/pr/src/misc/prdtoa.c b/nsprpub/pr/src/misc/prdtoa.c new file mode 100644 index 0000000000..51b331bac8 --- /dev/null +++ b/nsprpub/pr/src/misc/prdtoa.c @@ -0,0 +1,3697 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This file is based on the third-party code dtoa.c. We minimize our + * modifications to third-party code to make it easy to merge new versions. + * The author of dtoa.c was not willing to add the parentheses suggested by + * GCC, so we suppress these warnings. + */ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) +#pragma GCC diagnostic ignored "-Wparentheses" +#endif + +#include "primpl.h" +#include "prbit.h" + +#define MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) PR_Lock(dtoa_lock[n]) +#define FREE_DTOA_LOCK(n) PR_Unlock(dtoa_lock[n]) + +static PRLock *dtoa_lock[2]; + +void _PR_InitDtoa(void) +{ + dtoa_lock[0] = PR_NewLock(); + dtoa_lock[1] = PR_NewLock(); +} + +void _PR_CleanupDtoa(void) +{ + PR_DestroyLock(dtoa_lock[0]); + dtoa_lock[0] = NULL; + PR_DestroyLock(dtoa_lock[1]); + dtoa_lock[1] = NULL; + + /* FIXME: deal with freelist and p5s. */ +} + +#if !defined(__ARM_EABI__) \ + && (defined(__arm) || defined(__arm__) || defined(__arm26__) \ + || defined(__arm32__)) +#define IEEE_ARM +#elif defined(IS_LITTLE_ENDIAN) +#define IEEE_8087 +#else +#define IEEE_MC68k +#endif + +#define Long PRInt32 +#define ULong PRUint32 +#define NO_LONG_LONG + +#define No_Hex_NaN + +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define IEEE_ARM for IEEE-arithmetic machines where the two words + * in a double are stored in big endian order but the two shorts + * in a word are still stored in little endian order. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define INFNAN_CHECK on IEEE systems to cause strtod to check for + * Infinity and NaN (case insensitively). On some systems (e.g., + * some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + */ + +#ifndef Long +#define Long long +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif +#ifdef IEEE_ARM +#define IEEE_Arith +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(IEEE_ARM) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, IEEE_ARM, VAX, or IBM should be defined. +#endif + +typedef union { + double d; + ULong L[2]; +} U; + +#define dval(x) (x).d +#ifdef IEEE_8087 +#define word0(x) (x).L[1] +#define word1(x) (x).L[0] +#else +#define word0(x) (x).L[0] +#define word1(x) (x).L[1] +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(IEEE_ARM) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#define Rounding rounding +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 7 + +struct + Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; +}; + +typedef struct Bigint Bigint; + +static Bigint *freelist[Kmax+1]; + +static Bigint * +Balloc +#ifdef KR_headers +(k) int k; +#else +(int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) { + freelist[k] = rv->next; + } + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else { + rv = (Bigint*)MALLOC(len*sizeof(double)); + } +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; +} + +static void +Bfree +#ifdef KR_headers +(v) Bigint *v; +#else +(Bigint *v) +#endif +{ + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } +} + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + +static Bigint * +multadd +#ifdef KR_headers +(b, m, a) Bigint *b; int m, a; +#else +(Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; +} + +static Bigint * +s2b +#ifdef KR_headers +(s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9; +#else +(CONST char *s, int nd0, int nd, ULong y9) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do { + b = multadd(b, 10, *s++ - '0'); + } + while(++i < nd0); + s++; + } + else { + s += 10; + } + for(; i < nd; i++) { + b = multadd(b, 10, *s++ - '0'); + } + return b; +} + +static int +hi0bits +#ifdef KR_headers +(x) register ULong x; +#else +(register ULong x) +#endif +{ +#ifdef PR_HAVE_BUILTIN_BITSCAN32 + return( (!x) ? 32 : pr_bitscan_clz32(x) ); +#else + register int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) { + return 32; + } + } + return k; +#endif /* PR_HAVE_BUILTIN_BITSCAN32 */ +} + +static int +lo0bits +#ifdef KR_headers +(y) ULong *y; +#else +(ULong *y) +#endif +{ +#ifdef PR_HAVE_BUILTIN_BITSCAN32 + int k; + ULong x = *y; + + if (x>1) { + *y = ( x >> (k = pr_bitscan_ctz32(x)) ); + } + else { + k = ((x ^ 1) << 5); + } +#else + register int k; + register ULong x = *y; + + if (x & 7) { + if (x & 1) { + return 0; + } + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) { + return 32; + } + } + *y = x; +#endif /* PR_HAVE_BUILTIN_BITSCAN32 */ + return k; +} + +static Bigint * +i2b +#ifdef KR_headers +(i) int i; +#else +(int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; +} + +static Bigint * +mult +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) { + k++; + } + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) { + *x = 0; + } + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +static Bigint *p5s; + +static Bigint * +pow5mult +#ifdef KR_headers +(b, k) Bigint *b; int k; +#else +(Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if (i = k & 3) { + b = multadd(b, p05[i-1], 0); + } + + if (!(k >>= 2)) { + return b; + } + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) { + break; + } + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; +} + +static Bigint * +lshift +#ifdef KR_headers +(b, k) Bigint *b; int k; +#else +(Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) { + k1++; + } + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) { + *x1++ = 0; + } + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) { + ++n1; + } + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) { + ++n1; + } + } +#endif + else do { + *x1++ = *x++; + } + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; +} + +static int +cmp +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) { + Bug("cmp called with a->x[a->wds-1] == 0"); + } + if (j > 1 && !b->x[j-1]) { + Bug("cmp called with b->x[b->wds-1] == 0"); + } +#endif + if (i -= j) { + return i; + } + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) { + return *xa < *xb ? -1 : 1; + } + if (xa <= xa0) { + break; + } + } + return 0; +} + +static Bigint * +diff +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else { + i = 0; + } + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) { + wa--; + } + c->wds = wa; + return c; +} + +static double +ulp +#ifdef KR_headers +(dx) double dx; +#else +(double dx) +#endif +{ + register Long L; + U x, a; + + dval(x) = dx; + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } + else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(a); +} + +static double +b2d +#ifdef KR_headers +(a, e) Bigint *a; int *e; +#else +(Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) { + Bug("zero y in b2d"); + } +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> Ebits - k; + w = xa > xa0 ? *--xa : 0; + d1 = y << (32-Ebits) + k | w >> Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> 32 - k; + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> 32 - k; + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif +ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(d); +} + +static Bigint * +d2b +#ifdef KR_headers +(dd, e, bits) double dd; int *e, *bits; +#else +(double dd, int *e, int *bits) +#endif +{ + U d; + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; +#endif + + dval(d) = dd; +#ifdef VAX + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if (de = (int)(d0 >> Exp_shift)) { + z |= Exp_msk1; + } +#endif +#ifdef Pack_32 + if (y = d1) { + if (k = lo0bits(&y)) { + x[0] = y | z << 32 - k; + z >>= k; + } + else { + x[0] = y; + } +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) { + Bug("Zero passed to d2b"); + } +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) { + --i; + } + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; +} +#undef d0 +#undef d1 + +static double +ratio +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(da) = b2d(a, &ka); + dval(db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) { + dval(da) *= 1 << k; + } + } + else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) { + dval(db) *= 1 << k; + } + } +#else + if (k > 0) { + word0(da) += k*Exp_msk1; + } + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return dval(da) / dval(db); +} + +static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif +}; + +static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-53 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#ifndef IEEE_Arith +#undef INFNAN_CHECK +#endif + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + +static int +match +#ifdef KR_headers +(sp, t) char **sp, *t; +#else +(CONST char **sp, char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while(d = *t++) { + if ((c = *++s) >= 'A' && c <= 'Z') { + c += 'a' - 'A'; + } + if (c != d) { + return 0; + } + } + *sp = s + 1; + return 1; +} + +#ifndef No_Hex_NaN +static void +hexnan +#ifdef KR_headers +(rvp, sp) double *rvp; CONST char **sp; +#else +(double *rvp, CONST char **sp) +#endif +{ + ULong c, x[2]; + CONST char *s; + int havedig, udx0, xshift; + + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + while(c = *(CONST unsigned char*)++s) { + if (c >= '0' && c <= '9') { + c -= '0'; + } + else if (c >= 'a' && c <= 'f') { + c += 10 - 'a'; + } + else if (c >= 'A' && c <= 'F') { + c += 10 - 'A'; + } + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else { + return; /* invalid form: don't change *sp */ + } + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) { + x[0] = (x[0] << 4) | (x[1] >> 28); + } + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(*rvp) = Exp_mask | x[0]; + word1(*rvp) = x[1]; + } +} +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + +PR_IMPLEMENT(double) +PR_strtod +#ifdef KR_headers +(s00, se) CONST char *s00; char **se; +#else +(CONST char *s00, char **se) +#endif +{ +#ifdef Avoid_Underflow + int scale; +#endif + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1, adj; + U aadj2, rv, rv0; + Long L; + ULong y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef USE_LOCALE + CONST char *s2; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + sign = nz0 = nz = 0; + dval(rv) = 0.; + for(s = s00;; s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) { + goto break2; + } + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } +break2: + if (*s == '0') { + nz0 = 1; + while(*++s == '0') ; + if (!*s) { + goto ret; + } + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) { + y = 10*y + c - '0'; + } + else if (nd < 16) { + z = 10*z + c - '0'; + } + nd0 = nd; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + if (!nd) { + for(; c == '0'; c = *++s) { + nz++; + } + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { +have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) { + y *= 10; + } + else if (nd <= DBL_DIG + 1) { + z *= 10; + } + if (nd++ < 9) { + y = 10*y + c; + } + else if (nd <= DBL_DIG + 1) { + z = 10*z + c; + } + nz = 0; + } + } + } +dig_done: + if (nd > 64 * 1024) { + goto ret0; + } + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') { + c = *++s; + } + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') { + L = 10*L + c - '0'; + } + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + { + e = 19999; /* safe for 16 bit ints */ + } + else { + e = (int)L; + } + if (esign) { + e = -e; + } + } + else { + e = 0; + } + } + else { + s = s00; + } + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) { + ++s; + } + word0(rv) = 0x7ff00000; + word1(rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(rv) = NAN_WORD0; + word1(rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') { /*)*/ + hexnan(&rv, &s); + } +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ +ret0: + s = s00; + sign = 0; + } + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) { + nd0 = nd; + } + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) { + oldinexact = get_inexact(); + } +#endif + dval(rv) = tens[k - 9] * dval(rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) { + goto ret; + } + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + e -= i; + dval(rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ +vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(rv), tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + goto ovfl; + } + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv = -rv; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(rv), tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + inexact = 1; + if (k <= DBL_DIG) { + oldinexact = get_inexact(); + } +#endif +#ifdef Avoid_Underflow + scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (sign) { + rounding = rounding == 2 ? 0 : 2; + } + else if (rounding != 2) { + rounding = 0; + } + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if (i = e1 & 15) { + dval(rv) *= tens[i]; + } + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { +ovfl: +#ifndef NO_ERRNO + PR_SetError(PR_RANGE_ERROR, 0); +#endif + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(rv) = Big0; + word1(rv) = Big1; + break; + default: + word0(rv) = Exp_mask; + word1(rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(rv) = Exp_mask; + word1(rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(rv0) = 1e300; + dval(rv0) *= dval(rv0); +#endif +#else /*IEEE_Arith*/ + word0(rv) = Big0; + word1(rv) = Big1; +#endif /*IEEE_Arith*/ + if (bd0) { + goto retfree; + } + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) { + dval(rv) *= bigtens[j]; + } + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + dval(rv) *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + goto ovfl; + } + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else { + word0(rv) += P*Exp_msk1; + } + } + } + else if (e1 < 0) { + e1 = -e1; + if (i = e1 & 15) { + dval(rv) /= tens[i]; + } + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) { + goto undfl; + } +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) { + scale = 2*P; + } + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) { + dval(rv) *= tinytens[j]; + } + if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + word1(rv) = 0; + if (j >= 53) { + word0(rv) = (P+2)*Exp_msk1; + } + else { + word0(rv) &= 0xffffffff << j-32; + } + } + else { + word1(rv) &= 0xffffffff << j; + } + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) { + dval(rv) *= tinytens[j]; + } + /* The last multiplication could underflow. */ + dval(rv0) = dval(rv); + dval(rv) *= tinytens[j]; + if (!dval(rv)) { + dval(rv) = 2.*dval(rv0); + dval(rv) *= tinytens[j]; +#endif + if (!dval(rv)) { +undfl: + dval(rv) = 0.; +#ifndef NO_ERRNO + PR_SetError(PR_RANGE_ERROR, 0); +#endif + if (bd0) { + goto retfree; + } + goto ret; + } +#ifndef Avoid_Underflow + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } +} + +/* Now the hard part -- adjusting rv to the correct value.*/ + +/* Put digits into bd: true value = bd * 10^e */ + +bd0 = s2b(s0, nd0, nd, y); + +for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) { + bb2 += bbe; + } + else { + bd2 -= bbe; + } + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) { + bs2++; + } +#endif +#ifdef Avoid_Underflow + j = bbe - scale; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) { /* denormal */ + j += P - Emin; + } + else { + j = P + 1 - bbbits; + } +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) { /* denormal */ + j += P - Emin; + } + else { + j = P + 1 - bbbits; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) { + i = bs2; + } + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) { + bb = lshift(bb, bb2); + } + if (bd5 > 0) { + bd = pow5mult(bd, bd5); + } + if (bd2 > 0) { + bd = lshift(bd, bd2); + } + if (bs2 > 0) { + bs = lshift(bs, bs2); + } + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifdef Honor_FLT_ROUNDS + if (rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (rounding) { + if (dsign) { + adj = 1.; + goto apply_adj; + } + } + else if (!dsign) { + adj = -1.; + if (!word1(rv) + && !(word0(rv) & Frac_mask)) { + y = word0(rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) { + adj = -0.5; + } + } + } +apply_adj: +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) + <= 2*P*Exp_msk1) { + word0(adj) += (2*P+1)*Exp_msk1 - y; + } +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= + P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + dval(rv) += adj*ulp(dval(rv)); + word0(rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(rv) += adj*ulp(dval(rv)); + } + break; + } + adj = ratio(delta, bs); + if (adj < 1.) { + adj = 1.; + } + if (adj <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj; + if (y != adj) { + if (!((rounding>>1) ^ dsign)) { + y++; + } + adj = y; + } + } +#ifdef Avoid_Underflow + if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) { + word0(adj) += (2*P+1)*Exp_msk1 - y; + } +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + word0(rv) += P*Exp_msk1; + adj *= ulp(dval(rv)); + if (dsign) { + dval(rv) += adj; + } + else { + dval(rv) -= adj; + } + word0(rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + adj *= ulp(dval(rv)); + if (dsign) { + dval(rv) += adj; + } + else { + dval(rv) -= adj; + } + goto cont; + } +#endif /*Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(rv) & Exp_mask) <= Exp_msk1 +#endif +#endif + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) { + inexact = 0; + } +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) { + goto drop_down; + } + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == ( +#ifdef Avoid_Underflow + (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { +drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + goto undfl; + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (scale) { + L = word0(rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + { + break; + } + /* rv = smallest denormal */ + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) { + break; + } +#endif + if (dsign) { + dval(rv) += ulp(dval(rv)); + } +#ifndef ROUND_BIASED + else { + dval(rv) -= ulp(dval(rv)); +#ifndef Sudden_Underflow + if (!dval(rv)) { + goto undfl; + } +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) { + aadj = aadj1 = 1.; + } + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) { + goto undfl; + } +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) { + aadj = 1./FLT_RADIX; + } + else { + aadj *= 0.5; + } + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(Rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) { + aadj1 += 0.5; + } +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(rv0) = dval(rv); + word0(rv) -= P*Exp_msk1; + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) { + goto ovfl; + } + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } + else { + word0(rv) += P*Exp_msk1; + } + } + else { +#ifdef Avoid_Underflow + if (scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) { + z = 1; + } + aadj = z; + aadj1 = dsign ? aadj : -aadj; + } + dval(aadj2) = aadj1; + word0(aadj2) += (2*P+1)*Exp_msk1 - y; + aadj1 = dval(aadj2); + } + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#else +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + dval(rv0) = dval(rv); + word0(rv) += P*Exp_msk1; + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) { + goto undfl; + } + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } + else { + word0(rv) -= P*Exp_msk1; + } + } + else { + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!dsign) { + aadj1 = -aadj1; + } + } + adj = aadj1 * ulp(dval(rv)); + dval(rv) += adj; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(rv) & Exp_mask; +#ifndef SET_INEXACT +#ifdef Avoid_Underflow + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) { + break; + } + } + else if (aadj < .4999999/FLT_RADIX) { + break; + } + } +#endif +cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); +} +#ifdef SET_INEXACT +if (inexact) { + if (!oldinexact) { + word0(rv0) = Exp_1 + (70 << Exp_shift); + word1(rv0) = 0; + dval(rv0) += 1.; + } +} +else if (!oldinexact) { + clear_inexact(); +} +#endif +#ifdef Avoid_Underflow +if (scale) { + word0(rv0) = Exp_1 - 2*P*Exp_msk1; + word1(rv0) = 0; + dval(rv) *= dval(rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ + if (word0(rv) == 0 && word1(rv) == 0) { + PR_SetError(PR_RANGE_ERROR, 0); + } +#endif +} +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT +if (inexact && !(word0(rv) & Exp_mask)) { + /* set underflow bit */ + dval(rv0) = 1e-300; + dval(rv0) *= dval(rv0); +} +#endif +retfree: +Bfree(bb); +Bfree(bd); +Bfree(bs); +Bfree(bd0); +Bfree(delta); +ret: +if (se) { + *se = (char *)s; +} +return sign ? -dval(rv) : dval(rv); +} + +static int +quorem +#ifdef KR_headers +(b, S) Bigint *b, *S; +#else +(Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/{ + Bug("oversize b in quorem"); + } +#endif + if (b->wds < n) { + return 0; + } + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/{ + Bug("oversized quotient in quorem"); + } +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) { + --n; + } + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) { + --n; + } + b->wds = n; + } + } + return q; +} + +#ifndef MULTIPLE_THREADS +static char *dtoa_result; +#endif + +static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; + j <<= 1) { + k++; + } + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); +} + +static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while(*t = *s++) { + t++; + } + if (rve) { + *rve = t; + } + return rv; +} + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + +static void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) { + dtoa_result = 0; + } +#endif +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +static char * +dtoa +#ifdef KR_headers +(dd, mode, ndigits, decpt, sign, rve) +double dd; int mode, ndigits, *decpt, *sign; char **rve; +#else +(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d, d2, eps; + double ds; + char *s, *s0; +#ifdef Honor_FLT_ROUNDS + int rounding; +#endif +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + dval(d) = dd; + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else { + *sign = 0; + } + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(d) && !(word0(d) & 0xfffff)) { + return nrv_alloc("Infinity", rve, 8); + } +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(d) += 0; /* normalize */ +#endif + if (!dval(d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if ((rounding = Flt_Rounds) >= 2) { + if (*sign) { + rounding = rounding == 2 ? 0 : 2; + } + else if (rounding != 2) { + rounding = 0; + } + } +#endif + + b = d2b(dval(d), &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if (i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) { +#endif + dval(d2) = dval(d); + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) { + dval(d2) /= 1 << j; + } +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; +} +else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << 64 - i | word1(d) >> i - 32 + : word1(d) << 32 - i; + dval(d2) = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; +} +#endif +ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; +k = (int)ds; +if (ds < 0. && ds != k) { + k--; /* want k = floor(ds) */ +} +k_check = 1; +if (k >= 0 && k <= Ten_pmax) { + if (dval(d) < tens[k]) { + k--; + } + k_check = 0; +} +j = bbits - i - 1; +if (j >= 0) { + b2 = 0; + s2 = j; +} +else { + b2 = -j; + s2 = 0; +} +if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; +} +else { + b2 -= k; + b5 = -k; + s5 = 0; +} +if (mode < 0 || mode > 9) { + mode = 0; +} + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS +try_quick = Rounding == 1; +#else +try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + +if (mode > 5) { + mode -= 4; + try_quick = 0; +} +leftright = 1; +switch(mode) { +case 0: +case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; +case 2: + leftright = 0; +/* no break */ +case 4: + if (ndigits <= 0) { + ndigits = 1; + } + ilim = ilim1 = i = ndigits; + break; +case 3: + leftright = 0; +/* no break */ +case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) { + i = 1; + } +} +s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS +if (mode > 1 && rounding != 1) { + leftright = 0; +} +#endif + +if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(d2) = dval(d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(d) /= ds; + } + else if (j1 = -k) { + dval(d) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(d) *= bigtens[i]; + } + } + if (k_check && dval(d) < 1. && ilim > 0) { + if (ilim1 <= 0) { + goto fast_failed; + } + ilim = ilim1; + k--; + dval(d) *= 10.; + ieps++; + } + dval(eps) = ieps*dval(d) + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(d) -= 5.; + if (dval(d) > dval(eps)) { + goto one_digit; + } + if (dval(d) < -dval(eps)) { + goto no_digits; + } + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(eps) = 0.5/tens[ilim-1] - dval(eps); + for(i = 0;;) { + L = dval(d); + dval(d) -= L; + *s++ = '0' + (int)L; + if (dval(d) < dval(eps)) { + goto ret1; + } + if (1. - dval(d) < dval(eps)) { + goto bump_up; + } + if (++i >= ilim) { + break; + } + dval(eps) *= 10.; + dval(d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(d) *= 10.) { + L = (Long)(dval(d)); + if (!(dval(d) -= L)) { + ilim = i; + } + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(d) > 0.5 + dval(eps)) { + goto bump_up; + } + else if (dval(d) < 0.5 - dval(eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif +fast_failed: + s = s0; + dval(d) = dval(d2); + k = k0; + ilim = ilim0; +} + +/* Do we have a "small" integer? */ + +if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(d) <= 5*ds) { + goto no_digits; + } + goto one_digit; + } + for(i = 1; i <= k+1; i++, dval(d) *= 10.) { + L = (Long)(dval(d) / ds); + dval(d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(d) < 0) { + L--; + dval(d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(d)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(d) += dval(d); + if (dval(d) > ds || dval(d) == ds && L & 1) { +bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; +} + +m2 = b2; +m5 = b5; +mhi = mlo = 0; +if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); +} +if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; +} +if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if (j = b5 - m5) { + b = pow5mult(b, j); + } + } + else { + b = pow5mult(b, b5); + } +} +S = i2b(1); +if (s5 > 0) { + S = pow5mult(S, s5); +} + +/* Check for special case that d is a normalized power of 2. */ + +spec_case = 0; +if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && rounding == 1 +#endif + ) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } +} + +/* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 +if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) { + i = 32 - i; +} +#else +if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) { + i = 16 - i; +} +#endif +if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; +} +else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; +} +if (b2 > 0) { + b = lshift(b, b2); +} +if (s2 > 0) { + S = lshift(S, s2); +} +if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) { + mhi = multadd(mhi, 10, 0); + } + ilim = ilim1; + } +} +if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ +no_digits: + k = -1 - ndigits; + goto ret; + } +one_digit: + *s++ = '1'; + k++; + goto ret; +} +if (leftright) { + if (m2 > 0) { + mhi = lshift(mhi, m2); + } + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;; i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(d) & 1) +#ifdef Honor_FLT_ROUNDS + && rounding >= 1 +#endif + ) { + if (dig == '9') { + goto round_9_up; + } + if (j > 0) { + dig++; + } +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) { + inexact = 0; + } +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + ) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || j1 == 0 && dig & 1) + && dig++ == '9') { + goto round_9_up; + } + } +accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!rounding) { + goto accept_dig; + } +#endif + if (dig == '9') { /* possible if i == 1 */ +round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS +keep_dig: +#endif + *s++ = dig; + if (i == ilim) { + break; + } + b = multadd(b, 10, 0); + if (mlo == mhi) { + mlo = mhi = multadd(mhi, 10, 0); + } + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } +} +else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) { + break; + } + b = multadd(b, 10, 0); + } + +/* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS +switch(rounding) { +case 0: goto trimzeros; +case 2: goto roundoff; +} +#endif +b = lshift(b, 1); +j = cmp(b, S); +if (j > 0 || j == 0 && dig & 1) { +roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; +} +else { +#ifdef Honor_FLT_ROUNDS +trimzeros: +#endif + while(*--s == '0'); + s++; +} +ret: +Bfree(S); +if (mhi) { + if (mlo && mlo != mhi) { + Bfree(mlo); + } + Bfree(mhi); +} +ret1: +#ifdef SET_INEXACT +if (inexact) { + if (!oldinexact) { + word0(d) = Exp_1 + (70 << Exp_shift); + word1(d) = 0; + dval(d) += 1.; + } +} +else if (!oldinexact) { + clear_inexact(); +} +#endif +Bfree(b); +*s = 0; +*decpt = k + 1; +if (rve) { + *rve = s; +} +return s0; +} +#ifdef __cplusplus +} +#endif + +PR_IMPLEMENT(PRStatus) +PR_dtoa(PRFloat64 d, PRIntn mode, PRIntn ndigits, + PRIntn *decpt, PRIntn *sign, char **rve, char *buf, PRSize bufsize) +{ + char *result; + PRSize resultlen; + PRStatus rv = PR_FAILURE; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (mode < 0 || mode > 3) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return rv; + } + result = dtoa(d, mode, ndigits, decpt, sign, rve); + if (!result) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return rv; + } + resultlen = strlen(result)+1; + if (bufsize < resultlen) { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + } else { + memcpy(buf, result, resultlen); + if (rve) { + *rve = buf + (*rve - result); + } + rv = PR_SUCCESS; + } + freedtoa(result); + return rv; +} + +/* +** conversion routines for floating point +** prcsn - number of digits of precision to generate floating +** point value. +** This should be reparameterized so that you can send in a +** prcn for the positive and negative ranges. For now, +** conform to the ECMA JavaScript spec which says numbers +** less than 1e-6 are in scientific notation. +** Also, the ECMA spec says that there should always be a +** '+' or '-' after the 'e' in scientific notation +*/ +PR_IMPLEMENT(void) +PR_cnvtf(char *buf, int bufsz, int prcsn, double dfval) +{ + PRIntn decpt, sign, numdigits; + char *num, *nump; + char *bufp = buf; + char *endnum; + U fval; + + dval(fval) = dfval; + /* If anything fails, we store an empty string in 'buf' */ + num = (char*)PR_MALLOC(bufsz); + if (num == NULL) { + buf[0] = '\0'; + return; + } + /* XXX Why use mode 1? */ + if (PR_dtoa(dval(fval),1,prcsn,&decpt,&sign,&endnum,num,bufsz) + == PR_FAILURE) { + buf[0] = '\0'; + goto done; + } + numdigits = endnum - num; + nump = num; + + if (sign && + !(word0(fval) == Sign_bit && word1(fval) == 0) && + !((word0(fval) & Exp_mask) == Exp_mask && + (word1(fval) || (word0(fval) & 0xfffff)))) { + *bufp++ = '-'; + } + + if (decpt == 9999) { + while ((*bufp++ = *nump++) != 0) {} /* nothing to execute */ + goto done; + } + + if (decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5) { + *bufp++ = *nump++; + if (numdigits != 1) { + *bufp++ = '.'; + } + + while (*nump != '\0') { + *bufp++ = *nump++; + } + *bufp++ = 'e'; + PR_snprintf(bufp, bufsz - (bufp - buf), "%+d", decpt-1); + } else if (decpt >= 0) { + if (decpt == 0) { + *bufp++ = '0'; + } else { + while (decpt--) { + if (*nump != '\0') { + *bufp++ = *nump++; + } else { + *bufp++ = '0'; + } + } + } + if (*nump != '\0') { + *bufp++ = '.'; + while (*nump != '\0') { + *bufp++ = *nump++; + } + } + *bufp++ = '\0'; + } else if (decpt < 0) { + *bufp++ = '0'; + *bufp++ = '.'; + while (decpt++) { + *bufp++ = '0'; + } + + while (*nump != '\0') { + *bufp++ = *nump++; + } + *bufp++ = '\0'; + } +done: + PR_DELETE(num); +} diff --git a/nsprpub/pr/src/misc/prenv.c b/nsprpub/pr/src/misc/prenv.c new file mode 100644 index 0000000000..b057a1c89a --- /dev/null +++ b/nsprpub/pr/src/misc/prenv.c @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <string.h> +#include <stdlib.h> +#include "primpl.h" +#include "prmem.h" + +#if defined(XP_UNIX) +#include <unistd.h> +#if defined(DARWIN) +#if defined(HAVE_CRT_EXTERNS_H) +#include <crt_externs.h> +#endif /* HAVE_CRT_EXTERNS_H */ +#else /* DARWIN */ +PR_IMPORT_DATA(char **) environ; +#endif /* DARWIN */ +#endif /* XP_UNIX */ + +#if !defined(HAVE_SECURE_GETENV) && defined(HAVE___SECURE_GETENV) +#define secure_getenv __secure_getenv +#define HAVE_SECURE_GETENV 1 +#endif + +/* Lock used to lock the environment */ +#if defined(_PR_NO_PREEMPT) +#define _PR_NEW_LOCK_ENV() +#define _PR_DELETE_LOCK_ENV() +#define _PR_LOCK_ENV() +#define _PR_UNLOCK_ENV() +#elif defined(_PR_LOCAL_THREADS_ONLY) +extern _PRCPU * _pr_primordialCPU; +static PRIntn _is; +#define _PR_NEW_LOCK_ENV() +#define _PR_DELETE_LOCK_ENV() +#define _PR_LOCK_ENV() if (_pr_primordialCPU) _PR_INTSOFF(_is); +#define _PR_UNLOCK_ENV() if (_pr_primordialCPU) _PR_INTSON(_is); +#else +static PRLock *_pr_envLock = NULL; +#define _PR_NEW_LOCK_ENV() {_pr_envLock = PR_NewLock();} +#define _PR_DELETE_LOCK_ENV() \ + { if (_pr_envLock) { PR_DestroyLock(_pr_envLock); _pr_envLock = NULL; } } +#define _PR_LOCK_ENV() { if (_pr_envLock) PR_Lock(_pr_envLock); } +#define _PR_UNLOCK_ENV() { if (_pr_envLock) PR_Unlock(_pr_envLock); } +#endif + +/************************************************************************/ + +void _PR_InitEnv(void) +{ + _PR_NEW_LOCK_ENV(); +} + +void _PR_CleanupEnv(void) +{ + _PR_DELETE_LOCK_ENV(); +} + +PR_IMPLEMENT(char*) PR_GetEnv(const char *var) +{ + char *ev; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + _PR_LOCK_ENV(); + ev = _PR_MD_GET_ENV(var); + _PR_UNLOCK_ENV(); + return ev; +} + +PR_IMPLEMENT(char*) PR_GetEnvSecure(const char *var) +{ +#ifdef HAVE_SECURE_GETENV + char *ev; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + _PR_LOCK_ENV(); + ev = secure_getenv(var); + _PR_UNLOCK_ENV(); + + return ev; +#else +#ifdef XP_UNIX + /* + ** Fall back to checking uids and gids. This won't detect any other + ** privilege-granting mechanisms the platform may have. This also + ** can't detect the case where the process already called + ** setuid(geteuid()) and/or setgid(getegid()). + */ + if (getuid() != geteuid() || getgid() != getegid()) { + return NULL; + } +#endif /* XP_UNIX */ + return PR_GetEnv(var); +#endif /* HAVE_SECURE_GETENV */ +} + +PR_IMPLEMENT(PRStatus) PR_SetEnv(const char *string) +{ + PRIntn result; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (!strchr(string, '=')) { + return(PR_FAILURE); + } + + _PR_LOCK_ENV(); + result = _PR_MD_PUT_ENV((char*)string); + _PR_UNLOCK_ENV(); + return result ? PR_FAILURE : PR_SUCCESS; +} + +#if defined(XP_UNIX) && (!defined(DARWIN) || defined(HAVE_CRT_EXTERNS_H)) +PR_IMPLEMENT(char **) PR_DuplicateEnvironment(void) +{ + char **the_environ, **result, **end, **src, **dst; + + _PR_LOCK_ENV(); +#ifdef DARWIN + the_environ = *(_NSGetEnviron()); +#else + the_environ = environ; +#endif + + for (end = the_environ; *end != NULL; end++) + /* empty loop body */; + + result = (char **)PR_Malloc(sizeof(char *) * (end - the_environ + 1)); + if (result != NULL) { + for (src = the_environ, dst = result; src != end; src++, dst++) { + size_t len; + + len = strlen(*src) + 1; + *dst = PR_Malloc(len); + if (*dst == NULL) { + /* Allocation failed. Must clean up the half-copied env. */ + char **to_delete; + + for (to_delete = result; to_delete != dst; to_delete++) { + PR_Free(*to_delete); + } + PR_Free(result); + result = NULL; + goto out; + } + memcpy(*dst, *src, len); + } + *dst = NULL; + } +out: + _PR_UNLOCK_ENV(); + return result; +} +#else +/* This platform doesn't support raw access to the environ block. */ +PR_IMPLEMENT(char **) PR_DuplicateEnvironment(void) +{ + return NULL; +} +#endif diff --git a/nsprpub/pr/src/misc/prerr.c b/nsprpub/pr/src/misc/prerr.c new file mode 100644 index 0000000000..99cf39f8c2 --- /dev/null +++ b/nsprpub/pr/src/misc/prerr.c @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * prerr.c + * This file is automatically generated; please do not edit it. + */ +#include "prerror.h" +static const struct PRErrorMessage text[] = { + {"PR_OUT_OF_MEMORY_ERROR", "Memory allocation attempt failed"}, + {"PR_BAD_DESCRIPTOR_ERROR", "Invalid file descriptor"}, + {"PR_WOULD_BLOCK_ERROR", "The operation would have blocked"}, + {"PR_ACCESS_FAULT_ERROR", "Invalid memory address argument"}, + {"PR_INVALID_METHOD_ERROR", "Invalid function for file type"}, + {"PR_ILLEGAL_ACCESS_ERROR", "Invalid memory address argument"}, + {"PR_UNKNOWN_ERROR", "Some unknown error has occurred"}, + {"PR_PENDING_INTERRUPT_ERROR", "Operation interrupted by another thread"}, + {"PR_NOT_IMPLEMENTED_ERROR", "function not implemented"}, + {"PR_IO_ERROR", "I/O function error"}, + {"PR_IO_TIMEOUT_ERROR", "I/O operation timed out"}, + {"PR_IO_PENDING_ERROR", "I/O operation on busy file descriptor"}, + {"PR_DIRECTORY_OPEN_ERROR", "The directory could not be opened"}, + {"PR_INVALID_ARGUMENT_ERROR", "Invalid function argument"}, + {"PR_ADDRESS_NOT_AVAILABLE_ERROR", "Network address not available (in use?)"}, + {"PR_ADDRESS_NOT_SUPPORTED_ERROR", "Network address type not supported"}, + {"PR_IS_CONNECTED_ERROR", "Already connected"}, + {"PR_BAD_ADDRESS_ERROR", "Network address is invalid"}, + {"PR_ADDRESS_IN_USE_ERROR", "Local Network address is in use"}, + {"PR_CONNECT_REFUSED_ERROR", "Connection refused by peer"}, + {"PR_NETWORK_UNREACHABLE_ERROR", "Network address is presently unreachable"}, + {"PR_CONNECT_TIMEOUT_ERROR", "Connection attempt timed out"}, + {"PR_NOT_CONNECTED_ERROR", "Network file descriptor is not connected"}, + {"PR_LOAD_LIBRARY_ERROR", "Failure to load dynamic library"}, + {"PR_UNLOAD_LIBRARY_ERROR", "Failure to unload dynamic library"}, + {"PR_FIND_SYMBOL_ERROR", "Symbol not found in any of the loaded dynamic libraries"}, + {"PR_INSUFFICIENT_RESOURCES_ERROR", "Insufficient system resources"}, + {"PR_DIRECTORY_LOOKUP_ERROR", "A directory lookup on a network address has failed"}, + {"PR_TPD_RANGE_ERROR", "Attempt to access a TPD key that is out of range"}, + {"PR_PROC_DESC_TABLE_FULL_ERROR", "Process open FD table is full"}, + {"PR_SYS_DESC_TABLE_FULL_ERROR", "System open FD table is full"}, + {"PR_NOT_SOCKET_ERROR", "Network operation attempted on non-network file descriptor"}, + {"PR_NOT_TCP_SOCKET_ERROR", "TCP-specific function attempted on a non-TCP file descriptor"}, + {"PR_SOCKET_ADDRESS_IS_BOUND_ERROR", "TCP file descriptor is already bound"}, + {"PR_NO_ACCESS_RIGHTS_ERROR", "Access Denied"}, + {"PR_OPERATION_NOT_SUPPORTED_ERROR", "The requested operation is not supported by the platform"}, + {"PR_PROTOCOL_NOT_SUPPORTED_ERROR", "The host operating system does not support the protocol requested"}, + {"PR_REMOTE_FILE_ERROR", "Access to the remote file has been severed"}, + {"PR_BUFFER_OVERFLOW_ERROR", "The value requested is too large to be stored in the data buffer provided"}, + {"PR_CONNECT_RESET_ERROR", "TCP connection reset by peer"}, + {"PR_RANGE_ERROR", "Unused"}, + {"PR_DEADLOCK_ERROR", "The operation would have deadlocked"}, + {"PR_FILE_IS_LOCKED_ERROR", "The file is already locked"}, + {"PR_FILE_TOO_BIG_ERROR", "Write would result in file larger than the system allows"}, + {"PR_NO_DEVICE_SPACE_ERROR", "The device for storing the file is full"}, + {"PR_PIPE_ERROR", "Unused"}, + {"PR_NO_SEEK_DEVICE_ERROR", "Unused"}, + {"PR_IS_DIRECTORY_ERROR", "Cannot perform a normal file operation on a directory"}, + {"PR_LOOP_ERROR", "Symbolic link loop"}, + {"PR_NAME_TOO_LONG_ERROR", "File name is too long"}, + {"PR_FILE_NOT_FOUND_ERROR", "File not found"}, + {"PR_NOT_DIRECTORY_ERROR", "Cannot perform directory operation on a normal file"}, + {"PR_READ_ONLY_FILESYSTEM_ERROR", "Cannot write to a read-only file system"}, + {"PR_DIRECTORY_NOT_EMPTY_ERROR", "Cannot delete a directory that is not empty"}, + {"PR_FILESYSTEM_MOUNTED_ERROR", "Cannot delete or rename a file object while the file system is busy"}, + {"PR_NOT_SAME_DEVICE_ERROR", "Cannot rename a file to a file system on another device"}, + {"PR_DIRECTORY_CORRUPTED_ERROR", "The directory object in the file system is corrupted"}, + {"PR_FILE_EXISTS_ERROR", "Cannot create or rename a filename that already exists"}, + {"PR_MAX_DIRECTORY_ENTRIES_ERROR", "Directory is full. No additional filenames may be added"}, + {"PR_INVALID_DEVICE_STATE_ERROR", "The required device was in an invalid state"}, + {"PR_DEVICE_IS_LOCKED_ERROR", "The device is locked"}, + {"PR_NO_MORE_FILES_ERROR", "No more entries in the directory"}, + {"PR_END_OF_FILE_ERROR", "Encountered end of file"}, + {"PR_FILE_SEEK_ERROR", "Seek error"}, + {"PR_FILE_IS_BUSY_ERROR", "The file is busy"}, + {"PR_OPERATION_ABORTED_ERROR", "The I/O operation was aborted"}, + {"PR_IN_PROGRESS_ERROR", "Operation is still in progress (probably a non-blocking connect)"}, + {"PR_ALREADY_INITIATED_ERROR", "Operation has already been initiated (probably a non-blocking connect)"}, + {"PR_GROUP_EMPTY_ERROR", "The wait group is empty"}, + {"PR_INVALID_STATE_ERROR", "Object state improper for request"}, + {"PR_NETWORK_DOWN_ERROR", "Network is down"}, + {"PR_SOCKET_SHUTDOWN_ERROR", "Socket shutdown"}, + {"PR_CONNECT_ABORTED_ERROR", "Connection aborted"}, + {"PR_HOST_UNREACHABLE_ERROR", "Host is unreachable"}, + {"PR_LIBRARY_NOT_LOADED_ERROR", "The library is not loaded"}, + {"PR_CALL_ONCE_ERROR", "The one-time function was previously called and failed. Its error code is no longer available"}, + {"PR_MAX_ERROR", "Placeholder for the end of the list"}, + {0, 0} +}; + +static const struct PRErrorTable et = { text, "prerr", -6000L, 77 }; + +void nspr_InitializePRErrorTable(void) { + PR_ErrorInstallTable(&et); +} diff --git a/nsprpub/pr/src/misc/prerr.et b/nsprpub/pr/src/misc/prerr.et new file mode 100644 index 0000000000..fa81aafcf6 --- /dev/null +++ b/nsprpub/pr/src/misc/prerr.et @@ -0,0 +1,108 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +et nspr -6000 + +ec PR_OUT_OF_MEMORY_ERROR, "Memory allocation attempt failed" +ec PR_BAD_DESCRIPTOR_ERROR, "Invalid file descriptor" +ec PR_WOULD_BLOCK_ERROR, "The operation would have blocked" +ec PR_ACCESS_FAULT_ERROR, "Invalid memory address argument" +ec PR_INVALID_METHOD_ERROR, "Invalid function for file type" +ec PR_ILLEGAL_ACCESS_ERROR, "Invalid memory address argument" +ec PR_UNKNOWN_ERROR, "Some unknown error has occurred" +ec PR_PENDING_INTERRUPT_ERROR,"Operation interrupted by another thread" +ec PR_NOT_IMPLEMENTED_ERROR, "function not implemented" +ec PR_IO_ERROR, "I/O function error" +ec PR_IO_TIMEOUT_ERROR, "I/O operation timed out" +ec PR_IO_PENDING_ERROR, "I/O operation on busy file descriptor" +ec PR_DIRECTORY_OPEN_ERROR, "The directory could not be opened" +ec PR_INVALID_ARGUMENT_ERROR, "Invalid function argument" +ec PR_ADDRESS_NOT_AVAILABLE_ERROR, "Network address not available (in use?)" +ec PR_ADDRESS_NOT_SUPPORTED_ERROR, "Network address type not supported" +ec PR_IS_CONNECTED_ERROR, "Already connected" +ec PR_BAD_ADDRESS_ERROR, "Network address is invalid" +ec PR_ADDRESS_IN_USE_ERROR, "Local Network address is in use" +ec PR_CONNECT_REFUSED_ERROR, "Connection refused by peer" +ec PR_NETWORK_UNREACHABLE_ERROR, "Network address is presently unreachable" +ec PR_CONNECT_TIMEOUT_ERROR, "Connection attempt timed out" +ec PR_NOT_CONNECTED_ERROR, "Network file descriptor is not connected" +ec PR_LOAD_LIBRARY_ERROR, "Failure to load dynamic library" +ec PR_UNLOAD_LIBRARY_ERROR, "Failure to unload dynamic library" +ec PR_FIND_SYMBOL_ERROR, +"Symbol not found in any of the loaded dynamic libraries" +ec PR_INSUFFICIENT_RESOURCES_ERROR, "Insufficient system resources" +ec PR_DIRECTORY_LOOKUP_ERROR, +"A directory lookup on a network address has failed" +ec PR_TPD_RANGE_ERROR, +"Attempt to access a TPD key that is out of range" +ec PR_PROC_DESC_TABLE_FULL_ERROR, "Process open FD table is full" +ec PR_SYS_DESC_TABLE_FULL_ERROR, "System open FD table is full" +ec PR_NOT_SOCKET_ERROR, +"Network operation attempted on non-network file descriptor" +ec PR_NOT_TCP_SOCKET_ERROR, +"TCP-specific function attempted on a non-TCP file descriptor" +ec PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "TCP file descriptor is already bound" +ec PR_NO_ACCESS_RIGHTS_ERROR, "Access Denied" +ec PR_OPERATION_NOT_SUPPORTED_ERROR, +"The requested operation is not supported by the platform" +ec PR_PROTOCOL_NOT_SUPPORTED_ERROR, +"The host operating system does not support the protocol requested" +ec PR_REMOTE_FILE_ERROR, "Access to the remote file has been severed" +ec PR_BUFFER_OVERFLOW_ERROR, +"The value requested is too large to be stored in the data buffer provided" +ec PR_CONNECT_RESET_ERROR, "TCP connection reset by peer" +ec PR_RANGE_ERROR, "Unused" +ec PR_DEADLOCK_ERROR, "The operation would have deadlocked" +ec PR_FILE_IS_LOCKED_ERROR, "The file is already locked" +ec PR_FILE_TOO_BIG_ERROR, +"Write would result in file larger than the system allows" +ec PR_NO_DEVICE_SPACE_ERROR, "The device for storing the file is full" +ec PR_PIPE_ERROR, "Unused" +ec PR_NO_SEEK_DEVICE_ERROR, "Unused" +ec PR_IS_DIRECTORY_ERROR, +"Cannot perform a normal file operation on a directory" +ec PR_LOOP_ERROR, "Symbolic link loop" +ec PR_NAME_TOO_LONG_ERROR, "File name is too long" +ec PR_FILE_NOT_FOUND_ERROR, "File not found" +ec PR_NOT_DIRECTORY_ERROR, +"Cannot perform directory operation on a normal file" +ec PR_READ_ONLY_FILESYSTEM_ERROR, +"Cannot write to a read-only file system" +ec PR_DIRECTORY_NOT_EMPTY_ERROR, +"Cannot delete a directory that is not empty" +ec PR_FILESYSTEM_MOUNTED_ERROR, +"Cannot delete or rename a file object while the file system is busy" +ec PR_NOT_SAME_DEVICE_ERROR, +"Cannot rename a file to a file system on another device" +ec PR_DIRECTORY_CORRUPTED_ERROR, +"The directory object in the file system is corrupted" +ec PR_FILE_EXISTS_ERROR, +"Cannot create or rename a filename that already exists" +ec PR_MAX_DIRECTORY_ENTRIES_ERROR, +"Directory is full. No additional filenames may be added" +ec PR_INVALID_DEVICE_STATE_ERROR, +"The required device was in an invalid state" +ec PR_DEVICE_IS_LOCKED_ERROR, "The device is locked" +ec PR_NO_MORE_FILES_ERROR, "No more entries in the directory" +ec PR_END_OF_FILE_ERROR, "Encountered end of file" +ec PR_FILE_SEEK_ERROR, "Seek error" +ec PR_FILE_IS_BUSY_ERROR, "The file is busy" +ec PR_OPERATION_ABORTED_ERROR, "The I/O operation was aborted" +ec PR_IN_PROGRESS_ERROR, +"Operation is still in progress (probably a non-blocking connect)" +ec PR_ALREADY_INITIATED_ERROR, +"Operation has already been initiated (probably a non-blocking connect)" +ec PR_GROUP_EMPTY_ERROR, "The wait group is empty" +ec PR_INVALID_STATE_ERROR, "Object state improper for request" +ec PR_NETWORK_DOWN_ERROR, "Network is down" +ec PR_SOCKET_SHUTDOWN_ERROR, "Socket shutdown" +ec PR_CONNECT_ABORTED_ERROR, "Connection aborted" +ec PR_HOST_UNREACHABLE_ERROR, "Host is unreachable" +ec PR_LIBRARY_NOT_LOADED_ERROR, "The library is not loaded" +ec PR_CALL_ONCE_ERROR, "The one-time function was previously called and failed. Its error code is no longer available" + +ec PR_MAX_ERROR, "Placeholder for the end of the list" + +end diff --git a/nsprpub/pr/src/misc/prerr.properties b/nsprpub/pr/src/misc/prerr.properties new file mode 100644 index 0000000000..ef236b138a --- /dev/null +++ b/nsprpub/pr/src/misc/prerr.properties @@ -0,0 +1,85 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# prerr.properties +# This file is automatically generated; please do not edit it. +PR_OUT_OF_MEMORY_ERROR=Memory allocation attempt failed +PR_BAD_DESCRIPTOR_ERROR=Invalid file descriptor +PR_WOULD_BLOCK_ERROR=The operation would have blocked +PR_ACCESS_FAULT_ERROR=Invalid memory address argument +PR_INVALID_METHOD_ERROR=Invalid function for file type +PR_ILLEGAL_ACCESS_ERROR=Invalid memory address argument +PR_UNKNOWN_ERROR=Some unknown error has occurred +PR_PENDING_INTERRUPT_ERROR=Operation interrupted by another thread +PR_NOT_IMPLEMENTED_ERROR=function not implemented +PR_IO_ERROR=I/O function error +PR_IO_TIMEOUT_ERROR=I/O operation timed out +PR_IO_PENDING_ERROR=I/O operation on busy file descriptor +PR_DIRECTORY_OPEN_ERROR=The directory could not be opened +PR_INVALID_ARGUMENT_ERROR=Invalid function argument +PR_ADDRESS_NOT_AVAILABLE_ERROR=Network address not available (in use?) +PR_ADDRESS_NOT_SUPPORTED_ERROR=Network address type not supported +PR_IS_CONNECTED_ERROR=Already connected +PR_BAD_ADDRESS_ERROR=Network address is invalid +PR_ADDRESS_IN_USE_ERROR=Local Network address is in use +PR_CONNECT_REFUSED_ERROR=Connection refused by peer +PR_NETWORK_UNREACHABLE_ERROR=Network address is presently unreachable +PR_CONNECT_TIMEOUT_ERROR=Connection attempt timed out +PR_NOT_CONNECTED_ERROR=Network file descriptor is not connected +PR_LOAD_LIBRARY_ERROR=Failure to load dynamic library +PR_UNLOAD_LIBRARY_ERROR=Failure to unload dynamic library +PR_FIND_SYMBOL_ERROR=Symbol not found in any of the loaded dynamic libraries +PR_INSUFFICIENT_RESOURCES_ERROR=Insufficient system resources +PR_DIRECTORY_LOOKUP_ERROR=A directory lookup on a network address has failed +PR_TPD_RANGE_ERROR=Attempt to access a TPD key that is out of range +PR_PROC_DESC_TABLE_FULL_ERROR=Process open FD table is full +PR_SYS_DESC_TABLE_FULL_ERROR=System open FD table is full +PR_NOT_SOCKET_ERROR=Network operation attempted on non-network file descriptor +PR_NOT_TCP_SOCKET_ERROR=TCP-specific function attempted on a non-TCP file descriptor +PR_SOCKET_ADDRESS_IS_BOUND_ERROR=TCP file descriptor is already bound +PR_NO_ACCESS_RIGHTS_ERROR=Access Denied +PR_OPERATION_NOT_SUPPORTED_ERROR=The requested operation is not supported by the platform +PR_PROTOCOL_NOT_SUPPORTED_ERROR=The host operating system does not support the protocol requested +PR_REMOTE_FILE_ERROR=Access to the remote file has been severed +PR_BUFFER_OVERFLOW_ERROR=The value requested is too large to be stored in the data buffer provided +PR_CONNECT_RESET_ERROR=TCP connection reset by peer +PR_RANGE_ERROR=Unused +PR_DEADLOCK_ERROR=The operation would have deadlocked +PR_FILE_IS_LOCKED_ERROR=The file is already locked +PR_FILE_TOO_BIG_ERROR=Write would result in file larger than the system allows +PR_NO_DEVICE_SPACE_ERROR=The device for storing the file is full +PR_PIPE_ERROR=Unused +PR_NO_SEEK_DEVICE_ERROR=Unused +PR_IS_DIRECTORY_ERROR=Cannot perform a normal file operation on a directory +PR_LOOP_ERROR=Symbolic link loop +PR_NAME_TOO_LONG_ERROR=File name is too long +PR_FILE_NOT_FOUND_ERROR=File not found +PR_NOT_DIRECTORY_ERROR=Cannot perform directory operation on a normal file +PR_READ_ONLY_FILESYSTEM_ERROR=Cannot write to a read-only file system +PR_DIRECTORY_NOT_EMPTY_ERROR=Cannot delete a directory that is not empty +PR_FILESYSTEM_MOUNTED_ERROR=Cannot delete or rename a file object while the file system is busy +PR_NOT_SAME_DEVICE_ERROR=Cannot rename a file to a file system on another device +PR_DIRECTORY_CORRUPTED_ERROR=The directory object in the file system is corrupted +PR_FILE_EXISTS_ERROR=Cannot create or rename a filename that already exists +PR_MAX_DIRECTORY_ENTRIES_ERROR=Directory is full. No additional filenames may be added +PR_INVALID_DEVICE_STATE_ERROR=The required device was in an invalid state +PR_DEVICE_IS_LOCKED_ERROR=The device is locked +PR_NO_MORE_FILES_ERROR=No more entries in the directory +PR_END_OF_FILE_ERROR=Encountered end of file +PR_FILE_SEEK_ERROR=Seek error +PR_FILE_IS_BUSY_ERROR=The file is busy +PR_OPERATION_ABORTED_ERROR=The I/O operation was aborted +PR_IN_PROGRESS_ERROR=Operation is still in progress (probably a non-blocking connect) +PR_ALREADY_INITIATED_ERROR=Operation has already been initiated (probably a non-blocking connect) +PR_GROUP_EMPTY_ERROR=The wait group is empty +PR_INVALID_STATE_ERROR=Object state improper for request +PR_NETWORK_DOWN_ERROR=Network is down +PR_SOCKET_SHUTDOWN_ERROR=Socket shutdown +PR_CONNECT_ABORTED_ERROR=Connection aborted +PR_HOST_UNREACHABLE_ERROR=Host is unreachable +PR_LIBRARY_NOT_LOADED_ERROR=The library is not loaded +PR_CALL_ONCE_ERROR=The one-time function was previously called and failed. Its error code is no longer available +PR_MAX_ERROR=Placeholder for the end of the list diff --git a/nsprpub/pr/src/misc/prerror.c b/nsprpub/pr/src/misc/prerror.c new file mode 100644 index 0000000000..a06b78ddbc --- /dev/null +++ b/nsprpub/pr/src/misc/prerror.c @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> +#include <stdlib.h> + +PR_IMPLEMENT(PRErrorCode) PR_GetError(void) +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->errorCode; +} + +PR_IMPLEMENT(PRInt32) PR_GetOSError(void) +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->osErrorCode; +} + +PR_IMPLEMENT(void) PR_SetError(PRErrorCode code, PRInt32 osErr) +{ + PRThread *thread = PR_GetCurrentThread(); + thread->errorCode = code; + thread->osErrorCode = osErr; + thread->errorStringLength = 0; +} + +PR_IMPLEMENT(void) PR_SetErrorText(PRIntn textLength, const char *text) +{ + PRThread *thread = PR_GetCurrentThread(); + + if (0 == textLength) + { + if (NULL != thread->errorString) { + PR_DELETE(thread->errorString); + } + thread->errorStringSize = 0; + } + else + { + PRIntn size = textLength + 31; /* actual length to allocate. Plus a little extra */ + if (thread->errorStringSize < textLength+1) /* do we have room? */ + { + if (NULL != thread->errorString) { + PR_DELETE(thread->errorString); + } + thread->errorString = (char*)PR_MALLOC(size); + if ( NULL == thread->errorString ) { + thread->errorStringSize = 0; + thread->errorStringLength = 0; + return; + } + thread->errorStringSize = size; + } + memcpy(thread->errorString, text, textLength+1 ); + } + thread->errorStringLength = textLength; +} + +PR_IMPLEMENT(PRInt32) PR_GetErrorTextLength(void) +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->errorStringLength; +} /* PR_GetErrorTextLength */ + +PR_IMPLEMENT(PRInt32) PR_GetErrorText(char *text) +{ + PRThread *thread = PR_GetCurrentThread(); + if (0 != thread->errorStringLength) { + memcpy(text, thread->errorString, thread->errorStringLength+1); + } + return thread->errorStringLength; +} /* PR_GetErrorText */ + + diff --git a/nsprpub/pr/src/misc/prerrortable.c b/nsprpub/pr/src/misc/prerrortable.c new file mode 100644 index 0000000000..a7ff4f0eb5 --- /dev/null +++ b/nsprpub/pr/src/misc/prerrortable.c @@ -0,0 +1,209 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + + + +/* + +Copyright 1987, 1988 by the Student Information Processing Board + of the Massachusetts Institute of Technology + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is +hereby granted, provided that the above copyright notice +appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation, +and that the names of M.I.T. and the M.I.T. S.I.P.B. not be +used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. +M.I.T. and the M.I.T. S.I.P.B. make no representations about +the suitability of this software for any purpose. It is +provided "as is" without express or implied warranty. + +*/ + +#include <string.h> +#include <assert.h> +#include <errno.h> +#include "prmem.h" +#include "prerror.h" + +#define ERRCODE_RANGE 8 /* # of bits to shift table number */ +#define BITS_PER_CHAR 6 /* # bits to shift per character in name */ + +#ifdef NEED_SYS_ERRLIST +extern char const * const sys_errlist[]; +extern const int sys_nerr; +#endif + +/* List of error tables */ +struct PRErrorTableList { + struct PRErrorTableList *next; + const struct PRErrorTable *table; + struct PRErrorCallbackTablePrivate *table_private; +}; +static struct PRErrorTableList * Table_List = (struct PRErrorTableList *) NULL; + +/* Supported languages */ +static const char * default_languages[] = { "i-default", "en", 0 }; +static const char * const * callback_languages = default_languages; + +/* Callback info */ +static struct PRErrorCallbackPrivate *callback_private = 0; +static PRErrorCallbackLookupFn *callback_lookup = 0; +static PRErrorCallbackNewTableFn *callback_newtable = 0; + + +static const char char_set[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + +static const char * +error_table_name (PRErrorCode num) +{ + static char buf[6]; /* only used if internal code problems exist */ + + long ch; + int i; + char *p; + + /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */ + p = buf; + num >>= ERRCODE_RANGE; + /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */ + num &= 077777777; + /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */ + for (i = 4; i >= 0; i--) { + ch = (num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1); + if (ch != 0) { + *p++ = char_set[ch-1]; + } + } + *p = '\0'; + return(buf); +} + +PR_IMPLEMENT(const char *) +PR_ErrorToString(PRErrorCode code, PRLanguageCode language) +{ + /* static buffer only used if code is using inconsistent error message + * numbers, so just ignore the possible thread contention + */ + static char buffer[25]; + + const char *msg; + int offset; + PRErrorCode table_num; + struct PRErrorTableList *et; + int started = 0; + char *cp; + + for (et = Table_List; et; et = et->next) { + if (et->table->base <= code && + et->table->base + et->table->n_msgs > code) { + /* This is the right table */ + if (callback_lookup) { + msg = callback_lookup(code, language, et->table, + callback_private, et->table_private); + if (msg) { + return msg; + } + } + + return(et->table->msgs[code - et->table->base].en_text); + } + } + + if (code >= 0 && code < 256) { + return strerror(code); + } + + offset = (int) (code & ((1<<ERRCODE_RANGE)-1)); + table_num = code - offset; + strcpy (buffer, "Unknown code "); + if (table_num) { + strcat(buffer, error_table_name (table_num)); + strcat(buffer, " "); + } + for (cp = buffer; *cp; cp++) + ; + if (offset >= 100) { + *cp++ = (char)('0' + offset / 100); + offset %= 100; + started++; + } + if (started || offset >= 10) { + *cp++ = (char)('0' + offset / 10); + offset %= 10; + } + *cp++ = (char)('0' + offset); + *cp = '\0'; + return(buffer); +} + +PR_IMPLEMENT(const char *) +PR_ErrorToName(PRErrorCode code) +{ + struct PRErrorTableList *et; + + for (et = Table_List; et; et = et->next) { + if (et->table->base <= code && + et->table->base + et->table->n_msgs > code) { + /* This is the right table */ + return(et->table->msgs[code - et->table->base].name); + } + } + + return 0; +} + +PR_IMPLEMENT(const char * const *) +PR_ErrorLanguages(void) +{ + return callback_languages; +} + +PR_IMPLEMENT(PRErrorCode) +PR_ErrorInstallTable(const struct PRErrorTable *table) +{ + struct PRErrorTableList * new_et; + + new_et = (struct PRErrorTableList *) + PR_Malloc(sizeof(struct PRErrorTableList)); + if (!new_et) { + return errno; /* oops */ + } + new_et->table = table; + if (callback_newtable) { + new_et->table_private = callback_newtable(table, callback_private); + } else { + new_et->table_private = 0; + } + new_et->next = Table_List; + Table_List = new_et; + return 0; +} + +PR_IMPLEMENT(void) +PR_ErrorInstallCallback(const char * const * languages, + PRErrorCallbackLookupFn *lookup, + PRErrorCallbackNewTableFn *newtable, + struct PRErrorCallbackPrivate *cb_private) +{ + struct PRErrorTableList *et; + + assert(strcmp(languages[0], "i-default") == 0); + assert(strcmp(languages[1], "en") == 0); + + callback_languages = languages; + callback_lookup = lookup; + callback_newtable = newtable; + callback_private = cb_private; + + if (callback_newtable) { + for (et = Table_List; et; et = et->next) { + et->table_private = callback_newtable(et->table, callback_private); + } + } +} diff --git a/nsprpub/pr/src/misc/prinit.c b/nsprpub/pr/src/misc/prinit.c new file mode 100644 index 0000000000..a952ad6554 --- /dev/null +++ b/nsprpub/pr/src/misc/prinit.c @@ -0,0 +1,857 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include <ctype.h> +#include <string.h> + +PRLogModuleInfo *_pr_clock_lm; +PRLogModuleInfo *_pr_cmon_lm; +PRLogModuleInfo *_pr_io_lm; +PRLogModuleInfo *_pr_cvar_lm; +PRLogModuleInfo *_pr_mon_lm; +PRLogModuleInfo *_pr_linker_lm; +PRLogModuleInfo *_pr_sched_lm; +PRLogModuleInfo *_pr_thread_lm; +PRLogModuleInfo *_pr_gc_lm; +PRLogModuleInfo *_pr_shm_lm; +PRLogModuleInfo *_pr_shma_lm; + +PRFileDesc *_pr_stdin; +PRFileDesc *_pr_stdout; +PRFileDesc *_pr_stderr; + +#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) + +PRCList _pr_active_local_threadQ = + PR_INIT_STATIC_CLIST(&_pr_active_local_threadQ); +PRCList _pr_active_global_threadQ = + PR_INIT_STATIC_CLIST(&_pr_active_global_threadQ); + +_MDLock _pr_cpuLock; /* lock for the CPU Q */ +PRCList _pr_cpuQ = PR_INIT_STATIC_CLIST(&_pr_cpuQ); + +PRUint32 _pr_utid; + +PRInt32 _pr_userActive; +PRInt32 _pr_systemActive; +PRUintn _pr_maxPTDs; + +#ifdef _PR_LOCAL_THREADS_ONLY + +struct _PRCPU *_pr_currentCPU; +PRThread *_pr_currentThread; +PRThread *_pr_lastThread; +PRInt32 _pr_intsOff; + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +/* Lock protecting all "termination" condition variables of all threads */ +PRLock *_pr_terminationCVLock; + +#endif /* !defined(_PR_PTHREADS) */ + +PRLock *_pr_sleeplock; /* used in PR_Sleep(), classic and pthreads */ + +static void _PR_InitCallOnce(void); + +PRBool _pr_initialized = PR_FALSE; + + +PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion) +{ + /* + ** This is the secret handshake algorithm. + ** + ** This release has a simple version compatibility + ** check algorithm. This release is not backward + ** compatible with previous major releases. It is + ** not compatible with future major, minor, or + ** patch releases. + */ + int vmajor = 0, vminor = 0, vpatch = 0; + const char *ptr = importedVersion; + + while (isdigit(*ptr)) { + vmajor = 10 * vmajor + *ptr - '0'; + ptr++; + } + if (*ptr == '.') { + ptr++; + while (isdigit(*ptr)) { + vminor = 10 * vminor + *ptr - '0'; + ptr++; + } + if (*ptr == '.') { + ptr++; + while (isdigit(*ptr)) { + vpatch = 10 * vpatch + *ptr - '0'; + ptr++; + } + } + } + + if (vmajor != PR_VMAJOR) { + return PR_FALSE; + } + if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) { + return PR_FALSE; + } + if (vmajor == PR_VMAJOR && vminor == PR_VMINOR && vpatch > PR_VPATCH) { + return PR_FALSE; + } + return PR_TRUE; +} /* PR_VersionCheck */ + +PR_IMPLEMENT(const char*) PR_GetVersion(void) +{ + return PR_VERSION; +} + +PR_IMPLEMENT(PRBool) PR_Initialized(void) +{ + return _pr_initialized; +} + +PRInt32 _native_threads_only = 0; + +#ifdef WINNT +static void _pr_SetNativeThreadsOnlyMode(void) +{ + HMODULE mainExe; + PRBool *globalp; + char *envp; + + mainExe = GetModuleHandle(NULL); + PR_ASSERT(NULL != mainExe); + globalp = (PRBool *) GetProcAddress(mainExe, "nspr_native_threads_only"); + if (globalp) { + _native_threads_only = (*globalp != PR_FALSE); + } else if (envp = getenv("NSPR_NATIVE_THREADS_ONLY")) { + _native_threads_only = (atoi(envp) == 1); + } +} +#endif + +static void _PR_InitStuff(void) +{ + + if (_pr_initialized) { + return; + } + _pr_initialized = PR_TRUE; +#ifdef _PR_ZONE_ALLOCATOR + _PR_InitZones(); +#endif +#ifdef WINNT + _pr_SetNativeThreadsOnlyMode(); +#endif + + + (void) PR_GetPageSize(); + + _pr_clock_lm = PR_NewLogModule("clock"); + _pr_cmon_lm = PR_NewLogModule("cmon"); + _pr_io_lm = PR_NewLogModule("io"); + _pr_mon_lm = PR_NewLogModule("mon"); + _pr_linker_lm = PR_NewLogModule("linker"); + _pr_cvar_lm = PR_NewLogModule("cvar"); + _pr_sched_lm = PR_NewLogModule("sched"); + _pr_thread_lm = PR_NewLogModule("thread"); + _pr_gc_lm = PR_NewLogModule("gc"); + _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(); + + _PR_InitLocks(); + _PR_InitAtomic(); + _PR_InitSegs(); + _PR_InitStacks(); + _PR_InitTPD(); + _PR_InitEnv(); + _PR_InitLayerCache(); + _PR_InitClock(); + + _pr_sleeplock = PR_NewLock(); + PR_ASSERT(NULL != _pr_sleeplock); + + _PR_InitThreads(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + +#ifdef WIN16 + { + PRInt32 top; /* artificial top of stack, win16 */ + _pr_top_of_task_stack = (char *) ⊤ + } +#endif + +#ifndef _PR_GLOBAL_THREADS_ONLY + _PR_InitCPUs(); +#endif + + /* + * XXX: call _PR_InitMem only on those platforms for which nspr implements + * malloc, for now. + */ +#ifdef _PR_OVERRIDE_MALLOC + _PR_InitMem(); +#endif + + _PR_InitCMon(); + _PR_InitIO(); + _PR_InitNet(); + _PR_InitTime(); + _PR_InitLog(); + _PR_InitLinker(); + _PR_InitCallOnce(); + _PR_InitDtoa(); + _PR_InitMW(); + _PR_InitRWLocks(); + + nspr_InitializePRErrorTable(); + + _PR_MD_FINAL_INIT(); +} + +void _PR_ImplicitInitialization(void) +{ + _PR_InitStuff(); + + /* Enable interrupts */ +#if !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY) + _PR_MD_START_INTERRUPTS(); +#endif + +} + +PR_IMPLEMENT(void) PR_DisableClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) + if (!_pr_initialized) { + _PR_InitStuff(); + } else { + _PR_MD_DISABLE_CLOCK_INTERRUPTS(); + } +#endif +} + +PR_IMPLEMENT(void) PR_EnableClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) + if (!_pr_initialized) { + _PR_InitStuff(); + } + _PR_MD_ENABLE_CLOCK_INTERRUPTS(); +#endif +} + +PR_IMPLEMENT(void) PR_BlockClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) + _PR_MD_BLOCK_CLOCK_INTERRUPTS(); +#endif +} + +PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) + _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(); +#endif +} + +PR_IMPLEMENT(void) PR_Init( + PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) +{ + _PR_ImplicitInitialization(); +} + +PR_IMPLEMENT(PRIntn) PR_Initialize( + PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs) +{ + PRIntn rv; + _PR_ImplicitInitialization(); + rv = prmain(argc, argv); + PR_Cleanup(); + return rv; +} /* PR_Initialize */ + +/* + *----------------------------------------------------------------------- + * + * _PR_CleanupBeforeExit -- + * + * Perform the cleanup work before exiting the process. + * We first do the cleanup generic to all platforms. Then + * we call _PR_MD_CLEANUP_BEFORE_EXIT(), where platform-dependent + * cleanup is done. This function is used by PR_Cleanup(). + * + * See also: PR_Cleanup(). + * + *----------------------------------------------------------------------- + */ +#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) +/* see ptthread.c */ +#else +static void +_PR_CleanupBeforeExit(void) +{ + /* + Do not make any calls here other than to destroy resources. For example, + do not make any calls that eventually may end up in PR_Lock. Because the + thread is destroyed, can not access current thread any more. + */ + _PR_CleanupTPD(); + if (_pr_terminationCVLock) + /* + * In light of the comment above, this looks real suspicious. + * I'd go so far as to say it's just a problem waiting to happen. + */ + { + PR_DestroyLock(_pr_terminationCVLock); + } + + _PR_MD_CLEANUP_BEFORE_EXIT(); +} +#endif /* defined(_PR_PTHREADS) */ + +/* + *---------------------------------------------------------------------- + * + * PR_Cleanup -- + * + * Perform a graceful shutdown of the NSPR runtime. PR_Cleanup() may + * only be called from the primordial thread, typically at the + * end of the main() function. It returns when it has completed + * its platform-dependent duty and the process must not make any other + * NSPR library calls prior to exiting from main(). + * + * PR_Cleanup() first blocks the primordial thread until all the + * other user (non-system) threads, if any, have terminated. + * Then it performs cleanup in preparation for exiting the process. + * PR_Cleanup() does not exit the primordial thread (which would + * in turn exit the process). + * + * PR_Cleanup() only responds when it is called by the primordial + * thread. Calls by any other thread are silently ignored. + * + * See also: PR_ExitProcess() + * + *---------------------------------------------------------------------- + */ +#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) +/* see ptthread.c */ +#else + +PR_IMPLEMENT(PRStatus) PR_Cleanup() +{ + PRThread *me = PR_GetCurrentThread(); + PR_ASSERT((NULL != me) && (me->flags & _PR_PRIMORDIAL)); + if ((NULL != me) && (me->flags & _PR_PRIMORDIAL)) + { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); + + /* + * No more recycling of threads + */ + _pr_recycleThreads = 0; + + /* + * Wait for all other user (non-system/daemon) threads + * to terminate. + */ + PR_Lock(_pr_activeLock); + while (_pr_userActive > _pr_primordialExitCount) { + PR_WaitCondVar(_pr_primordialExitCVar, PR_INTERVAL_NO_TIMEOUT); + } + if (me->flags & _PR_SYSTEM) { + _pr_systemActive--; + } else { + _pr_userActive--; + } + PR_Unlock(_pr_activeLock); + + _PR_MD_EARLY_CLEANUP(); + + _PR_CleanupMW(); + _PR_CleanupTime(); + _PR_CleanupDtoa(); + _PR_CleanupCallOnce(); + _PR_ShutdownLinker(); + _PR_CleanupNet(); + _PR_CleanupIO(); + /* Release the primordial thread's private data, etc. */ + _PR_CleanupThread(me); + + _PR_MD_STOP_INTERRUPTS(); + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_Cleanup: clean up before destroying thread")); + _PR_LogCleanup(); + + /* + * This part should look like the end of _PR_NativeRunThread + * and _PR_UserRunThread. + */ + if (_PR_IS_NATIVE_THREAD(me)) { + _PR_MD_EXIT_THREAD(me); + _PR_NativeDestroyThread(me); + } else { + _PR_UserDestroyThread(me); + PR_DELETE(me->stack); + PR_DELETE(me); + } + + /* + * XXX: We are freeing the heap memory here so that Purify won't + * complain, but we should also free other kinds of resources + * that are allocated by the _PR_InitXXX() functions. + * Ideally, for each _PR_InitXXX(), there should be a corresponding + * _PR_XXXCleanup() that we can call here. + */ +#ifdef WINNT + _PR_CleanupCPUs(); +#endif + _PR_CleanupThreads(); + _PR_CleanupCMon(); + PR_DestroyLock(_pr_sleeplock); + _pr_sleeplock = NULL; + _PR_CleanupLayerCache(); + _PR_CleanupEnv(); + _PR_CleanupStacks(); + _PR_CleanupBeforeExit(); + _pr_initialized = PR_FALSE; + return PR_SUCCESS; + } + return PR_FAILURE; +} +#endif /* defined(_PR_PTHREADS) */ + +/* + *------------------------------------------------------------------------ + * PR_ProcessExit -- + * + * Cause an immediate, nongraceful, forced termination of the process. + * It takes a PRIntn argument, which is the exit status code of the + * process. + * + * See also: PR_Cleanup() + * + *------------------------------------------------------------------------ + */ + +#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) +/* see ptthread.c */ +#else +PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) +{ + _PR_MD_EXIT(status); +} + +#endif /* defined(_PR_PTHREADS) */ + +PR_IMPLEMENT(PRProcessAttr *) +PR_NewProcessAttr(void) +{ + PRProcessAttr *attr; + + attr = PR_NEWZAP(PRProcessAttr); + if (!attr) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return attr; +} + +PR_IMPLEMENT(void) +PR_ResetProcessAttr(PRProcessAttr *attr) +{ + PR_FREEIF(attr->currentDirectory); + PR_FREEIF(attr->fdInheritBuffer); + memset(attr, 0, sizeof(*attr)); +} + +PR_IMPLEMENT(void) +PR_DestroyProcessAttr(PRProcessAttr *attr) +{ + PR_FREEIF(attr->currentDirectory); + PR_FREEIF(attr->fdInheritBuffer); + PR_DELETE(attr); +} + +PR_IMPLEMENT(void) +PR_ProcessAttrSetStdioRedirect( + PRProcessAttr *attr, + PRSpecialFD stdioFd, + PRFileDesc *redirectFd) +{ + switch (stdioFd) { + case PR_StandardInput: + attr->stdinFd = redirectFd; + break; + case PR_StandardOutput: + attr->stdoutFd = redirectFd; + break; + case PR_StandardError: + attr->stderrFd = redirectFd; + break; + default: + PR_ASSERT(0); + } +} + +/* + * OBSOLETE + */ +PR_IMPLEMENT(void) +PR_SetStdioRedirect( + PRProcessAttr *attr, + PRSpecialFD stdioFd, + PRFileDesc *redirectFd) +{ +#if defined(DEBUG) + static PRBool warn = PR_TRUE; + if (warn) { + warn = _PR_Obsolete("PR_SetStdioRedirect()", + "PR_ProcessAttrSetStdioRedirect()"); + } +#endif + PR_ProcessAttrSetStdioRedirect(attr, stdioFd, redirectFd); +} + +PR_IMPLEMENT(PRStatus) +PR_ProcessAttrSetCurrentDirectory( + PRProcessAttr *attr, + const char *dir) +{ + PR_FREEIF(attr->currentDirectory); + attr->currentDirectory = (char *) PR_MALLOC(strlen(dir) + 1); + if (!attr->currentDirectory) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_FAILURE; + } + strcpy(attr->currentDirectory, dir); + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +PR_ProcessAttrSetInheritableFD( + PRProcessAttr *attr, + PRFileDesc *fd, + const char *name) +{ + /* We malloc the fd inherit buffer in multiples of this number. */ +#define FD_INHERIT_BUFFER_INCR 128 + /* The length of "NSPR_INHERIT_FDS=" */ +#define NSPR_INHERIT_FDS_STRLEN 17 + /* The length of osfd (PROsfd) printed in hexadecimal with 0x prefix */ +#ifdef _WIN64 +#define OSFD_STRLEN 18 +#else +#define OSFD_STRLEN 10 +#endif + /* The length of fd type (PRDescType) printed in decimal */ +#define FD_TYPE_STRLEN 1 + PRSize newSize; + int remainder; + char *newBuffer; + int nwritten; + char *cur; + int freeSize; + + if (fd->identity != PR_NSPR_IO_LAYER) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + if (fd->secret->inheritable == _PR_TRI_UNKNOWN) { + _PR_MD_QUERY_FD_INHERITABLE(fd); + } + if (fd->secret->inheritable != _PR_TRI_TRUE) { + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0); + return PR_FAILURE; + } + + /* + * We also need to account for the : separators and the + * terminating null byte. + */ + if (NULL == attr->fdInheritBuffer) { + /* The first time, we print "NSPR_INHERIT_FDS=<name>:<type>:<val>" */ + newSize = NSPR_INHERIT_FDS_STRLEN + strlen(name) + + FD_TYPE_STRLEN + OSFD_STRLEN + 2 + 1; + } else { + /* At other times, we print ":<name>:<type>:<val>" */ + newSize = attr->fdInheritBufferUsed + strlen(name) + + FD_TYPE_STRLEN + OSFD_STRLEN + 3 + 1; + } + if (newSize > attr->fdInheritBufferSize) { + /* Make newSize a multiple of FD_INHERIT_BUFFER_INCR */ + remainder = newSize % FD_INHERIT_BUFFER_INCR; + if (remainder != 0) { + newSize += (FD_INHERIT_BUFFER_INCR - remainder); + } + if (NULL == attr->fdInheritBuffer) { + newBuffer = (char *) PR_MALLOC(newSize); + } else { + newBuffer = (char *) PR_REALLOC(attr->fdInheritBuffer, newSize); + } + if (NULL == newBuffer) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_FAILURE; + } + attr->fdInheritBuffer = newBuffer; + attr->fdInheritBufferSize = newSize; + } + cur = attr->fdInheritBuffer + attr->fdInheritBufferUsed; + freeSize = attr->fdInheritBufferSize - attr->fdInheritBufferUsed; + if (0 == attr->fdInheritBufferUsed) { + nwritten = PR_snprintf(cur, freeSize, + "NSPR_INHERIT_FDS=%s:%d:0x%" PR_PRIxOSFD, + name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd); + } else { + nwritten = PR_snprintf(cur, freeSize, ":%s:%d:0x%" PR_PRIxOSFD, + name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd); + } + attr->fdInheritBufferUsed += nwritten; + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRFileDesc *) PR_GetInheritedFD( + const char *name) +{ + PRFileDesc *fd; + const char *envVar; + const char *ptr; + int len = strlen(name); + PROsfd osfd; + int nColons; + PRIntn fileType; + + envVar = PR_GetEnv("NSPR_INHERIT_FDS"); + if (NULL == envVar || '\0' == envVar[0]) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return NULL; + } + + ptr = envVar; + while (1) { + if ((ptr[len] == ':') && (strncmp(ptr, name, len) == 0)) { + ptr += len + 1; + if (PR_sscanf(ptr, "%d:0x%" PR_SCNxOSFD, &fileType, &osfd) != 2) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return NULL; + } + switch ((PRDescType)fileType) { + case PR_DESC_FILE: + fd = PR_ImportFile(osfd); + break; + case PR_DESC_PIPE: + fd = PR_ImportPipe(osfd); + break; + case PR_DESC_SOCKET_TCP: + fd = PR_ImportTCPSocket(osfd); + break; + case PR_DESC_SOCKET_UDP: + fd = PR_ImportUDPSocket(osfd); + break; + default: + PR_ASSERT(0); + PR_SetError(PR_UNKNOWN_ERROR, 0); + fd = NULL; + break; + } + if (fd) { + /* + * An inherited FD is inheritable by default. + * The child process needs to call PR_SetFDInheritable + * to make it non-inheritable if so desired. + */ + fd->secret->inheritable = _PR_TRI_TRUE; + } + return fd; + } + /* Skip three colons */ + nColons = 0; + while (*ptr) { + if (*ptr == ':') { + if (++nColons == 3) { + break; + } + } + ptr++; + } + if (*ptr == '\0') { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return NULL; + } + ptr++; + } +} + +PR_IMPLEMENT(PRProcess*) PR_CreateProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + return _PR_MD_CREATE_PROCESS(path, argv, envp, attr); +} /* PR_CreateProcess */ + +PR_IMPLEMENT(PRStatus) PR_CreateProcessDetached( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + PRProcess *process; + PRStatus rv; + + process = PR_CreateProcess(path, argv, envp, attr); + if (NULL == process) { + return PR_FAILURE; + } + rv = PR_DetachProcess(process); + PR_ASSERT(PR_SUCCESS == rv); + if (rv == PR_FAILURE) { + PR_DELETE(process); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_DetachProcess(PRProcess *process) +{ + return _PR_MD_DETACH_PROCESS(process); +} + +PR_IMPLEMENT(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode) +{ + return _PR_MD_WAIT_PROCESS(process, exitCode); +} /* PR_WaitProcess */ + +PR_IMPLEMENT(PRStatus) PR_KillProcess(PRProcess *process) +{ + return _PR_MD_KILL_PROCESS(process); +} + +/* + ******************************************************************** + * + * Module initialization + * + ******************************************************************** + */ + +static struct { + PRLock *ml; + PRCondVar *cv; +} mod_init; + +static void _PR_InitCallOnce(void) { + mod_init.ml = PR_NewLock(); + PR_ASSERT(NULL != mod_init.ml); + mod_init.cv = PR_NewCondVar(mod_init.ml); + PR_ASSERT(NULL != mod_init.cv); +} + +void _PR_CleanupCallOnce() +{ + PR_DestroyLock(mod_init.ml); + mod_init.ml = NULL; + PR_DestroyCondVar(mod_init.cv); + mod_init.cv = NULL; +} + +PR_IMPLEMENT(PRStatus) PR_CallOnce( + PRCallOnceType *once, + PRCallOnceFN func) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + PR_Lock(mod_init.ml); + PRIntn initialized = once->initialized; + PRStatus status = once->status; + PR_Unlock(mod_init.ml); + if (!initialized) { + if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) { + status = (*func)(); + PR_Lock(mod_init.ml); + once->status = status; + once->initialized = 1; + PR_NotifyAllCondVar(mod_init.cv); + PR_Unlock(mod_init.ml); + } else { + PR_Lock(mod_init.ml); + while (!once->initialized) { + PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); + } + status = once->status; + PR_Unlock(mod_init.ml); + if (PR_SUCCESS != status) { + PR_SetError(PR_CALL_ONCE_ERROR, 0); + } + } + return status; + } + if (PR_SUCCESS != status) { + PR_SetError(PR_CALL_ONCE_ERROR, 0); + } + return status; +} + +PR_IMPLEMENT(PRStatus) PR_CallOnceWithArg( + PRCallOnceType *once, + PRCallOnceWithArgFN func, + void *arg) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + PR_Lock(mod_init.ml); + PRIntn initialized = once->initialized; + PRStatus status = once->status; + PR_Unlock(mod_init.ml); + if (!initialized) { + if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) { + status = (*func)(arg); + PR_Lock(mod_init.ml); + once->status = status; + once->initialized = 1; + PR_NotifyAllCondVar(mod_init.cv); + PR_Unlock(mod_init.ml); + } else { + PR_Lock(mod_init.ml); + while (!once->initialized) { + PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); + } + status = once->status; + PR_Unlock(mod_init.ml); + if (PR_SUCCESS != status) { + PR_SetError(PR_CALL_ONCE_ERROR, 0); + } + } + return status; + } + if (PR_SUCCESS != status) { + PR_SetError(PR_CALL_ONCE_ERROR, 0); + } + return status; +} + +PRBool _PR_Obsolete(const char *obsolete, const char *preferred) +{ +#if defined(DEBUG) + PR_fprintf( + PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n", + obsolete, (NULL == preferred) ? "something else" : preferred); +#endif + return PR_FALSE; +} /* _PR_Obsolete */ + +/* prinit.c */ + + diff --git a/nsprpub/pr/src/misc/prinrval.c b/nsprpub/pr/src/misc/prinrval.c new file mode 100644 index 0000000000..79ff2cbca4 --- /dev/null +++ b/nsprpub/pr/src/misc/prinrval.c @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * file: prinrval.c + * description: implementation for the kernel interval timing functions + */ + +#include "primpl.h" + +/* + *----------------------------------------------------------------------- + * + * _PR_InitClock -- + * + * + *----------------------------------------------------------------------- + */ + +void _PR_InitClock(void) +{ + _PR_MD_INTERVAL_INIT(); +#ifdef DEBUG + { + PRIntervalTime ticksPerSec = PR_TicksPerSecond(); + + PR_ASSERT(ticksPerSec >= PR_INTERVAL_MIN); + PR_ASSERT(ticksPerSec <= PR_INTERVAL_MAX); + } +#endif /* DEBUG */ +} + +PR_IMPLEMENT(PRIntervalTime) PR_IntervalNow(void) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + return _PR_MD_GET_INTERVAL(); +} /* PR_IntervalNow */ + +PR_EXTERN(PRUint32) PR_TicksPerSecond(void) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + return _PR_MD_INTERVAL_PER_SEC(); +} /* PR_TicksPerSecond */ + +PR_IMPLEMENT(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds) +{ + return seconds * PR_TicksPerSecond(); +} /* PR_SecondsToInterval */ + +PR_IMPLEMENT(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli) +{ + PRIntervalTime ticks; + PRUint64 tock, tps, msecPerSec, rounding; + LL_UI2L(tock, milli); + LL_I2L(msecPerSec, PR_MSEC_PER_SEC); + LL_I2L(rounding, (PR_MSEC_PER_SEC >> 1)); + LL_I2L(tps, PR_TicksPerSecond()); + LL_MUL(tock, tock, tps); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, msecPerSec); + LL_L2UI(ticks, tock); + return ticks; +} /* PR_MillisecondsToInterval */ + +PR_IMPLEMENT(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro) +{ + PRIntervalTime ticks; + PRUint64 tock, tps, usecPerSec, rounding; + LL_UI2L(tock, micro); + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_I2L(rounding, (PR_USEC_PER_SEC >> 1)); + LL_I2L(tps, PR_TicksPerSecond()); + LL_MUL(tock, tock, tps); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, usecPerSec); + LL_L2UI(ticks, tock); + return ticks; +} /* PR_MicrosecondsToInterval */ + +PR_IMPLEMENT(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks) +{ + return ticks / PR_TicksPerSecond(); +} /* PR_IntervalToSeconds */ + +PR_IMPLEMENT(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks) +{ + PRUint32 milli; + PRUint64 tock, tps, msecPerSec, rounding; + LL_UI2L(tock, ticks); + LL_I2L(msecPerSec, PR_MSEC_PER_SEC); + LL_I2L(tps, PR_TicksPerSecond()); + LL_USHR(rounding, tps, 1); + LL_MUL(tock, tock, msecPerSec); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, tps); + LL_L2UI(milli, tock); + return milli; +} /* PR_IntervalToMilliseconds */ + +PR_IMPLEMENT(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks) +{ + PRUint32 micro; + PRUint64 tock, tps, usecPerSec, rounding; + LL_UI2L(tock, ticks); + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_I2L(tps, PR_TicksPerSecond()); + LL_USHR(rounding, tps, 1); + LL_MUL(tock, tock, usecPerSec); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, tps); + LL_L2UI(micro, tock); + return micro; +} /* PR_IntervalToMicroseconds */ + +/* prinrval.c */ + diff --git a/nsprpub/pr/src/misc/pripc.c b/nsprpub/pr/src/misc/pripc.c new file mode 100644 index 0000000000..64415dd631 --- /dev/null +++ b/nsprpub/pr/src/misc/pripc.c @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * 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 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/nsprpub/pr/src/misc/pripcsem.c b/nsprpub/pr/src/misc/pripcsem.c new file mode 100644 index 0000000000..49b051b74d --- /dev/null +++ b/nsprpub/pr/src/misc/pripcsem.c @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * 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/nsprpub/pr/src/misc/prlog2.c b/nsprpub/pr/src/misc/prlog2.c new file mode 100644 index 0000000000..2d476cd711 --- /dev/null +++ b/nsprpub/pr/src/misc/prlog2.c @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prbit.h" + +/* +** Compute the log of the least power of 2 greater than or equal to n +*/ +PR_IMPLEMENT(PRIntn) PR_CeilingLog2(PRUint32 n) +{ + PRIntn log2; + PR_CEILING_LOG2(log2, n); + return log2; +} + +/* +** Compute the log of the greatest power of 2 less than or equal to n. +** This really just finds the highest set bit in the word. +*/ +PR_IMPLEMENT(PRIntn) PR_FloorLog2(PRUint32 n) +{ + PRIntn log2; + PR_FLOOR_LOG2(log2, n); + return log2; +} diff --git a/nsprpub/pr/src/misc/prlong.c b/nsprpub/pr/src/misc/prlong.c new file mode 100644 index 0000000000..9c5abf06bb --- /dev/null +++ b/nsprpub/pr/src/misc/prlong.c @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prlong.h" + +static PRInt64 ll_zero = PR_INT64(0x0000000000000000); +static PRInt64 ll_maxint = PR_INT64(0x7fffffffffffffff); +static PRInt64 ll_minint = PR_INT64(0x8000000000000000); +static PRUint64 ll_maxuint = PR_UINT64(0xffffffffffffffff); + +PR_IMPLEMENT(PRInt64) LL_Zero(void) { + return ll_zero; +} +PR_IMPLEMENT(PRInt64) LL_MaxInt(void) { + return ll_maxint; +} +PR_IMPLEMENT(PRInt64) LL_MinInt(void) { + return ll_minint; +} +PR_IMPLEMENT(PRUint64) LL_MaxUint(void) { + return ll_maxuint; +} + +#ifndef HAVE_LONG_LONG +/* +** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1. +*/ +static void norm_udivmod32(PRUint32 *qp, PRUint32 *rp, PRUint64 a, PRUint32 b) +{ + PRUint32 d1, d0, q1, q0; + PRUint32 r1, r0, m; + + d1 = _hi16(b); + d0 = _lo16(b); + r1 = a.hi % d1; + q1 = a.hi / d1; + m = q1 * d0; + r1 = (r1 << 16) | _hi16(a.lo); + if (r1 < m) { + q1--, r1 += b; + if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */ + && r1 < m) { + q1--, r1 += b; + } + } + r1 -= m; + r0 = r1 % d1; + q0 = r1 / d1; + m = q0 * d0; + r0 = (r0 << 16) | _lo16(a.lo); + if (r0 < m) { + q0--, r0 += b; + if (r0 >= b + && r0 < m) { + q0--, r0 += b; + } + } + *qp = (q1 << 16) | q0; + *rp = r0 - m; +} + +static PRUint32 CountLeadingZeros(PRUint32 a) +{ + PRUint32 t; + PRUint32 r = 32; + + if ((t = a >> 16) != 0) { + r -= 16, a = t; + } + if ((t = a >> 8) != 0) { + r -= 8, a = t; + } + if ((t = a >> 4) != 0) { + r -= 4, a = t; + } + if ((t = a >> 2) != 0) { + r -= 2, a = t; + } + if ((t = a >> 1) != 0) { + r -= 1, a = t; + } + if (a & 1) { + r--; + } + return r; +} + +PR_IMPLEMENT(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b) +{ + PRUint32 n0, n1, n2; + PRUint32 q0, q1; + PRUint32 rsh, lsh; + + n0 = a.lo; + n1 = a.hi; + + if (b.hi == 0) { + if (b.lo > n1) { + /* (0 q0) = (n1 n0) / (0 D0) */ + + lsh = CountLeadingZeros(b.lo); + + if (lsh) { + /* + * Normalize, i.e. make the most significant bit of the + * denominator be set. + */ + b.lo = b.lo << lsh; + n1 = (n1 << lsh) | (n0 >> (32 - lsh)); + n0 = n0 << lsh; + } + + a.lo = n0, a.hi = n1; + norm_udivmod32(&q0, &n0, a, b.lo); + q1 = 0; + + /* remainder is in n0 >> lsh */ + } else { + /* (q1 q0) = (n1 n0) / (0 d0) */ + + if (b.lo == 0) { /* user wants to divide by zero! */ + b.lo = 1 / b.lo; /* so go ahead and crash */ + } + + lsh = CountLeadingZeros(b.lo); + + if (lsh == 0) { + /* + * From (n1 >= b.lo) + * && (the most significant bit of b.lo is set), + * conclude that + * (the most significant bit of n1 is set) + * && (the leading quotient digit q1 = 1). + * + * This special case is necessary, not an optimization + * (Shifts counts of 32 are undefined). + */ + n1 -= b.lo; + q1 = 1; + } else { + /* + * Normalize. + */ + rsh = 32 - lsh; + + b.lo = b.lo << lsh; + n2 = n1 >> rsh; + n1 = (n1 << lsh) | (n0 >> rsh); + n0 = n0 << lsh; + + a.lo = n1, a.hi = n2; + norm_udivmod32(&q1, &n1, a, b.lo); + } + + /* n1 != b.lo... */ + + a.lo = n0, a.hi = n1; + norm_udivmod32(&q0, &n0, a, b.lo); + + /* remainder in n0 >> lsh */ + } + + if (rp) { + rp->lo = n0 >> lsh; + rp->hi = 0; + } + } else { + if (b.hi > n1) { + /* (0 0) = (n1 n0) / (D1 d0) */ + + q0 = 0; + q1 = 0; + + /* remainder in (n1 n0) */ + if (rp) { + rp->lo = n0; + rp->hi = n1; + } + } else { + /* (0 q0) = (n1 n0) / (d1 d0) */ + + lsh = CountLeadingZeros(b.hi); + if (lsh == 0) { + /* + * From (n1 >= b.hi) + * && (the most significant bit of b.hi is set), + * conclude that + * (the most significant bit of n1 is set) + * && (the quotient digit q0 = 0 or 1). + * + * This special case is necessary, not an optimization. + */ + + /* + * The condition on the next line takes advantage of that + * n1 >= b.hi (true due to control flow). + */ + if (n1 > b.hi || n0 >= b.lo) { + q0 = 1; + a.lo = n0, a.hi = n1; + LL_SUB(a, a, b); + } else { + q0 = 0; + } + q1 = 0; + + if (rp) { + rp->lo = n0; + rp->hi = n1; + } + } else { + PRInt64 m; + + /* + * Normalize. + */ + rsh = 32 - lsh; + + b.hi = (b.hi << lsh) | (b.lo >> rsh); + b.lo = b.lo << lsh; + n2 = n1 >> rsh; + n1 = (n1 << lsh) | (n0 >> rsh); + n0 = n0 << lsh; + + a.lo = n1, a.hi = n2; + norm_udivmod32(&q0, &n1, a, b.hi); + LL_MUL32(m, q0, b.lo); + + if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) { + q0--; + LL_SUB(m, m, b); + } + + q1 = 0; + + /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */ + if (rp) { + a.lo = n0, a.hi = n1; + LL_SUB(a, a, m); + rp->lo = (a.hi << rsh) | (a.lo >> lsh); + rp->hi = a.hi >> lsh; + } + } + } + } + + if (qp) { + qp->lo = q0; + qp->hi = q1; + } +} +#endif /* !HAVE_LONG_LONG */ diff --git a/nsprpub/pr/src/misc/prnetdb.c b/nsprpub/pr/src/misc/prnetdb.c new file mode 100644 index 0000000000..7decd96751 --- /dev/null +++ b/nsprpub/pr/src/misc/prnetdb.c @@ -0,0 +1,2549 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> + +#if defined(LINUX) +#include <sys/un.h> +#endif + +/* + * On Unix, the error code for gethostbyname() and gethostbyaddr() + * is returned in the global variable h_errno, instead of the usual + * errno. + */ +#if defined(XP_UNIX) +#if defined(_PR_NEED_H_ERRNO) +extern int h_errno; +#endif +#define _MD_GETHOST_ERRNO() h_errno +#else +#define _MD_GETHOST_ERRNO() _MD_ERRNO() +#endif + +/* + * The meaning of the macros related to gethostbyname, gethostbyaddr, + * and gethostbyname2 is defined below. + * - _PR_HAVE_THREADSAFE_GETHOST: the gethostbyXXX functions return + * the result in thread specific storage. For example, AIX, HP-UX. + * - _PR_HAVE_GETHOST_R: have the gethostbyXXX_r functions. See next + * two macros. + * - _PR_HAVE_GETHOST_R_INT: the gethostbyXXX_r functions return an + * int. For example, Linux glibc. + * - _PR_HAVE_GETHOST_R_POINTER: the gethostbyXXX_r functions return + * a struct hostent* pointer. For example, Solaris. + */ +#if defined(_PR_NO_PREEMPT) || defined(_PR_HAVE_GETHOST_R) \ + || defined(_PR_HAVE_THREADSAFE_GETHOST) +#define _PR_NO_DNS_LOCK +#endif + +#if defined(_PR_NO_DNS_LOCK) +#define LOCK_DNS() +#define UNLOCK_DNS() +#else +PRLock *_pr_dnsLock = NULL; +#define LOCK_DNS() PR_Lock(_pr_dnsLock) +#define UNLOCK_DNS() PR_Unlock(_pr_dnsLock) +#endif /* defined(_PR_NO_DNS_LOCK) */ + +/* + * Some platforms have the reentrant getprotobyname_r() and + * getprotobynumber_r(). However, they come in three flavors. + * Some return a pointer to struct protoent, others return + * an int, and glibc's flavor takes five arguments. + */ + +#if defined(SOLARIS) || (defined(BSDI) && defined(_REENTRANT)) \ + || (defined(LINUX) && defined(_REENTRANT) \ + && defined(__GLIBC__) && __GLIBC__ < 2) +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_GETPROTO_R_POINTER +#endif + +#if defined(AIX4_3_PLUS) || (defined(AIX) && defined(_THREAD_SAFE)) \ + || (defined(HPUX10_10) && defined(_REENTRANT)) \ + || (defined(HPUX10_20) && defined(_REENTRANT)) \ + || defined(OPENBSD) +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_GETPROTO_R_INT +#endif + +#if __FreeBSD_version >= 602000 +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_5_ARG_GETPROTO_R +#endif + +/* BeOS has glibc but not the glibc-style getprotobyxxx_r functions. */ +#if (defined(__GLIBC__) && __GLIBC__ >= 2) +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_5_ARG_GETPROTO_R +#endif + +#if !defined(_PR_HAVE_GETPROTO_R) +PRLock* _getproto_lock = NULL; +#endif + +#if defined(_PR_INET6_PROBE) +extern PRBool _pr_ipv6_is_present(void); +#endif + +#define _PR_IN6_IS_ADDR_UNSPECIFIED(a) \ + (((a)->pr_s6_addr32[0] == 0) && \ + ((a)->pr_s6_addr32[1] == 0) && \ + ((a)->pr_s6_addr32[2] == 0) && \ + ((a)->pr_s6_addr32[3] == 0)) + +#define _PR_IN6_IS_ADDR_LOOPBACK(a) \ + (((a)->pr_s6_addr32[0] == 0) && \ + ((a)->pr_s6_addr32[1] == 0) && \ + ((a)->pr_s6_addr32[2] == 0) && \ + ((a)->pr_s6_addr[12] == 0) && \ + ((a)->pr_s6_addr[13] == 0) && \ + ((a)->pr_s6_addr[14] == 0) && \ + ((a)->pr_s6_addr[15] == 0x1U)) + +const PRIPv6Addr _pr_in6addr_any = {{{ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + } + } +}; + +const PRIPv6Addr _pr_in6addr_loopback = {{{ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x1U + } + } +}; +/* + * The values at bytes 10 and 11 are compared using pointers to + * 8-bit fields, and not 32-bit fields, to make the comparison work on + * both big-endian and little-endian systems + */ + +#define _PR_IN6_IS_ADDR_V4MAPPED(a) \ + (((a)->pr_s6_addr32[0] == 0) && \ + ((a)->pr_s6_addr32[1] == 0) && \ + ((a)->pr_s6_addr[8] == 0) && \ + ((a)->pr_s6_addr[9] == 0) && \ + ((a)->pr_s6_addr[10] == 0xff) && \ + ((a)->pr_s6_addr[11] == 0xff)) + +#define _PR_IN6_IS_ADDR_V4COMPAT(a) \ + (((a)->pr_s6_addr32[0] == 0) && \ + ((a)->pr_s6_addr32[1] == 0) && \ + ((a)->pr_s6_addr32[2] == 0)) + +#define _PR_IN6_V4MAPPED_TO_IPADDR(a) ((a)->pr_s6_addr32[3]) + +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + +/* + * The _pr_QueryNetIfs() function finds out if the system has + * IPv4 or IPv6 source addresses configured and sets _pr_have_inet_if + * and _pr_have_inet6_if accordingly. + * + * We have an implementation using SIOCGIFCONF ioctl and a + * default implementation that simply sets _pr_have_inet_if + * and _pr_have_inet6_if to true. A better implementation + * would be to use the routing sockets (see Chapter 17 of + * W. Richard Stevens' Unix Network Programming, Vol. 1, 2nd. Ed.) + */ + +static PRLock *_pr_query_ifs_lock = NULL; +static PRBool _pr_have_inet_if = PR_FALSE; +static PRBool _pr_have_inet6_if = PR_FALSE; + +#undef DEBUG_QUERY_IFS + +#if defined(AIX) \ + || (defined(DARWIN) && !defined(HAVE_GETIFADDRS)) + +/* + * Use SIOCGIFCONF ioctl on platforms that don't have routing + * sockets. Warning: whether SIOCGIFCONF ioctl returns AF_INET6 + * network interfaces is not portable. + * + * The _pr_QueryNetIfs() function is derived from the code in + * src/lib/libc/net/getifaddrs.c in BSD Unix and the code in + * Section 16.6 of W. Richard Stevens' Unix Network Programming, + * Vol. 1, 2nd. Ed. + */ + +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> + +#ifdef DEBUG_QUERY_IFS +static void +_pr_PrintIfreq(struct ifreq *ifr) +{ + PRNetAddr addr; + struct sockaddr *sa; + const char* family; + char addrstr[64]; + + sa = &ifr->ifr_addr; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + family = "inet"; + memcpy(&addr.inet.ip, &sin->sin_addr, sizeof(sin->sin_addr)); + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + family = "inet6"; + memcpy(&addr.ipv6.ip, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); + } else { + return; /* skip if not AF_INET or AF_INET6 */ + } + addr.raw.family = sa->sa_family; + PR_NetAddrToString(&addr, addrstr, sizeof(addrstr)); + printf("%s: %s %s\n", ifr->ifr_name, family, addrstr); +} +#endif + +static void +_pr_QueryNetIfs(void) +{ + int sock; + int rv; + struct ifconf ifc; + struct ifreq *ifr; + struct ifreq *lifr; + PRUint32 len, lastlen; + char *buf; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + return; + } + + /* Issue SIOCGIFCONF request in a loop. */ + lastlen = 0; + len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ + for (;;) { + buf = (char *)PR_Malloc(len); + if (NULL == buf) { + close(sock); + return; + } + ifc.ifc_buf = buf; + ifc.ifc_len = len; + rv = ioctl(sock, SIOCGIFCONF, &ifc); + if (rv < 0) { + if (errno != EINVAL || lastlen != 0) { + close(sock); + PR_Free(buf); + return; + } + } else { + if (ifc.ifc_len == lastlen) { + break; /* success, len has not changed */ + } + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + PR_Free(buf); + } + close(sock); + + ifr = ifc.ifc_req; + lifr = (struct ifreq *)&ifc.ifc_buf[ifc.ifc_len]; + + while (ifr < lifr) { + struct sockaddr *sa; + int sa_len; + +#ifdef DEBUG_QUERY_IFS + _pr_PrintIfreq(ifr); +#endif + sa = &ifr->ifr_addr; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { + _pr_have_inet_if = PR_TRUE; + } + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) + && !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + _pr_have_inet6_if = PR_TRUE; + } + } + +#ifdef _PR_HAVE_SOCKADDR_LEN + sa_len = PR_MAX(sa->sa_len, sizeof(struct sockaddr)); +#else + switch (sa->sa_family) { +#ifdef AF_LINK + case AF_LINK: + sa_len = sizeof(struct sockaddr_dl); + break; +#endif + case AF_INET6: + sa_len = sizeof(struct sockaddr_in6); + break; + default: + sa_len = sizeof(struct sockaddr); + break; + } +#endif + ifr = (struct ifreq *)(((char *)sa) + sa_len); + } + PR_Free(buf); +} + +#elif (defined(DARWIN) && defined(HAVE_GETIFADDRS)) || defined(FREEBSD) \ + || defined(NETBSD) || defined(OPENBSD) + +/* + * Use the BSD getifaddrs function. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <ifaddrs.h> +#include <netinet/in.h> + +#ifdef DEBUG_QUERY_IFS +static void +_pr_PrintIfaddrs(struct ifaddrs *ifa) +{ + struct sockaddr *sa; + const char* family; + void *addrp; + char addrstr[64]; + + sa = ifa->ifa_addr; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + family = "inet"; + addrp = &sin->sin_addr; + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + family = "inet6"; + addrp = &sin6->sin6_addr; + } else { + return; /* skip if not AF_INET or AF_INET6 */ + } + inet_ntop(sa->sa_family, addrp, addrstr, sizeof(addrstr)); + printf("%s: %s %s\n", ifa->ifa_name, family, addrstr); +} +#endif + +static void +_pr_QueryNetIfs(void) +{ + struct ifaddrs *ifp; + struct ifaddrs *ifa; + + if (getifaddrs(&ifp) == -1) { + return; + } + for (ifa = ifp; ifa; ifa = ifa->ifa_next) { + struct sockaddr *sa; + +#ifdef DEBUG_QUERY_IFS + _pr_PrintIfaddrs(ifa); +#endif + sa = ifa->ifa_addr; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { + _pr_have_inet_if = 1; + } + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) + && !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + _pr_have_inet6_if = 1; + } + } + } + freeifaddrs(ifp); +} + +#else /* default */ + +/* + * Emulate the code in NSPR 4.2 or older. PR_GetIPNodeByName behaves + * as if the system had both IPv4 and IPv6 source addresses configured. + */ +static void +_pr_QueryNetIfs(void) +{ + _pr_have_inet_if = PR_TRUE; + _pr_have_inet6_if = PR_TRUE; +} + +#endif + +#endif /* _PR_INET6 && _PR_HAVE_GETHOSTBYNAME2 */ + +void _PR_InitNet(void) +{ +#if defined(XP_UNIX) +#ifdef HAVE_NETCONFIG + /* + * This one-liner prevents the endless re-open's and re-read's of + * /etc/netconfig on EACH and EVERY call to accept(), connect(), etc. + */ + (void)setnetconfig(); +#endif +#endif +#if !defined(_PR_NO_DNS_LOCK) + _pr_dnsLock = PR_NewLock(); +#endif +#if !defined(_PR_HAVE_GETPROTO_R) + _getproto_lock = PR_NewLock(); +#endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + _pr_query_ifs_lock = PR_NewLock(); +#endif +} + +void _PR_CleanupNet(void) +{ +#if !defined(_PR_NO_DNS_LOCK) + if (_pr_dnsLock) { + PR_DestroyLock(_pr_dnsLock); + _pr_dnsLock = NULL; + } +#endif +#if !defined(_PR_HAVE_GETPROTO_R) + if (_getproto_lock) { + PR_DestroyLock(_getproto_lock); + _getproto_lock = NULL; + } +#endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + if (_pr_query_ifs_lock) { + PR_DestroyLock(_pr_query_ifs_lock); + _pr_query_ifs_lock = NULL; + } +#endif +} + +/* +** Allocate space from the buffer, aligning it to "align" before doing +** the allocation. "align" must be a power of 2. +*/ +static char *Alloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align) +{ + char *buf = *bufp; + PRIntn buflen = *buflenp; + + if (align && ((long)buf & (align - 1))) { + PRIntn skip = align - ((ptrdiff_t)buf & (align - 1)); + if (buflen < skip) { + return 0; + } + buf += skip; + buflen -= skip; + } + if (buflen < amount) { + return 0; + } + *bufp = buf + amount; + *buflenp = buflen - amount; + return buf; +} + +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); +} + +/* +** 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); +} + +/* +** 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, + _PRIPAddrConversion conversion, + PRHostEnt *to) +{ + PRIntn len, na; + char **ap; + + if (conversion != _PRIPAddrNoConversion + && from->h_addrtype == AF_INET) { + PR_ASSERT(from->h_length == 4); + to->h_addrtype = PR_AF_INET6; + to->h_length = 16; + } else { +#if defined(_PR_INET6) || defined(_PR_INET6_PROBE) + if (AF_INET6 == from->h_addrtype) { + to->h_addrtype = PR_AF_INET6; + } + else +#endif + to->h_addrtype = from->h_addrtype; + to->h_length = from->h_length; + } + + /* Copy the official name */ + if (!from->h_name) { + return PR_FAILURE; + } + len = strlen(from->h_name) + 1; + to->h_name = Alloc(len, buf, bufsize, 0); + if (!to->h_name) { + return PR_FAILURE; + } + memcpy(to->h_name, from->h_name, len); + + /* Count the aliases, then allocate storage for the pointers */ + if (!from->h_aliases) { + na = 1; + } else { + for (na = 1, ap = from->h_aliases; *ap != 0; na++, ap++) {;} /* nothing to execute */ + } + to->h_aliases = (char**)Alloc( + na * sizeof(char*), buf, bufsize, sizeof(char**)); + if (!to->h_aliases) { + return PR_FAILURE; + } + + /* Copy the aliases, one at a time */ + if (!from->h_aliases) { + to->h_aliases[0] = 0; + } else { + for (na = 0, ap = from->h_aliases; *ap != 0; na++, ap++) { + len = strlen(*ap) + 1; + to->h_aliases[na] = Alloc(len, buf, bufsize, 0); + if (!to->h_aliases[na]) { + return PR_FAILURE; + } + memcpy(to->h_aliases[na], *ap, len); + } + to->h_aliases[na] = 0; + } + + /* Count the addresses, then allocate storage for the pointers */ + for (na = 1, ap = from->h_addr_list; *ap != 0; na++, ap++) {;} /* nothing to execute */ + to->h_addr_list = (char**)Alloc( + na * sizeof(char*), buf, bufsize, sizeof(char**)); + if (!to->h_addr_list) { + return PR_FAILURE; + } + + /* Copy the addresses, one at a time */ + for (na = 0, ap = from->h_addr_list; *ap != 0; na++, ap++) { + to->h_addr_list[na] = Alloc(to->h_length, buf, bufsize, 0); + if (!to->h_addr_list[na]) { + return PR_FAILURE; + } + 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); + } + } + to->h_addr_list[na] = 0; + return PR_SUCCESS; +} + +#if !defined(_PR_HAVE_GETPROTO_R) +/* +** Copy a protoent, and all of the memory that it refers to into +** (hopefully) stacked buffers. +*/ +static PRStatus CopyProtoent( + struct protoent *from, char *buf, PRIntn bufsize, PRProtoEnt *to) +{ + PRIntn len, na; + char **ap; + + /* Do the easy stuff */ + to->p_num = from->p_proto; + + /* Copy the official name */ + if (!from->p_name) { + return PR_FAILURE; + } + len = strlen(from->p_name) + 1; + to->p_name = Alloc(len, &buf, &bufsize, 0); + if (!to->p_name) { + return PR_FAILURE; + } + memcpy(to->p_name, from->p_name, len); + + /* Count the aliases, then allocate storage for the pointers */ + for (na = 1, ap = from->p_aliases; *ap != 0; na++, ap++) {;} /* nothing to execute */ + to->p_aliases = (char**)Alloc( + na * sizeof(char*), &buf, &bufsize, sizeof(char**)); + if (!to->p_aliases) { + return PR_FAILURE; + } + + /* Copy the aliases, one at a time */ + for (na = 0, ap = from->p_aliases; *ap != 0; na++, ap++) { + len = strlen(*ap) + 1; + to->p_aliases[na] = Alloc(len, &buf, &bufsize, 0); + if (!to->p_aliases[na]) { + return PR_FAILURE; + } + memcpy(to->p_aliases[na], *ap, len); + } + to->p_aliases[na] = 0; + + return PR_SUCCESS; +} +#endif /* !defined(_PR_HAVE_GETPROTO_R) */ + +/* + * ################################################################# + * NOTE: tmphe, tmpbuf, bufsize, h, and h_err are local variables + * or arguments of PR_GetHostByName, PR_GetIPNodeByName, and + * PR_GetHostByAddr. DO NOT CHANGE THE NAMES OF THESE LOCAL + * VARIABLES OR ARGUMENTS. + * ################################################################# + */ +#if defined(_PR_HAVE_GETHOST_R_INT) + +#define GETHOSTBYNAME(name) \ + (gethostbyname_r(name, &tmphe, tmpbuf, bufsize, &h, &h_err), h) +#define GETHOSTBYNAME2(name, af) \ + (gethostbyname2_r(name, af, &tmphe, tmpbuf, bufsize, &h, &h_err), h) +#define GETHOSTBYADDR(addr, addrlen, af) \ + (gethostbyaddr_r(addr, addrlen, af, \ + &tmphe, tmpbuf, bufsize, &h, &h_err), h) + +#elif defined(_PR_HAVE_GETHOST_R_POINTER) + +#define GETHOSTBYNAME(name) \ + gethostbyname_r(name, &tmphe, tmpbuf, bufsize, &h_err) +#define GETHOSTBYNAME2(name, af) \ + gethostbyname2_r(name, af, &tmphe, tmpbuf, bufsize, &h_err) +#define GETHOSTBYADDR(addr, addrlen, af) \ + gethostbyaddr_r(addr, addrlen, af, &tmphe, tmpbuf, bufsize, &h_err) + +#else + +#define GETHOSTBYNAME(name) gethostbyname(name) +#define GETHOSTBYNAME2(name, af) gethostbyname2(name, af) +#define GETHOSTBYADDR(addr, addrlen, af) gethostbyaddr(addr, addrlen, af) + +#endif /* definition of GETHOSTBYXXX */ + +PR_IMPLEMENT(PRStatus) PR_GetHostByName( + const char *name, char *buf, PRIntn bufsize, PRHostEnt *hp) +{ + struct hostent *h; + PRStatus rv = PR_FAILURE; +#if defined(_PR_HAVE_GETHOST_R) + char localbuf[PR_NETDB_BUF_SIZE]; + char *tmpbuf; + struct hostent tmphe; + int h_err; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#if defined(_PR_HAVE_GETHOST_R) + tmpbuf = localbuf; + if (bufsize > sizeof(localbuf)) + { + tmpbuf = (char *)PR_Malloc(bufsize); + if (NULL == tmpbuf) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return rv; + } + } +#endif + + LOCK_DNS(); + + h = GETHOSTBYNAME(name); + + if (NULL == h) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); + } + else + { + _PRIPAddrConversion conversion = _PRIPAddrNoConversion; + rv = CopyHostent(h, &buf, &bufsize, conversion, hp); + if (PR_SUCCESS != rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + } + UNLOCK_DNS(); +#if defined(_PR_HAVE_GETHOST_R) + if (tmpbuf != localbuf) { + PR_Free(tmpbuf); + } +#endif + return rv; +} + +#if !defined(_PR_INET6) && \ + defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME) +typedef struct hostent * (*_pr_getipnodebyname_t)(const char *, int, + int, int *); +typedef struct hostent * (*_pr_getipnodebyaddr_t)(const void *, size_t, + int, int *); +typedef void (*_pr_freehostent_t)(struct hostent *); +static void * _pr_getipnodebyname_fp; +static void * _pr_getipnodebyaddr_fp; +static void * _pr_freehostent_fp; + +/* + * Look up the addresses of getipnodebyname, getipnodebyaddr, + * and freehostent. + */ +PRStatus +_pr_find_getipnodebyname(void) +{ + PRLibrary *lib; + PRStatus rv; +#define GETIPNODEBYNAME "getipnodebyname" +#define GETIPNODEBYADDR "getipnodebyaddr" +#define FREEHOSTENT "freehostent" + + _pr_getipnodebyname_fp = PR_FindSymbolAndLibrary(GETIPNODEBYNAME, &lib); + if (NULL != _pr_getipnodebyname_fp) { + _pr_freehostent_fp = PR_FindSymbol(lib, FREEHOSTENT); + if (NULL != _pr_freehostent_fp) { + _pr_getipnodebyaddr_fp = PR_FindSymbol(lib, GETIPNODEBYADDR); + if (NULL != _pr_getipnodebyaddr_fp) { + rv = PR_SUCCESS; + } + else { + rv = PR_FAILURE; + } + } else { + rv = PR_FAILURE; + } + (void)PR_UnloadLibrary(lib); + } else { + rv = PR_FAILURE; + } + return rv; +} +#endif + +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) +/* +** Append the V4 addresses to the end of the list +*/ +static PRStatus AppendV4AddrsToHostent( + struct hostent *from, + char **buf, + PRIntn *bufsize, + PRHostEnt *to) +{ + PRIntn na, na_old; + char **ap; + char **new_addr_list; + + /* Count the addresses, then grow storage for the pointers */ + for (na_old = 0, ap = to->h_addr_list; *ap != 0; na_old++, ap++) + {;} /* nothing to execute */ + for (na = na_old + 1, ap = from->h_addr_list; *ap != 0; na++, ap++) + {;} /* nothing to execute */ + new_addr_list = (char**)Alloc( + na * sizeof(char*), buf, bufsize, sizeof(char**)); + if (!new_addr_list) { + return PR_FAILURE; + } + + /* Copy the V6 addresses, one at a time */ + for (na = 0, ap = to->h_addr_list; *ap != 0; na++, ap++) { + new_addr_list[na] = to->h_addr_list[na]; + } + to->h_addr_list = new_addr_list; + + /* Copy the V4 addresses, one at a time */ + for (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; + } + MakeIPv4MappedAddr(*ap, to->h_addr_list[na]); + } + to->h_addr_list[na] = 0; + return PR_SUCCESS; +} +#endif + +PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName( + const char *name, PRUint16 af, PRIntn flags, + char *buf, PRIntn bufsize, PRHostEnt *hp) +{ + struct hostent *h = 0; + PRStatus rv = PR_FAILURE; +#if defined(_PR_HAVE_GETHOST_R) + char localbuf[PR_NETDB_BUF_SIZE]; + char *tmpbuf; + struct hostent tmphe; + int h_err; +#endif +#if defined(_PR_HAVE_GETIPNODEBYNAME) + PRUint16 md_af = af; + int error_num; + int tmp_flags = 0; +#endif +#if defined(_PR_HAVE_GETHOSTBYNAME2) + PRBool did_af_inet = PR_FALSE; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (af != PR_AF_INET && af != PR_AF_INET6) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + PR_Lock(_pr_query_ifs_lock); + /* + * Keep querying the presence of IPv4 and IPv6 interfaces until + * at least one is up. This allows us to detect the local + * machine going from offline to online. + */ + if (!_pr_have_inet_if && !_pr_have_inet6_if) { + _pr_QueryNetIfs(); +#ifdef DEBUG_QUERY_IFS + if (_pr_have_inet_if) { + printf("Have IPv4 source address\n"); + } + if (_pr_have_inet6_if) { + printf("Have IPv6 source address\n"); + } +#endif + } + PR_Unlock(_pr_query_ifs_lock); +#endif + +#if defined(_PR_HAVE_GETIPNODEBYNAME) + if (flags & PR_AI_V4MAPPED) { + tmp_flags |= AI_V4MAPPED; + } + if (flags & PR_AI_ADDRCONFIG) { + tmp_flags |= AI_ADDRCONFIG; + } + if (flags & PR_AI_ALL) { + tmp_flags |= AI_ALL; + } + if (af == PR_AF_INET6) { + md_af = AF_INET6; + } + else { + md_af = af; + } +#endif + +#if defined(_PR_HAVE_GETHOST_R) + tmpbuf = localbuf; + if (bufsize > sizeof(localbuf)) + { + tmpbuf = (char *)PR_Malloc(bufsize); + if (NULL == tmpbuf) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return rv; + } + } +#endif + + /* Do not need to lock the DNS lock if getipnodebyname() is called */ +#ifdef _PR_INET6 +#ifdef _PR_HAVE_GETHOSTBYNAME2 + LOCK_DNS(); + if (af == PR_AF_INET6) + { + if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet6_if) + { +#ifdef _PR_INET6_PROBE + if (_pr_ipv6_is_present()) +#endif + h = GETHOSTBYNAME2(name, AF_INET6); + } + if ((NULL == h) && (flags & PR_AI_V4MAPPED) + && ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if)) + { + did_af_inet = PR_TRUE; + h = GETHOSTBYNAME2(name, AF_INET); + } + } + else + { + if ((flags & PR_AI_ADDRCONFIG) == 0 || _pr_have_inet_if) + { + did_af_inet = PR_TRUE; + h = GETHOSTBYNAME2(name, af); + } + } +#elif defined(_PR_HAVE_GETIPNODEBYNAME) + h = getipnodebyname(name, md_af, tmp_flags, &error_num); +#else +#error "Unknown name-to-address translation function" +#endif /* _PR_HAVE_GETHOSTBYNAME2 */ +#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME) + if (_pr_ipv6_is_present()) + { +#ifdef PR_GETIPNODE_NOT_THREADSAFE + LOCK_DNS(); +#endif + h = (*((_pr_getipnodebyname_t)_pr_getipnodebyname_fp))(name, md_af, tmp_flags, &error_num); + } + else + { + LOCK_DNS(); + h = GETHOSTBYNAME(name); + } +#else /* _PR_INET6 */ + LOCK_DNS(); + h = GETHOSTBYNAME(name); +#endif /* _PR_INET6 */ + + if (NULL == h) + { +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); +#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME) + if (_pr_ipv6_is_present()) { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); + } + else { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); + } +#else + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); +#endif + } + else + { + _PRIPAddrConversion conversion = _PRIPAddrNoConversion; + + if (af == PR_AF_INET6) { + conversion = _PRIPAddrIPv4Mapped; + } + rv = CopyHostent(h, &buf, &bufsize, conversion, hp); + if (PR_SUCCESS != rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + freehostent(h); +#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME) + if (_pr_ipv6_is_present()) { + (*((_pr_freehostent_t)_pr_freehostent_fp))(h); + } +#endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETHOSTBYNAME2) + if ((PR_SUCCESS == rv) && (flags & PR_AI_V4MAPPED) + && ((flags & PR_AI_ALL) + || ((flags & PR_AI_ADDRCONFIG) && _pr_have_inet_if)) + && !did_af_inet && (h = GETHOSTBYNAME2(name, AF_INET)) != 0) { + rv = AppendV4AddrsToHostent(h, &buf, &bufsize, hp); + if (PR_SUCCESS != rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + } +#endif + } + + /* Must match the convoluted logic above for LOCK_DNS() */ +#ifdef _PR_INET6 +#ifdef _PR_HAVE_GETHOSTBYNAME2 + UNLOCK_DNS(); +#endif /* _PR_HAVE_GETHOSTBYNAME2 */ +#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYNAME) +#ifdef PR_GETIPNODE_NOT_THREADSAFE + UNLOCK_DNS(); +#else + if (!_pr_ipv6_is_present()) { + UNLOCK_DNS(); + } +#endif +#else /* _PR_INET6 */ + UNLOCK_DNS(); +#endif /* _PR_INET6 */ + +#if defined(_PR_HAVE_GETHOST_R) + if (tmpbuf != localbuf) { + PR_Free(tmpbuf); + } +#endif + + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_GetHostByAddr( + const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry) +{ + struct hostent *h; + PRStatus rv = PR_FAILURE; + const void *addr; + PRUint32 tmp_ip; + int addrlen; + PRInt32 af; +#if defined(_PR_HAVE_GETHOST_R) + char localbuf[PR_NETDB_BUF_SIZE]; + char *tmpbuf; + struct hostent tmphe; + int h_err; +#endif +#if defined(_PR_HAVE_GETIPNODEBYADDR) + int error_num; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (hostaddr->raw.family == PR_AF_INET6) + { +#if defined(_PR_INET6_PROBE) + af = _pr_ipv6_is_present() ? AF_INET6 : AF_INET; +#elif defined(_PR_INET6) + af = AF_INET6; +#else + af = AF_INET; +#endif +#if defined(_PR_GHBA_DISALLOW_V4MAPPED) + if (_PR_IN6_IS_ADDR_V4MAPPED(&hostaddr->ipv6.ip)) { + af = AF_INET; + } +#endif + } + else + { + PR_ASSERT(hostaddr->raw.family == AF_INET); + af = AF_INET; + } + if (hostaddr->raw.family == PR_AF_INET6) { +#if defined(_PR_INET6) || defined(_PR_INET6_PROBE) + if (af == AF_INET6) { + addr = &hostaddr->ipv6.ip; + addrlen = sizeof(hostaddr->ipv6.ip); + } + else +#endif + { + PR_ASSERT(af == AF_INET); + if (!_PR_IN6_IS_ADDR_V4MAPPED(&hostaddr->ipv6.ip)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return rv; + } + tmp_ip = _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *) + &hostaddr->ipv6.ip); + addr = &tmp_ip; + addrlen = sizeof(tmp_ip); + } + } else { + PR_ASSERT(hostaddr->raw.family == AF_INET); + PR_ASSERT(af == AF_INET); + addr = &hostaddr->inet.ip; + addrlen = sizeof(hostaddr->inet.ip); + } + +#if defined(_PR_HAVE_GETHOST_R) + tmpbuf = localbuf; + if (bufsize > sizeof(localbuf)) + { + tmpbuf = (char *)PR_Malloc(bufsize); + if (NULL == tmpbuf) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return rv; + } + } +#endif + + /* Do not need to lock the DNS lock if getipnodebyaddr() is called */ +#if defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6) + h = getipnodebyaddr(addr, addrlen, af, &error_num); +#elif defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6_PROBE) + if (_pr_ipv6_is_present()) + { +#ifdef PR_GETIPNODE_NOT_THREADSAFE + LOCK_DNS(); +#endif + h = (*((_pr_getipnodebyaddr_t)_pr_getipnodebyaddr_fp))(addr, addrlen, + af, &error_num); + } + else + { + LOCK_DNS(); + h = GETHOSTBYADDR(addr, addrlen, af); + } +#else /* _PR_HAVE_GETIPNODEBYADDR */ + LOCK_DNS(); + h = GETHOSTBYADDR(addr, addrlen, af); +#endif /* _PR_HAVE_GETIPNODEBYADDR */ + if (NULL == h) + { +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR) + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); +#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYADDR) + if (_pr_ipv6_is_present()) { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); + } + else { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); + } +#else + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); +#endif + } + else + { + _PRIPAddrConversion conversion = _PRIPAddrNoConversion; + if (hostaddr->raw.family == PR_AF_INET6) { + if (af == AF_INET) { + if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr*) + &hostaddr->ipv6.ip)) { + conversion = _PRIPAddrIPv4Mapped; + } else if (_PR_IN6_IS_ADDR_V4COMPAT((PRIPv6Addr *) + &hostaddr->ipv6.ip)) { + conversion = _PRIPAddrIPv4Compat; + } + } + } + rv = CopyHostent(h, &buf, &bufsize, conversion, hostentry); + if (PR_SUCCESS != rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR) + freehostent(h); +#elif defined(_PR_INET6_PROBE) && defined(_PR_HAVE_GETIPNODEBYADDR) + if (_pr_ipv6_is_present()) { + (*((_pr_freehostent_t)_pr_freehostent_fp))(h); + } +#endif + } + + /* Must match the convoluted logic above for LOCK_DNS() */ +#if defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6) +#elif defined(_PR_HAVE_GETIPNODEBYADDR) && defined(_PR_INET6_PROBE) +#ifdef PR_GETIPNODE_NOT_THREADSAFE + UNLOCK_DNS(); +#else + if (!_pr_ipv6_is_present()) { + UNLOCK_DNS(); + } +#endif +#else /* _PR_HAVE_GETIPNODEBYADDR */ + UNLOCK_DNS(); +#endif /* _PR_HAVE_GETIPNODEBYADDR */ + +#if defined(_PR_HAVE_GETHOST_R) + if (tmpbuf != localbuf) { + PR_Free(tmpbuf); + } +#endif + + return rv; +} + +/******************************************************************************/ +/* + * Some systems define a reentrant version of getprotobyname(). Too bad + * the signature isn't always the same. But hey, they tried. If there + * is such a definition, use it. Otherwise, grab a lock and do it here. + */ +/******************************************************************************/ + +#if !defined(_PR_HAVE_GETPROTO_R) +/* + * This may seem like a silly thing to do, but the compiler SHOULD + * complain if getprotobyname_r() is implemented on some system and + * we're not using it. For sure these signatures are different than + * any usable implementation. + */ + +#if defined(ANDROID) +/* Android's Bionic libc system includes prototypes for these in netdb.h, + * but doesn't actually include implementations. It uses the 5-arg form, + * so these functions end up not matching the prototype. So just rename + * them if not found. + */ +#define getprotobyname_r _pr_getprotobyname_r +#define getprotobynumber_r _pr_getprotobynumber_r +#endif + +static struct protoent *getprotobyname_r(const char* name) +{ + return getprotobyname(name); +} /* getprotobyname_r */ + +static struct protoent *getprotobynumber_r(PRInt32 number) +{ + return getprotobynumber(number); +} /* getprotobynumber_r */ + +#endif /* !defined(_PR_HAVE_GETPROTO_R) */ + +PR_IMPLEMENT(PRStatus) PR_GetProtoByName( + const char* name, char* buffer, PRInt32 buflen, PRProtoEnt* result) +{ + PRStatus rv = PR_SUCCESS; +#if defined(_PR_HAVE_GETPROTO_R) + struct protoent* res = (struct protoent*)result; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#if defined(_PR_HAVE_GETPROTO_R_INT) + { + /* + ** The protoent_data has a pointer as the first field. + ** That implies the buffer better be aligned, and char* + ** doesn't promise much. + */ + PRUptrdiff aligned = (PRUptrdiff)buffer; + if (0 != (aligned & (sizeof(struct protoent_data*) - 1))) + { + aligned += sizeof(struct protoent_data*) - 1; + aligned &= ~(sizeof(struct protoent_data*) - 1); + buflen -= (aligned - (PRUptrdiff)buffer); + buffer = (char*)aligned; + } + } +#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */ + + if (PR_MIN_NETDB_BUF_SIZE > buflen) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#if defined(_PR_HAVE_GETPROTO_R_POINTER) + if (NULL == getprotobyname_r(name, res, buffer, buflen)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#elif defined(_PR_HAVE_GETPROTO_R_INT) + /* + ** The buffer needs to be zero'd, and it should be + ** at least the size of a struct protoent_data. + */ + memset(buffer, 0, buflen); + if (-1 == getprotobyname_r(name, res, (struct protoent_data*)buffer)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#elif defined(_PR_HAVE_5_ARG_GETPROTO_R) + /* The 5th argument for getprotobyname_r() cannot be NULL */ + if (-1 == getprotobyname_r(name, res, buffer, buflen, &res)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#else /* do it the hard way */ + { + struct protoent *staticBuf; + PR_Lock(_getproto_lock); + staticBuf = getprotobyname_r(name); + if (NULL == staticBuf) + { + rv = PR_FAILURE; + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + } + else + { + rv = CopyProtoent(staticBuf, buffer, buflen, result); + if (PR_FAILURE == rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + } + PR_Unlock(_getproto_lock); + } +#endif /* all that */ + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_GetProtoByNumber( + PRInt32 number, char* buffer, PRInt32 buflen, PRProtoEnt* result) +{ + PRStatus rv = PR_SUCCESS; +#if defined(_PR_HAVE_GETPROTO_R) + struct protoent* res = (struct protoent*)result; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#if defined(_PR_HAVE_GETPROTO_R_INT) + { + /* + ** The protoent_data has a pointer as the first field. + ** That implies the buffer better be aligned, and char* + ** doesn't promise much. + */ + PRUptrdiff aligned = (PRUptrdiff)buffer; + if (0 != (aligned & (sizeof(struct protoent_data*) - 1))) + { + aligned += sizeof(struct protoent_data*) - 1; + aligned &= ~(sizeof(struct protoent_data*) - 1); + buflen -= (aligned - (PRUptrdiff)buffer); + buffer = (char*)aligned; + } + } +#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */ + + if (PR_MIN_NETDB_BUF_SIZE > buflen) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#if defined(_PR_HAVE_GETPROTO_R_POINTER) + if (NULL == getprotobynumber_r(number, res, buffer, buflen)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } + +#elif defined(_PR_HAVE_GETPROTO_R_INT) + /* + ** The buffer needs to be zero'd for these OS's. + */ + memset(buffer, 0, buflen); + if (-1 == getprotobynumber_r(number, res, (struct protoent_data*)buffer)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#elif defined(_PR_HAVE_5_ARG_GETPROTO_R) + /* The 5th argument for getprotobynumber_r() cannot be NULL */ + if (-1 == getprotobynumber_r(number, res, buffer, buflen, &res)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#else /* do it the hard way */ + { + struct protoent *staticBuf; + PR_Lock(_getproto_lock); + staticBuf = getprotobynumber_r(number); + if (NULL == staticBuf) + { + rv = PR_FAILURE; + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + } + else + { + rv = CopyProtoent(staticBuf, buffer, buflen, result); + if (PR_FAILURE == rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + } + PR_Unlock(_getproto_lock); + } +#endif /* all that crap */ + return rv; + +} + +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); + } + else if (PR_AF_INET6 == addr->raw.family) +#if defined(_PR_INET6) + addrsize = sizeof(struct sockaddr_in6); +#else + addrsize = sizeof(addr->ipv6); +#endif +#if defined(XP_UNIX) || defined(XP_OS2) + else if (AF_UNIX == addr->raw.family) + { +#if defined(LINUX) + if (addr->local.path[0] == 0) + /* abstract socket address is supported on Linux only */ + addrsize = strnlen(addr->local.path + 1, + sizeof(addr->local.path)) + + offsetof(struct sockaddr_un, sun_path) + 1; + else +#endif + addrsize = sizeof(addr->local); + } +#endif + else { + addrsize = 0; + } + + return addrsize; +} /* _PR_NetAddrSize */ + +PR_IMPLEMENT(PRIntn) PR_EnumerateHostEnt( + PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address) +{ + void *addr = hostEnt->h_addr_list[enumIndex++]; + memset(address, 0, sizeof(PRNetAddr)); + if (NULL == addr) { + enumIndex = 0; + } + else + { + address->raw.family = hostEnt->h_addrtype; + if (PR_AF_INET6 == hostEnt->h_addrtype) + { + address->ipv6.port = htons(port); + address->ipv6.flowinfo = 0; + address->ipv6.scope_id = 0; + memcpy(&address->ipv6.ip, addr, hostEnt->h_length); + } + else + { + PR_ASSERT(AF_INET == hostEnt->h_addrtype); + address->inet.port = htons(port); + memcpy(&address->inet.ip, addr, hostEnt->h_length); + } + } + return enumIndex; +} /* PR_EnumerateHostEnt */ + +PR_IMPLEMENT(PRStatus) PR_InitializeNetAddr( + PRNetAddrValue val, PRUint16 port, PRNetAddr *addr) +{ + PRStatus rv = PR_SUCCESS; + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (val != PR_IpAddrNull) { + memset(addr, 0, sizeof(*addr)); + } + addr->inet.family = AF_INET; + addr->inet.port = htons(port); + switch (val) + { + case PR_IpAddrNull: + break; /* don't overwrite the address */ + case PR_IpAddrAny: + addr->inet.ip = htonl(INADDR_ANY); + break; + case PR_IpAddrLoopback: + addr->inet.ip = htonl(INADDR_LOOPBACK); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + } + return rv; +} /* PR_InitializeNetAddr */ + +PR_IMPLEMENT(PRStatus) PR_SetNetAddr( + PRNetAddrValue val, PRUint16 af, PRUint16 port, PRNetAddr *addr) +{ + PRStatus rv = PR_SUCCESS; + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (af == PR_AF_INET6) + { + if (val != PR_IpAddrNull) { + memset(addr, 0, sizeof(addr->ipv6)); + } + addr->ipv6.family = af; + addr->ipv6.port = htons(port); + addr->ipv6.flowinfo = 0; + addr->ipv6.scope_id = 0; + switch (val) + { + case PR_IpAddrNull: + break; /* don't overwrite the address */ + case PR_IpAddrAny: + addr->ipv6.ip = _pr_in6addr_any; + break; + case PR_IpAddrLoopback: + addr->ipv6.ip = _pr_in6addr_loopback; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + } + } + else + { + if (val != PR_IpAddrNull) { + memset(addr, 0, sizeof(addr->inet)); + } + addr->inet.family = af; + 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 (addr->raw.family == PR_AF_INET6) { + if (val == PR_IpAddrAny) { + if (_PR_IN6_IS_ADDR_UNSPECIFIED((PRIPv6Addr *)&addr->ipv6.ip)) { + return PR_TRUE; + } + if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip) + && _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)&addr->ipv6.ip) + == htonl(INADDR_ANY)) { + return PR_TRUE; + } + } else if (val == PR_IpAddrLoopback) { + if (_PR_IN6_IS_ADDR_LOOPBACK((PRIPv6Addr *)&addr->ipv6.ip)) { + return PR_TRUE; + } + if (_PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip) + && _PR_IN6_V4MAPPED_TO_IPADDR((PRIPv6Addr *)&addr->ipv6.ip) + == htonl(INADDR_LOOPBACK)) { + return PR_TRUE; + } + } else if (val == PR_IpAddrV4Mapped + && _PR_IN6_IS_ADDR_V4MAPPED((PRIPv6Addr *)&addr->ipv6.ip)) { + return PR_TRUE; + } + } else { + if (addr->raw.family == AF_INET) { + if (val == PR_IpAddrAny && addr->inet.ip == htonl(INADDR_ANY)) { + return PR_TRUE; + } + if (val == PR_IpAddrLoopback + && addr->inet.ip == htonl(INADDR_LOOPBACK)) { + return PR_TRUE; + } + } + } + return PR_FALSE; +} + +extern int pr_inet_aton(const char *cp, PRUint32 *addr); + +#define XX 127 +static const unsigned char index_hex[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX, + XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +/* + * StringToV6Addr() returns 1 if the conversion succeeds, + * or 0 if the input is not a valid IPv6 address string. + * (Same as inet_pton(AF_INET6, string, addr).) + */ +static int StringToV6Addr(const char *string, PRIPv6Addr *addr) +{ + const unsigned char *s = (const unsigned char *)string; + int section = 0; /* index of the current section (a 16-bit + * piece of the address */ + int double_colon = -1; /* index of the section after the first + * 16-bit group of zeros represented by + * the double colon */ + unsigned int val; + int len; + + /* Handle initial (double) colon */ + if (*s == ':') { + if (s[1] != ':') { + return 0; + } + s += 2; + addr->pr_s6_addr16[0] = 0; + section = double_colon = 1; + } + + while (*s) { + if (section == 8) { + return 0; /* too long */ + } + if (*s == ':') { + if (double_colon != -1) { + return 0; /* two double colons */ + } + addr->pr_s6_addr16[section++] = 0; + double_colon = section; + s++; + continue; + } + for (len = val = 0; len < 4 && index_hex[*s] != XX; len++) { + val = (val << 4) + index_hex[*s++]; + } + if (*s == '.') { + if (len == 0) { + return 0; /* nothing between : and . */ + } + break; + } + if (*s == ':') { + s++; + if (!*s) { + return 0; /* cannot end with single colon */ + } + } else if (*s) { + return 0; /* bad character */ + } + addr->pr_s6_addr16[section++] = htons((unsigned short)val); + } + + if (*s == '.') { + /* Have a trailing v4 format address */ + if (section > 6) { + return 0; /* not enough room */ + } + + /* + * The number before the '.' is decimal, but we parsed it + * as hex. That means it is in BCD. Check it for validity + * and convert it to binary. + */ + if (val > 0x0255 || (val & 0xf0) > 0x90 || (val & 0xf) > 9) { + return 0; + } + val = (val >> 8) * 100 + ((val >> 4) & 0xf) * 10 + (val & 0xf); + addr->pr_s6_addr[2 * section] = val; + + s++; + val = index_hex[*s++]; + if (val > 9) { + return 0; + } + while (*s >= '0' && *s <= '9') { + val = val * 10 + *s++ - '0'; + if (val > 255) { + return 0; + } + } + if (*s != '.') { + return 0; /* must have exactly 4 decimal numbers */ + } + addr->pr_s6_addr[2 * section + 1] = val; + section++; + + s++; + val = index_hex[*s++]; + if (val > 9) { + return 0; + } + while (*s >= '0' && *s <= '9') { + val = val * 10 + *s++ - '0'; + if (val > 255) { + return 0; + } + } + if (*s != '.') { + return 0; /* must have exactly 4 decimal numbers */ + } + addr->pr_s6_addr[2 * section] = val; + + s++; + val = index_hex[*s++]; + if (val > 9) { + return 0; + } + while (*s >= '0' && *s <= '9') { + val = val * 10 + *s++ - '0'; + if (val > 255) { + return 0; + } + } + if (*s) { + return 0; /* must have exactly 4 decimal numbers */ + } + addr->pr_s6_addr[2 * section + 1] = val; + section++; + } + + if (double_colon != -1) { + /* Stretch the double colon */ + int tosection; + int ncopy = section - double_colon; + for (tosection = 7; ncopy--; tosection--) { + addr->pr_s6_addr16[tosection] = + addr->pr_s6_addr16[double_colon + ncopy]; + } + while (tosection >= double_colon) { + addr->pr_s6_addr16[tosection--] = 0; + } + } else if (section != 8) { + return 0; /* too short */ + } + return 1; +} +#undef XX + +#ifndef _PR_HAVE_INET_NTOP +static const char *basis_hex = "0123456789abcdef"; + +/* + * V6AddrToString() returns a pointer to the buffer containing + * the text string if the conversion succeeds, and NULL otherwise. + * (Same as inet_ntop(AF_INET6, addr, buf, size), except that errno + * is not set on failure.) + */ +static const char *V6AddrToString( + const PRIPv6Addr *addr, char *buf, PRUint32 size) +{ +#define STUFF(c) do { \ + if (!size--) return NULL; \ + *buf++ = (c); \ +} while (0) + + int double_colon = -1; /* index of the first 16-bit + * group of zeros represented + * by the double colon */ + int double_colon_length = 1; /* use double colon only if + * there are two or more 16-bit + * groups of zeros */ + int zero_length; + int section; + unsigned int val; + const char *bufcopy = buf; + + /* Scan to find the placement of the double colon */ + for (section = 0; section < 8; section++) { + if (addr->pr_s6_addr16[section] == 0) { + zero_length = 1; + section++; + while (section < 8 && addr->pr_s6_addr16[section] == 0) { + zero_length++; + section++; + } + /* Select the longest sequence of zeros */ + if (zero_length > double_colon_length) { + double_colon = section - zero_length; + double_colon_length = zero_length; + } + } + } + + /* Now start converting to a string */ + section = 0; + + if (double_colon == 0) { + if (double_colon_length == 6 || + (double_colon_length == 5 && addr->pr_s6_addr16[5] == 0xffff)) { + /* ipv4 format address */ + STUFF(':'); + STUFF(':'); + if (double_colon_length == 5) { + STUFF('f'); + STUFF('f'); + STUFF('f'); + STUFF('f'); + STUFF(':'); + } + if (addr->pr_s6_addr[12] > 99) { + STUFF(addr->pr_s6_addr[12]/100 + '0'); + } + if (addr->pr_s6_addr[12] > 9) { + STUFF((addr->pr_s6_addr[12]%100)/10 + '0'); + } + STUFF(addr->pr_s6_addr[12]%10 + '0'); + STUFF('.'); + if (addr->pr_s6_addr[13] > 99) { + STUFF(addr->pr_s6_addr[13]/100 + '0'); + } + if (addr->pr_s6_addr[13] > 9) { + STUFF((addr->pr_s6_addr[13]%100)/10 + '0'); + } + STUFF(addr->pr_s6_addr[13]%10 + '0'); + STUFF('.'); + if (addr->pr_s6_addr[14] > 99) { + STUFF(addr->pr_s6_addr[14]/100 + '0'); + } + if (addr->pr_s6_addr[14] > 9) { + STUFF((addr->pr_s6_addr[14]%100)/10 + '0'); + } + STUFF(addr->pr_s6_addr[14]%10 + '0'); + STUFF('.'); + if (addr->pr_s6_addr[15] > 99) { + STUFF(addr->pr_s6_addr[15]/100 + '0'); + } + if (addr->pr_s6_addr[15] > 9) { + STUFF((addr->pr_s6_addr[15]%100)/10 + '0'); + } + STUFF(addr->pr_s6_addr[15]%10 + '0'); + STUFF('\0'); + return bufcopy; + } + } + + while (section < 8) { + if (section == double_colon) { + STUFF(':'); + STUFF(':'); + section += double_colon_length; + continue; + } + val = ntohs(addr->pr_s6_addr16[section]); + if (val > 0xfff) { + STUFF(basis_hex[val >> 12]); + } + if (val > 0xff) { + STUFF(basis_hex[(val >> 8) & 0xf]); + } + if (val > 0xf) { + STUFF(basis_hex[(val >> 4) & 0xf]); + } + STUFF(basis_hex[val & 0xf]); + section++; + if (section < 8 && section != double_colon) { + STUFF(':'); + } + } + STUFF('\0'); + return bufcopy; +#undef STUFF +} +#endif /* !_PR_HAVE_INET_NTOP */ + +/* + * Convert an IPv4 addr to an (IPv4-mapped) IPv6 addr + */ +PR_IMPLEMENT(void) PR_ConvertIPv4AddrToIPv6(PRUint32 v4addr, PRIPv6Addr *v6addr) +{ + PRUint8 *dstp; + dstp = v6addr->pr_s6_addr; + memset(dstp, 0, 10); + memset(dstp + 10, 0xff, 2); + memcpy(dstp + 12,(char *) &v4addr, 4); +} + +PR_IMPLEMENT(PRUint16) PR_ntohs(PRUint16 n) { + return ntohs(n); +} +PR_IMPLEMENT(PRUint32) PR_ntohl(PRUint32 n) { + return ntohl(n); +} +PR_IMPLEMENT(PRUint16) PR_htons(PRUint16 n) { + return htons(n); +} +PR_IMPLEMENT(PRUint32) PR_htonl(PRUint32 n) { + return htonl(n); +} +PR_IMPLEMENT(PRUint64) PR_ntohll(PRUint64 n) +{ +#ifdef IS_BIG_ENDIAN + return n; +#else + PRUint32 hi, lo; + lo = (PRUint32)n; + hi = (PRUint32)(n >> 32); + hi = PR_ntohl(hi); + lo = PR_ntohl(lo); + return ((PRUint64)lo << 32) + (PRUint64)hi; +#endif +} /* ntohll */ + +PR_IMPLEMENT(PRUint64) PR_htonll(PRUint64 n) +{ +#ifdef IS_BIG_ENDIAN + return n; +#else + PRUint32 hi, lo; + lo = (PRUint32)n; + hi = (PRUint32)(n >> 32); + hi = htonl(hi); + lo = htonl(lo); + return ((PRUint64)lo << 32) + (PRUint64)hi; +#endif +} /* htonll */ + + +/* + * Implementation of PR_GetAddrInfoByName and friends + * + * Compile-time options: + * + * _PR_HAVE_GETADDRINFO Define this macro if the target system provides + * getaddrinfo. With this defined, NSPR will require + * getaddrinfo at run time. If this if not defined, + * then NSPR will attempt to dynamically resolve + * getaddrinfo, falling back to PR_GetHostByName if + * getaddrinfo does not exist on the target system. + * + * Since getaddrinfo is a relatively new system call on many systems, + * we are forced to dynamically resolve it at run time in most cases. + * The exception includes any system (such as Mac OS X) that is known to + * provide getaddrinfo in all versions that NSPR cares to support. + */ + +#if defined(_PR_HAVE_GETADDRINFO) + +#if defined(_PR_INET6) + +typedef struct addrinfo PRADDRINFO; +#define GETADDRINFO getaddrinfo +#define FREEADDRINFO freeaddrinfo +#define GETNAMEINFO getnameinfo + +#elif defined(_PR_INET6_PROBE) + +typedef struct addrinfo PRADDRINFO; + +/* getaddrinfo/freeaddrinfo/getnameinfo prototypes */ +#if defined(WIN32) +#define FUNC_MODIFIER __stdcall +#else +#define FUNC_MODIFIER +#endif +typedef int (FUNC_MODIFIER * FN_GETADDRINFO) +(const char *nodename, + const char *servname, + const PRADDRINFO *hints, + PRADDRINFO **res); +typedef int (FUNC_MODIFIER * FN_FREEADDRINFO) +(PRADDRINFO *ai); +typedef int (FUNC_MODIFIER * FN_GETNAMEINFO) +(const struct sockaddr *addr, int addrlen, + char *host, int hostlen, + char *serv, int servlen, int flags); + +/* global state */ +static FN_GETADDRINFO _pr_getaddrinfo = NULL; +static FN_FREEADDRINFO _pr_freeaddrinfo = NULL; +static FN_GETNAMEINFO _pr_getnameinfo = NULL; + +#define GETADDRINFO_SYMBOL "getaddrinfo" +#define FREEADDRINFO_SYMBOL "freeaddrinfo" +#define GETNAMEINFO_SYMBOL "getnameinfo" + +PRStatus +_pr_find_getaddrinfo(void) +{ + PRLibrary *lib; +#ifdef WIN32 + /* + * On windows, we need to search ws2_32.dll or wship6.dll + * (Microsoft IPv6 Technology Preview for Windows 2000) for + * getaddrinfo and freeaddrinfo. These libraries might not + * be loaded yet. + */ + const char *libname[] = { "ws2_32.dll", "wship6.dll" }; + int i; + + for (i = 0; i < sizeof(libname)/sizeof(libname[0]); i++) { + lib = PR_LoadLibrary(libname[i]); + if (!lib) { + continue; + } + _pr_getaddrinfo = (FN_GETADDRINFO) + PR_FindFunctionSymbol(lib, GETADDRINFO_SYMBOL); + if (!_pr_getaddrinfo) { + PR_UnloadLibrary(lib); + continue; + } + _pr_freeaddrinfo = (FN_FREEADDRINFO) + PR_FindFunctionSymbol(lib, FREEADDRINFO_SYMBOL); + _pr_getnameinfo = (FN_GETNAMEINFO) + PR_FindFunctionSymbol(lib, GETNAMEINFO_SYMBOL); + if (!_pr_freeaddrinfo || !_pr_getnameinfo) { + PR_UnloadLibrary(lib); + continue; + } + /* Keep the library loaded. */ + return PR_SUCCESS; + } + return PR_FAILURE; +#else + /* + * Resolve getaddrinfo by searching all loaded libraries. Then + * search library containing getaddrinfo for freeaddrinfo. + */ + _pr_getaddrinfo = (FN_GETADDRINFO) + PR_FindFunctionSymbolAndLibrary(GETADDRINFO_SYMBOL, &lib); + if (!_pr_getaddrinfo) { + return PR_FAILURE; + } + _pr_freeaddrinfo = (FN_FREEADDRINFO) + PR_FindFunctionSymbol(lib, FREEADDRINFO_SYMBOL); + _pr_getnameinfo = (FN_GETNAMEINFO) + PR_FindFunctionSymbol(lib, GETNAMEINFO_SYMBOL); + PR_UnloadLibrary(lib); + if (!_pr_freeaddrinfo || !_pr_getnameinfo) { + return PR_FAILURE; + } + return PR_SUCCESS; +#endif +} + +#define GETADDRINFO (*_pr_getaddrinfo) +#define FREEADDRINFO (*_pr_freeaddrinfo) +#define GETNAMEINFO (*_pr_getnameinfo) + +#endif /* _PR_INET6 */ + +#endif /* _PR_HAVE_GETADDRINFO */ + +#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE) +/* + * If getaddrinfo does not exist, then we will fall back on + * PR_GetHostByName, which requires that we allocate a buffer for the + * PRHostEnt data structure and its members. + */ +typedef struct PRAddrInfoFB { + char buf[PR_NETDB_BUF_SIZE]; + PRHostEnt hostent; + PRBool has_cname; +} PRAddrInfoFB; + +static PRAddrInfo * +pr_GetAddrInfoByNameFB(const char *hostname, + PRUint16 af, + PRIntn flags) +{ + PRStatus rv; + PRAddrInfoFB *ai; + /* fallback on PR_GetHostByName */ + ai = PR_NEW(PRAddrInfoFB); + if (!ai) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + rv = PR_GetHostByName(hostname, ai->buf, sizeof ai->buf, &ai->hostent); + if (rv == PR_FAILURE) { + PR_Free(ai); + return NULL; + } + ai->has_cname = !(flags & PR_AI_NOCANONNAME); + + return (PRAddrInfo *) ai; +} +#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE */ + +PR_IMPLEMENT(PRAddrInfo *) PR_GetAddrInfoByName(const char *hostname, + PRUint16 af, + PRIntn flags) +{ + /* restrict input to supported values */ + if ((af != PR_AF_INET && af != PR_AF_UNSPEC) || + (flags & ~ PR_AI_NOCANONNAME) != PR_AI_ADDRCONFIG) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#if !defined(_PR_HAVE_GETADDRINFO) + return pr_GetAddrInfoByNameFB(hostname, af, flags); +#else +#if defined(_PR_INET6_PROBE) + if (!_pr_ipv6_is_present()) { + return pr_GetAddrInfoByNameFB(hostname, af, flags); + } +#endif + { + PRADDRINFO *res, hints; + int rv; + + /* + * we assume a RFC 2553 compliant getaddrinfo. this may at some + * point need to be customized as platforms begin to adopt the + * RFC 3493. + */ + + memset(&hints, 0, sizeof(hints)); + if (!(flags & PR_AI_NOCANONNAME)) { + hints.ai_flags |= AI_CANONNAME; + } +#ifdef AI_ADDRCONFIG + /* + * Propagate AI_ADDRCONFIG to the GETADDRINFO call if PR_AI_ADDRCONFIG + * is set. + * + * Need a workaround for loopback host addresses: + * The problem is that in glibc and Windows, AI_ADDRCONFIG applies the + * existence of an outgoing network interface to IP addresses of the + * loopback interface, due to a strict interpretation of the + * specification. For example, if a computer does not have any + * outgoing IPv6 network interface, but its loopback network interface + * supports IPv6, a getaddrinfo call on "localhost" with AI_ADDRCONFIG + * won't return the IPv6 loopback address "::1", because getaddrinfo + * thinks the computer cannot connect to any IPv6 destination, + * ignoring the remote vs. local/loopback distinction. + */ + if ((flags & PR_AI_ADDRCONFIG) && + strcmp(hostname, "localhost") != 0 && + strcmp(hostname, "localhost.localdomain") != 0 && + strcmp(hostname, "localhost6") != 0 && + strcmp(hostname, "localhost6.localdomain6") != 0) { + hints.ai_flags |= AI_ADDRCONFIG; + } +#endif + hints.ai_family = (af == PR_AF_INET) ? AF_INET : AF_UNSPEC; + + /* + * it is important to select a socket type in the hints, otherwise we + * will get back repetitive entries: one for each socket type. since + * we do not expose ai_socktype through our API, it is okay to do this + * here. the application may still choose to create a socket of some + * other type. + */ + hints.ai_socktype = SOCK_STREAM; + + rv = GETADDRINFO(hostname, NULL, &hints, &res); +#ifdef AI_ADDRCONFIG + if (rv == EAI_BADFLAGS && (hints.ai_flags & AI_ADDRCONFIG)) { + hints.ai_flags &= ~AI_ADDRCONFIG; + rv = GETADDRINFO(hostname, NULL, &hints, &res); + } +#endif + if (rv == 0) { + return (PRAddrInfo *) res; + } + + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, rv); + } + return NULL; +#endif +} + +PR_IMPLEMENT(PRStatus) +PR_GetPrefLoopbackAddrInfo(PRNetAddr *result, + PRUint16 port) +{ + char tmpBuf[ 40 ]; + const int tmpBufSize = sizeof( tmpBuf ); + + if (!result) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + PR_snprintf(tmpBuf, tmpBufSize, "%u", port ); + +#if !defined(_PR_HAVE_GETADDRINFO) || !defined(AI_PASSIVE) + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#else + + PRADDRINFO *res, hints; + PRStatus rv; + + memset(&hints, 0, sizeof(hints)); + + rv = GETADDRINFO(NULL, tmpBuf, &hints, &res); + if (rv == 0) { + PRBool result_still_empty = PR_TRUE; + PRADDRINFO *ai = res; + do { + PRNetAddr aNetAddr; + + while (ai && ai->ai_addrlen > sizeof(PRNetAddr)) + ai = ai->ai_next; + + if (ai) { + /* copy sockaddr to PRNetAddr */ + memcpy(&aNetAddr, ai->ai_addr, ai->ai_addrlen); + aNetAddr.raw.family = ai->ai_addr->sa_family; +#ifdef _PR_INET6 + if (AF_INET6 == aNetAddr.raw.family) + aNetAddr.raw.family = PR_AF_INET6; +#endif + if (ai->ai_addrlen < sizeof(PRNetAddr)) + memset(((char*)result)+ai->ai_addrlen, 0, + sizeof(PRNetAddr) - ai->ai_addrlen); + } + + /* If we obtain more than one result, prefer IPv6. */ + if (result_still_empty || aNetAddr.raw.family == PR_AF_INET6) { + memcpy(result, &aNetAddr, sizeof(PRNetAddr)); + } + result_still_empty = PR_FALSE; + ai = ai->ai_next; + } + while (ai); + + FREEADDRINFO(res); + return PR_SUCCESS; + } + + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, rv); + return PR_FAILURE; +#endif +} + +PR_IMPLEMENT(void) PR_FreeAddrInfo(PRAddrInfo *ai) +{ +#if defined(_PR_HAVE_GETADDRINFO) +#if defined(_PR_INET6_PROBE) + if (!_pr_ipv6_is_present()) { + PR_Free((PRAddrInfoFB *) ai); + } + else +#endif + FREEADDRINFO((PRADDRINFO *) ai); +#else + PR_Free((PRAddrInfoFB *) ai); +#endif +} + +PR_IMPLEMENT(void *) PR_EnumerateAddrInfo(void *iterPtr, + const PRAddrInfo *base, + PRUint16 port, + PRNetAddr *result) +{ +#if defined(_PR_HAVE_GETADDRINFO) + PRADDRINFO *ai; +#if defined(_PR_INET6_PROBE) + if (!_pr_ipv6_is_present()) { + /* using PRAddrInfoFB */ + PRIntn iter = (PRIntn)(PRPtrdiff) iterPtr; + iter = PR_EnumerateHostEnt(iter, &((PRAddrInfoFB *) base)->hostent, port, result); + if (iter < 0) { + iter = 0; + } + return (void *)(PRPtrdiff) iter; + } +#endif + + if (iterPtr) { + ai = ((PRADDRINFO *) iterPtr)->ai_next; + } + else { + ai = (PRADDRINFO *) base; + } + + while (ai && ai->ai_addrlen > sizeof(PRNetAddr)) { + ai = ai->ai_next; + } + + if (ai) { + /* copy sockaddr to PRNetAddr */ + memcpy(result, ai->ai_addr, ai->ai_addrlen); + result->raw.family = ai->ai_addr->sa_family; +#ifdef _PR_INET6 + if (AF_INET6 == result->raw.family) { + result->raw.family = PR_AF_INET6; + } +#endif + if (ai->ai_addrlen < sizeof(PRNetAddr)) { + memset(((char*)result)+ai->ai_addrlen, 0, sizeof(PRNetAddr) - ai->ai_addrlen); + } + + if (result->raw.family == PR_AF_INET) { + result->inet.port = htons(port); + } + else { + result->ipv6.port = htons(port); + } + } + + return ai; +#else + /* using PRAddrInfoFB */ + PRIntn iter = (PRIntn) iterPtr; + iter = PR_EnumerateHostEnt(iter, &((PRAddrInfoFB *) base)->hostent, port, result); + if (iter < 0) { + iter = 0; + } + return (void *) iter; +#endif +} + +PR_IMPLEMENT(const char *) PR_GetCanonNameFromAddrInfo(const PRAddrInfo *ai) +{ +#if defined(_PR_HAVE_GETADDRINFO) +#if defined(_PR_INET6_PROBE) + if (!_pr_ipv6_is_present()) { + const PRAddrInfoFB *fb = (const PRAddrInfoFB *) ai; + return fb->has_cname ? fb->hostent.h_name : NULL; + } +#endif + return ((const PRADDRINFO *) ai)->ai_canonname; +#else + const PRAddrInfoFB *fb = (const PRAddrInfoFB *) ai; + return fb->has_cname ? fb->hostent.h_name : NULL; +#endif +} + +#if defined(_PR_HAVE_GETADDRINFO) +static PRStatus pr_StringToNetAddrGAI(const char *string, PRNetAddr *addr) +{ + PRADDRINFO *res, hints; + int rv; /* 0 for success, or the error code EAI_xxx */ + PRNetAddr laddr; + PRStatus status = PR_SUCCESS; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + rv = GETADDRINFO(string, NULL, &hints, &res); + if (rv != 0) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv); + return PR_FAILURE; + } + + /* pick up the first addr */ + memcpy(&laddr, res->ai_addr, res->ai_addrlen); + if (AF_INET6 == res->ai_addr->sa_family) + { + addr->ipv6.family = PR_AF_INET6; + addr->ipv6.ip = laddr.ipv6.ip; + addr->ipv6.scope_id = laddr.ipv6.scope_id; + } + else if (AF_INET == res->ai_addr->sa_family) + { + addr->inet.family = PR_AF_INET; + addr->inet.ip = laddr.inet.ip; + } + else + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + status = PR_FAILURE; + } + + FREEADDRINFO(res); + return status; +} +#endif /* _PR_HAVE_GETADDRINFO */ + +static PRStatus pr_StringToNetAddrFB(const char *string, PRNetAddr *addr) +{ + PRIntn rv; + + rv = pr_inet_aton(string, &addr->inet.ip); + if (1 == rv) + { + addr->raw.family = AF_INET; + return PR_SUCCESS; + } + + PR_ASSERT(0 == rv); + /* clean up after the failed call */ + memset(&addr->inet.ip, 0, sizeof(addr->inet.ip)); + + rv = StringToV6Addr(string, &addr->ipv6.ip); + if (1 == rv) + { + addr->raw.family = PR_AF_INET6; + return PR_SUCCESS; + } + + PR_ASSERT(0 == rv); + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) PR_StringToNetAddr(const char *string, PRNetAddr *addr) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (!addr || !string || !*string) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#if !defined(_PR_HAVE_GETADDRINFO) + return pr_StringToNetAddrFB(string, addr); +#else + /* + * getaddrinfo with AI_NUMERICHOST is much slower than pr_inet_aton on some + * platforms, such as Mac OS X (bug 404399), Linux glibc 2.10 (bug 344809), + * and most likely others. So we only use it to convert literal IP addresses + * that contain IPv6 scope IDs, which pr_inet_aton cannot convert. + */ + if (!strchr(string, '%')) { + return pr_StringToNetAddrFB(string, addr); + } + +#if defined(_PR_INET6_PROBE) + if (!_pr_ipv6_is_present()) { + return pr_StringToNetAddrFB(string, addr); + } +#endif + + return pr_StringToNetAddrGAI(string, addr); +#endif +} + +#if defined(_PR_HAVE_GETADDRINFO) +static PRStatus pr_NetAddrToStringGNI( + const PRNetAddr *addr, char *string, PRUint32 size) +{ + int addrlen; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRUint16 md_af = addr->raw.family; + PRNetAddr addrcopy; +#endif + int rv; /* 0 for success, or the error code EAI_xxx */ + +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) + { + md_af = AF_INET6; +#ifndef _PR_HAVE_SOCKADDR_LEN + addrcopy = *addr; + addrcopy.raw.family = md_af; + addrp = &addrcopy; +#endif + } +#endif + + addrlen = PR_NETADDR_SIZE(addr); +#ifdef _PR_HAVE_SOCKADDR_LEN + addrcopy = *addr; + ((struct sockaddr*)&addrcopy)->sa_len = addrlen; + ((struct sockaddr*)&addrcopy)->sa_family = md_af; + addrp = &addrcopy; +#endif + rv = GETNAMEINFO((const struct sockaddr *)addrp, addrlen, + string, size, NULL, 0, NI_NUMERICHOST); + if (rv != 0) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, rv); + return PR_FAILURE; + } + return PR_SUCCESS; +} +#endif /* _PR_HAVE_GETADDRINFO */ + +#if !defined(_PR_HAVE_GETADDRINFO) || defined(_PR_INET6_PROBE) +static PRStatus pr_NetAddrToStringFB( + const PRNetAddr *addr, char *string, PRUint32 size) +{ + if (PR_AF_INET6 == addr->raw.family) + { +#if defined(_PR_HAVE_INET_NTOP) + if (NULL == inet_ntop(AF_INET6, &addr->ipv6.ip, string, size)) +#else + if (NULL == V6AddrToString(&addr->ipv6.ip, string, size)) +#endif + { + /* the size of the result buffer is inadequate */ + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return PR_FAILURE; + } + } + else + { + if (size < 16) { + goto failed; + } + if (AF_INET != addr->raw.family) { + goto failed; + } + else + { + unsigned char *byte = (unsigned char*)&addr->inet.ip; + PR_snprintf(string, size, "%u.%u.%u.%u", + byte[0], byte[1], byte[2], byte[3]); + } + } + + return PR_SUCCESS; + +failed: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + +} /* pr_NetAddrToStringFB */ +#endif /* !_PR_HAVE_GETADDRINFO || _PR_INET6_PROBE */ + +PR_IMPLEMENT(PRStatus) PR_NetAddrToString( + const PRNetAddr *addr, char *string, PRUint32 size) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#if !defined(_PR_HAVE_GETADDRINFO) + return pr_NetAddrToStringFB(addr, string, size); +#else +#if defined(_PR_INET6_PROBE) + if (!_pr_ipv6_is_present()) { + return pr_NetAddrToStringFB(addr, string, size); + } +#endif + return pr_NetAddrToStringGNI(addr, string, size); +#endif +} /* PR_NetAddrToString */ diff --git a/nsprpub/pr/src/misc/prolock.c b/nsprpub/pr/src/misc/prolock.c new file mode 100644 index 0000000000..77a26bb2f7 --- /dev/null +++ b/nsprpub/pr/src/misc/prolock.c @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** prolock.c -- NSPR Ordered Lock +** +** Implement the API defined in prolock.h +** +*/ +#include "prolock.h" +#include "prlog.h" +#include "prerror.h" + +PR_IMPLEMENT(PROrderedLock *) +PR_CreateOrderedLock( + PRInt32 order, + const char *name +) +{ + PR_NOT_REACHED("Not implemented"); /* Not implemented yet */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} /* end PR_CreateOrderedLock() */ + + +PR_IMPLEMENT(void) +PR_DestroyOrderedLock( + PROrderedLock *lock +) +{ + PR_NOT_REACHED("Not implemented"); /* Not implemented yet */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); +} /* end PR_DestroyOrderedLock() */ + + +PR_IMPLEMENT(void) +PR_LockOrderedLock( + PROrderedLock *lock +) +{ + PR_NOT_REACHED("Not implemented"); /* Not implemented yet */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); +} /* end PR_LockOrderedLock() */ + + +PR_IMPLEMENT(PRStatus) +PR_UnlockOrderedLock( + PROrderedLock *lock +) +{ + PR_NOT_REACHED("Not implemented"); /* Not implemented yet */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} /* end PR_UnlockOrderedLock() */ diff --git a/nsprpub/pr/src/misc/prrng.c b/nsprpub/pr/src/misc/prrng.c new file mode 100644 index 0000000000..b3e3d4878b --- /dev/null +++ b/nsprpub/pr/src/misc/prrng.c @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/* + * We were not including <string.h> in optimized builds. On AIX this + * caused libnspr4.so to export memcpy and some binaries linked with + * libnspr4.so resolved their memcpy references with libnspr4.so. To + * be backward compatible with old libnspr4.so binaries, we do not + * include <string.h> in optimized builds for AIX. (bug 200561) + */ +#if !(defined(AIX) && !defined(DEBUG)) +#include <string.h> +#endif + +PRSize _pr_CopyLowBits( + void *dst, + PRSize dstlen, + void *src, + PRSize srclen ) +{ + if (srclen <= dstlen) { + memcpy(dst, src, srclen); + return srclen; + } +#if defined IS_BIG_ENDIAN + memcpy(dst, (char*)src + (srclen - dstlen), dstlen); +#else + memcpy(dst, src, dstlen); +#endif + return dstlen; +} + +PR_IMPLEMENT(PRSize) PR_GetRandomNoise( + void *buf, + PRSize size +) +{ + return( _PR_MD_GET_RANDOM_NOISE( buf, size )); +} /* end PR_GetRandomNoise() */ +/* end prrng.c */ diff --git a/nsprpub/pr/src/misc/prsystem.c b/nsprpub/pr/src/misc/prsystem.c new file mode 100644 index 0000000000..dba093e9b1 --- /dev/null +++ b/nsprpub/pr/src/misc/prsystem.c @@ -0,0 +1,373 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include "prsystem.h" +#include "prprf.h" +#include "prlong.h" + +#if defined(OS2) +#define INCL_DOS +#define INCL_DOSMISC +#include <os2.h> +/* define the required constant if it is not already defined in the headers */ +#ifndef QSV_NUMPROCESSORS +#define QSV_NUMPROCESSORS 26 +#endif +#endif + +/* BSD-derived systems use sysctl() to get the number of processors */ +#if defined(BSDI) || defined(FREEBSD) || defined(NETBSD) \ + || defined(OPENBSD) || defined(DRAGONFLY) || defined(DARWIN) +#define _PR_HAVE_SYSCTL +#include <sys/param.h> +#include <sys/sysctl.h> +#endif + +#if defined(DARWIN) +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <mach/mach_port.h> +#endif + +#if defined(HPUX) +#include <sys/mpctl.h> +#include <sys/pstat.h> +#endif + +#if defined(XP_UNIX) +#include <unistd.h> +#include <sys/utsname.h> +#endif + +#if defined(LINUX) +#include <string.h> +#include <ctype.h> +#define MAX_LINE 512 +#endif + +#if defined(AIX) +#include <cf.h> +#include <sys/cfgodm.h> +#endif + +PR_IMPLEMENT(char) PR_GetDirectorySeparator(void) +{ + return PR_DIRECTORY_SEPARATOR; +} /* PR_GetDirectorySeparator */ + +/* +** OBSOLETE -- the function name is misspelled. +*/ +PR_IMPLEMENT(char) PR_GetDirectorySepartor(void) +{ +#if defined(DEBUG) + static PRBool warn = PR_TRUE; + if (warn) { + warn = _PR_Obsolete("PR_GetDirectorySepartor()", + "PR_GetDirectorySeparator()"); + } +#endif + return PR_GetDirectorySeparator(); +} /* PR_GetDirectorySepartor */ + +PR_IMPLEMENT(char) PR_GetPathSeparator(void) +{ + return PR_PATH_SEPARATOR; +} /* PR_GetPathSeparator */ + +PR_IMPLEMENT(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen) +{ + PRUintn len = 0; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + switch(cmd) + { + case PR_SI_HOSTNAME: + case PR_SI_HOSTNAME_UNTRUNCATED: + if (PR_FAILURE == _PR_MD_GETHOSTNAME(buf, (PRUintn)buflen)) { + return PR_FAILURE; + } + + if (cmd == PR_SI_HOSTNAME_UNTRUNCATED) { + break; + } + /* + * On some platforms a system does not have a hostname and + * its IP address is returned instead. The following code + * should be skipped on those platforms. + */ +#ifndef _PR_GET_HOST_ADDR_AS_NAME + /* Return the unqualified hostname */ + while (buf[len] && (len < buflen)) { + if (buf[len] == '.') { + buf[len] = '\0'; + break; + } + len += 1; + } +#endif + break; + + case PR_SI_SYSNAME: + /* Return the operating system name */ +#if defined(XP_UNIX) || defined(WIN32) + if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) { + return PR_FAILURE; + } +#else + (void)PR_snprintf(buf, buflen, _PR_SI_SYSNAME); +#endif + break; + + case PR_SI_RELEASE: + /* Return the version of the operating system */ +#if defined(XP_UNIX) || defined(WIN32) + if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) { + return PR_FAILURE; + } +#endif +#if defined(XP_OS2) + { + ULONG os2ver[2] = {0}; + DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_REVISION, + &os2ver, sizeof(os2ver)); + /* Formatting for normal usage (2.11, 3.0, 4.0, 4.5); officially, + Warp 4 is version 2.40.00, WSeB 2.45.00 */ + if (os2ver[0] < 30) + (void)PR_snprintf(buf, buflen, "%s%lu", + "2.", os2ver[0]); + else if (os2ver[0] < 45) + (void)PR_snprintf(buf, buflen, "%lu%s%lu", + os2ver[0]/10, ".", os2ver[1]); + else + (void)PR_snprintf(buf, buflen, "%.1f", + os2ver[0]/10.0); + } +#endif /* OS2 */ + break; + + case PR_SI_RELEASE_BUILD: + /* Return the version of the operating system */ +#if defined(XP_UNIX) || defined(WIN32) + if (PR_FAILURE == _PR_MD_GETSYSINFO(cmd, buf, (PRUintn)buflen)) { + return PR_FAILURE; + } +#else + if (buflen) { + *buf = 0; + } +#endif /* XP_UNIX || WIN32 */ + break; + + case PR_SI_ARCHITECTURE: + /* Return the architecture of the machine (ie. x86, mips, alpha, ...)*/ + (void)PR_snprintf(buf, buflen, _PR_SI_ARCHITECTURE); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +/* +** PR_GetNumberOfProcessors() +** +** Implementation notes: +** Every platform does it a bit different. +** numCpus is the returned value. +** for each platform's "if defined" section +** declare your local variable +** do your thing, assign to numCpus +** order of the if defined()s may be important, +** especially for unix variants. Do platform +** specific implementations before XP_UNIX. +** +*/ +PR_IMPLEMENT(PRInt32) PR_GetNumberOfProcessors( void ) +{ + PRInt32 numCpus; +#if defined(WIN32) + SYSTEM_INFO info; + + GetSystemInfo( &info ); + numCpus = info.dwNumberOfProcessors; +#elif defined(OS2) + DosQuerySysInfo( QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, &numCpus, sizeof(numCpus)); +#elif defined(_PR_HAVE_SYSCTL) + int mib[2]; + int rc; + size_t len = sizeof(numCpus); + + mib[0] = CTL_HW; +#ifdef HW_NCPUONLINE + mib[1] = HW_NCPUONLINE; +#else + mib[1] = HW_NCPU; +#endif + rc = sysctl( mib, 2, &numCpus, &len, NULL, 0 ); + if ( -1 == rc ) { + numCpus = -1; /* set to -1 for return value on error */ + _PR_MD_MAP_DEFAULT_ERROR( _MD_ERRNO() ); + } +#elif defined(HPUX) + numCpus = mpctl( MPC_GETNUMSPUS, 0, 0 ); + if ( numCpus < 1 ) { + numCpus = -1; /* set to -1 for return value on error */ + _PR_MD_MAP_DEFAULT_ERROR( _MD_ERRNO() ); + } +#elif defined(RISCOS) + numCpus = 1; +#elif defined(LINUX) + /* for the benefit of devices with advanced power-saving, that + actually hotplug their cpus in heavy load, try to figure out + the real number of CPUs */ + char buf[MAX_LINE]; + FILE *fin; + const char *cpu_present = "/sys/devices/system/cpu/present"; + size_t strsize; + numCpus = 0; + fin = fopen(cpu_present, "r"); + if (fin != NULL) { + if (fgets(buf, MAX_LINE, fin) != NULL) { + /* check that the format is what we expect */ + if (buf[0] == '0') { + strsize = strlen(buf); + if (strsize == 1) { + /* single core */ + numCpus = 1; + } else if (strsize >= 3 && strsize <= 5) { + /* should be of the form 0-999 */ + /* parse the part after the 0-, note count is 0-based */ + if (buf[1] == '-' && isdigit(buf[2])) { + numCpus = 1 + atoi(buf + 2); + } + } + } + } + fclose(fin); + } + /* if that fails, fall back to more standard methods */ + if (!numCpus) { + numCpus = sysconf( _SC_NPROCESSORS_CONF ); + } +#elif defined(XP_UNIX) + numCpus = sysconf( _SC_NPROCESSORS_CONF ); +#else +#error "An implementation is required" +#endif + return(numCpus); +} /* end PR_GetNumberOfProcessors() */ + +/* +** PR_GetPhysicalMemorySize() +** +** Implementation notes: +** Every platform does it a bit different. +** bytes is the returned value. +** for each platform's "if defined" section +** declare your local variable +** do your thing, assign to bytes. +** +*/ +PR_IMPLEMENT(PRUint64) PR_GetPhysicalMemorySize(void) +{ + PRUint64 bytes = 0; + +#if defined(LINUX) || defined(SOLARIS) + + long pageSize = sysconf(_SC_PAGESIZE); + long pageCount = sysconf(_SC_PHYS_PAGES); + if (pageSize >= 0 && pageCount >= 0) { + bytes = (PRUint64) pageSize * pageCount; + } + +#elif defined(NETBSD) || defined(OPENBSD) \ + || defined(FREEBSD) || defined(DRAGONFLY) + + int mib[2]; + int rc; +#ifdef HW_PHYSMEM64 + uint64_t memSize; +#else + unsigned long memSize; +#endif + size_t len = sizeof(memSize); + + mib[0] = CTL_HW; +#ifdef HW_PHYSMEM64 + mib[1] = HW_PHYSMEM64; +#else + mib[1] = HW_PHYSMEM; +#endif + rc = sysctl(mib, 2, &memSize, &len, NULL, 0); + if (-1 != rc) { + bytes = memSize; + } + +#elif defined(HPUX) + + struct pst_static info; + int result = pstat_getstatic(&info, sizeof(info), 1, 0); + if (result == 1) { + bytes = (PRUint64) info.physical_memory * info.page_size; + } + +#elif defined(DARWIN) + + mach_port_t mach_host = mach_host_self(); + struct host_basic_info hInfo; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + + int result = host_info(mach_host, + HOST_BASIC_INFO, + (host_info_t) &hInfo, + &count); + mach_port_deallocate(mach_task_self(), mach_host); + if (result == KERN_SUCCESS) { + bytes = hInfo.max_mem; + } + +#elif defined(WIN32) + + MEMORYSTATUSEX memStat; + memStat.dwLength = sizeof(memStat); + if (GlobalMemoryStatusEx(&memStat)) { + bytes = memStat.ullTotalPhys; + } + +#elif defined(OS2) + + ULONG ulPhysMem; + DosQuerySysInfo(QSV_TOTPHYSMEM, + QSV_TOTPHYSMEM, + &ulPhysMem, + sizeof(ulPhysMem)); + bytes = ulPhysMem; + +#elif defined(AIX) + + if (odm_initialize() == 0) { + int how_many; + struct CuAt *obj = getattr("sys0", "realmem", 0, &how_many); + if (obj != NULL) { + bytes = (PRUint64) atoi(obj->value) * 1024; + free(obj); + } + odm_terminate(); + } + +#else + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + +#endif + + return bytes; +} /* end PR_GetPhysicalMemorySize() */ diff --git a/nsprpub/pr/src/misc/prthinfo.c b/nsprpub/pr/src/misc/prthinfo.c new file mode 100644 index 0000000000..14602df0be --- /dev/null +++ b/nsprpub/pr/src/misc/prthinfo.c @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prlog.h" +#include "prthread.h" +#include "private/pprthred.h" +#include "primpl.h" + +PR_IMPLEMENT(PRWord *) +PR_GetGCRegisters(PRThread *t, int isCurrent, int *np) +{ + return _MD_HomeGCRegisters(t, isCurrent, np); +} + +PR_IMPLEMENT(PRStatus) +PR_ThreadScanStackPointers(PRThread* t, + PRScanStackFun scanFun, void* scanClosure) +{ + PRThread* current = PR_GetCurrentThread(); + PRWord *sp, *esp, *p0; + int n; + void **ptd; + PRStatus status; + PRUint32 index; + int stack_end; + + /* + ** Store the thread's registers in the thread structure so the GC + ** can scan them. Then scan them. + */ + p0 = _MD_HomeGCRegisters(t, t == current, &n); + status = scanFun(t, (void**)p0, n, scanClosure); + if (status != PR_SUCCESS) { + return status; + } + + /* Scan the C stack for pointers into the GC heap */ +#if defined(XP_PC) && defined(WIN16) + /* + ** Under WIN16, the stack of the current thread is always mapped into + ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan + ** the "task stack". Otherwise, scan the "cached stack" of the inactive + ** thread... + */ + if (t == current) { + sp = (PRWord*) &stack_end; + esp = (PRWord*) _pr_top_of_task_stack; + + PR_ASSERT(sp <= esp); + } else { + sp = (PRWord*) PR_GetSP(t); + esp = (PRWord*) t->stack->stackTop; + + PR_ASSERT((t->stack->stackSize == 0) || + ((sp > (PRWord*)t->stack->stackBottom) && + (sp <= (PRWord*)t->stack->stackTop))); + } +#else /* ! WIN16 */ +#ifdef HAVE_STACK_GROWING_UP + if (t == current) { + esp = (PRWord*) &stack_end; + } else { + esp = (PRWord*) PR_GetSP(t); + } + sp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((esp > (PRWord*)t->stack->stackTop) && + (esp < (PRWord*)t->stack->stackBottom)); + } +#else /* ! HAVE_STACK_GROWING_UP */ + if (t == current) { + sp = (PRWord*) &stack_end; + } else { + sp = (PRWord*) PR_GetSP(t); + } + esp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) && + (sp < (PRWord*)t->stack->stackTop)); + } +#endif /* ! HAVE_STACK_GROWING_UP */ +#endif /* ! WIN16 */ + +#if defined(WIN16) + { + prword_t scan; + prword_t limit; + + scan = (prword_t) sp; + limit = (prword_t) esp; + while (scan < limit) { + prword_t *test; + + test = *((prword_t **)scan); + status = scanFun(t, (void**)&test, 1, scanClosure); + if (status != PR_SUCCESS) { + return status; + } + scan += sizeof(char); + } + } +#else + if (sp < esp) { + status = scanFun(t, (void**)sp, esp - sp, scanClosure); + if (status != PR_SUCCESS) { + return status; + } + } +#endif + + /* + ** Mark all of the per-thread-data items attached to this thread + ** + ** The execution environment better be accounted for otherwise it + ** will be collected + */ + status = scanFun(t, (void**)&t->environment, 1, scanClosure); + if (status != PR_SUCCESS) { + return status; + } + + /* if thread is not allocated on stack, this is redundant. */ + ptd = t->privateData; + for (index = 0; index < t->tpdLength; index++, ptd++) { + status = scanFun(t, (void**)ptd, 1, scanClosure); + if (status != PR_SUCCESS) { + return status; + } + } + + return PR_SUCCESS; +} + +/* transducer for PR_EnumerateThreads */ +typedef struct PRScanStackData { + PRScanStackFun scanFun; + void* scanClosure; +} PRScanStackData; + +static PRStatus PR_CALLBACK +pr_ScanStack(PRThread* t, int i, void* arg) +{ + PRScanStackData* data = (PRScanStackData*)arg; + return PR_ThreadScanStackPointers(t, data->scanFun, data->scanClosure); +} + +PR_IMPLEMENT(PRStatus) +PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure) +{ + PRScanStackData data; + data.scanFun = scanFun; + data.scanClosure = scanClosure; + return PR_EnumerateThreads(pr_ScanStack, &data); +} + +PR_IMPLEMENT(PRUword) +PR_GetStackSpaceLeft(PRThread* t) +{ + PRThread *current = PR_GetCurrentThread(); + PRWord *sp, *esp; + int stack_end; + +#if defined(WIN16) + /* + ** Under WIN16, the stack of the current thread is always mapped into + ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan + ** the "task stack". Otherwise, scan the "cached stack" of the inactive + ** thread... + */ + if (t == current) { + sp = (PRWord*) &stack_end; + esp = (PRWord*) _pr_top_of_task_stack; + + PR_ASSERT(sp <= esp); + } else { + sp = (PRWord*) PR_GetSP(t); + esp = (PRWord*) t->stack->stackTop; + + PR_ASSERT((t->stack->stackSize == 0) || + ((sp > (PRWord*)t->stack->stackBottom) && + (sp <= (PRWord*)t->stack->stackTop))); + } +#else /* ! WIN16 */ +#ifdef HAVE_STACK_GROWING_UP + if (t == current) { + esp = (PRWord*) &stack_end; + } else { + esp = (PRWord*) PR_GetSP(t); + } + sp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((esp > (PRWord*)t->stack->stackTop) && + (esp < (PRWord*)t->stack->stackBottom)); + } +#else /* ! HAVE_STACK_GROWING_UP */ + if (t == current) { + sp = (PRWord*) &stack_end; + } else { + sp = (PRWord*) PR_GetSP(t); + } + esp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) && + (sp < (PRWord*)t->stack->stackTop)); + } +#endif /* ! HAVE_STACK_GROWING_UP */ +#endif /* ! WIN16 */ + return (PRUword)t->stack->stackSize - ((PRWord)esp - (PRWord)sp); +} diff --git a/nsprpub/pr/src/misc/prtime.c b/nsprpub/pr/src/misc/prtime.c new file mode 100644 index 0000000000..6d711a6b8d --- /dev/null +++ b/nsprpub/pr/src/misc/prtime.c @@ -0,0 +1,2150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * prtime.c -- + * + * NSPR date and time functions + * + */ + +#include "prinit.h" +#include "prtime.h" +#include "prlock.h" +#include "prprf.h" +#include "prlog.h" + +#include <string.h> +#include <ctype.h> +#include <errno.h> /* for EINVAL */ +#include <time.h> + +/* + * The COUNT_LEAPS macro counts the number of leap years passed by + * till the start of the given year Y. At the start of the year 4 + * A.D. the number of leap years passed by is 0, while at the start of + * the year 5 A.D. this count is 1. The number of years divisible by + * 100 but not divisible by 400 (the non-leap years) is deducted from + * the count to get the correct number of leap years. + * + * The COUNT_DAYS macro counts the number of days since 01/01/01 till the + * start of the given year Y. The number of days at the start of the year + * 1 is 0 while the number of days at the start of the year 2 is 365 + * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01 + * midnight 00:00:00. + */ + +#define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 ) +#define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) ) +#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A)) + +/* + * Static variables used by functions in this file + */ + +/* + * The following array contains the day of year for the last day of + * each month, where index 1 is January, and day 0 is January 1. + */ + +static const int lastDayOfMonth[2][13] = { + {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364}, + {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365} +}; + +/* + * The number of days in a month + */ + +static const PRInt8 nDays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +/* + * Declarations for internal functions defined later in this file. + */ + +static void ComputeGMT(PRTime time, PRExplodedTime *gmt); +static int IsLeapYear(PRInt16 year); +static void ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset); + +/* + *------------------------------------------------------------------------ + * + * ComputeGMT -- + * + * Caveats: + * - we ignore leap seconds + * + *------------------------------------------------------------------------ + */ + +static void +ComputeGMT(PRTime time, PRExplodedTime *gmt) +{ + PRInt32 tmp, rem; + PRInt32 numDays; + PRInt64 numDays64, rem64; + int isLeap; + PRInt64 sec; + PRInt64 usec; + PRInt64 usecPerSec; + PRInt64 secPerDay; + + /* + * We first do the usec, sec, min, hour thing so that we do not + * have to do LL arithmetic. + */ + + LL_I2L(usecPerSec, 1000000L); + LL_DIV(sec, time, usecPerSec); + LL_MOD(usec, time, usecPerSec); + LL_L2I(gmt->tm_usec, usec); + /* Correct for weird mod semantics so the remainder is always positive */ + if (gmt->tm_usec < 0) { + PRInt64 one; + + LL_I2L(one, 1L); + LL_SUB(sec, sec, one); + gmt->tm_usec += 1000000L; + } + + LL_I2L(secPerDay, 86400L); + LL_DIV(numDays64, sec, secPerDay); + LL_MOD(rem64, sec, secPerDay); + /* We are sure both of these numbers can fit into PRInt32 */ + LL_L2I(numDays, numDays64); + LL_L2I(rem, rem64); + if (rem < 0) { + numDays--; + rem += 86400L; + } + + /* Compute day of week. Epoch started on a Thursday. */ + + gmt->tm_wday = (numDays + 4) % 7; + if (gmt->tm_wday < 0) { + gmt->tm_wday += 7; + } + + /* Compute the time of day. */ + + gmt->tm_hour = rem / 3600; + rem %= 3600; + gmt->tm_min = rem / 60; + gmt->tm_sec = rem % 60; + + /* + * Compute the year by finding the 400 year period, then working + * down from there. + * + * Since numDays is originally the number of days since January 1, 1970, + * we must change it to be the number of days from January 1, 0001. + */ + + numDays += 719162; /* 719162 = days from year 1 up to 1970 */ + tmp = numDays / 146097; /* 146097 = days in 400 years */ + rem = numDays % 146097; + gmt->tm_year = tmp * 400 + 1; + + /* Compute the 100 year period. */ + + tmp = rem / 36524; /* 36524 = days in 100 years */ + rem %= 36524; + if (tmp == 4) { /* the 400th year is a leap year */ + tmp = 3; + rem = 36524; + } + gmt->tm_year += tmp * 100; + + /* Compute the 4 year period. */ + + tmp = rem / 1461; /* 1461 = days in 4 years */ + rem %= 1461; + gmt->tm_year += tmp * 4; + + /* Compute which year in the 4. */ + + tmp = rem / 365; + rem %= 365; + if (tmp == 4) { /* the 4th year is a leap year */ + tmp = 3; + rem = 365; + } + + gmt->tm_year += tmp; + gmt->tm_yday = rem; + isLeap = IsLeapYear(gmt->tm_year); + + /* Compute the month and day of month. */ + + for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) { + } + gmt->tm_month = --tmp; + gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp]; + + gmt->tm_params.tp_gmt_offset = 0; + gmt->tm_params.tp_dst_offset = 0; +} + + +/* + *------------------------------------------------------------------------ + * + * PR_ExplodeTime -- + * + * Cf. struct tm *gmtime(const time_t *tp) and + * struct tm *localtime(const time_t *tp) + * + *------------------------------------------------------------------------ + */ + +PR_IMPLEMENT(void) +PR_ExplodeTime( + PRTime usecs, + PRTimeParamFn params, + PRExplodedTime *exploded) +{ + ComputeGMT(usecs, exploded); + exploded->tm_params = params(exploded); + ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset + + exploded->tm_params.tp_dst_offset); +} + + +/* + *------------------------------------------------------------------------ + * + * PR_ImplodeTime -- + * + * Cf. time_t mktime(struct tm *tp) + * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough. + * + *------------------------------------------------------------------------ + */ +PR_IMPLEMENT(PRTime) +PR_ImplodeTime(const PRExplodedTime *exploded) +{ + PRExplodedTime copy; + PRTime retVal; + PRInt64 secPerDay, usecPerSec; + PRInt64 temp; + PRInt64 numSecs64; + PRInt32 numDays; + PRInt32 numSecs; + + /* Normalize first. Do this on our copy */ + copy = *exploded; + PR_NormalizeTime(©, PR_GMTParameters); + + numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year); + + numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600 + + copy.tm_min * 60 + copy.tm_sec; + + LL_I2L(temp, numDays); + LL_I2L(secPerDay, 86400); + LL_MUL(temp, temp, secPerDay); + LL_I2L(numSecs64, numSecs); + LL_ADD(numSecs64, numSecs64, temp); + + /* apply the GMT and DST offsets */ + LL_I2L(temp, copy.tm_params.tp_gmt_offset); + LL_SUB(numSecs64, numSecs64, temp); + LL_I2L(temp, copy.tm_params.tp_dst_offset); + LL_SUB(numSecs64, numSecs64, temp); + + LL_I2L(usecPerSec, 1000000L); + LL_MUL(temp, numSecs64, usecPerSec); + LL_I2L(retVal, copy.tm_usec); + LL_ADD(retVal, retVal, temp); + + return retVal; +} + +/* + *------------------------------------------------------------------------- + * + * IsLeapYear -- + * + * Returns 1 if the year is a leap year, 0 otherwise. + * + *------------------------------------------------------------------------- + */ + +static int IsLeapYear(PRInt16 year) +{ + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { + return 1; + } + return 0; +} + +/* + * 'secOffset' should be less than 86400 (i.e., a day). + * 'time' should point to a normalized PRExplodedTime. + */ + +static void +ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset) +{ + time->tm_sec += secOffset; + + /* Note that in this implementation we do not count leap seconds */ + if (time->tm_sec < 0 || time->tm_sec >= 60) { + time->tm_min += time->tm_sec / 60; + time->tm_sec %= 60; + if (time->tm_sec < 0) { + time->tm_sec += 60; + time->tm_min--; + } + } + + if (time->tm_min < 0 || time->tm_min >= 60) { + time->tm_hour += time->tm_min / 60; + time->tm_min %= 60; + if (time->tm_min < 0) { + time->tm_min += 60; + time->tm_hour--; + } + } + + if (time->tm_hour < 0) { + /* Decrement mday, yday, and wday */ + time->tm_hour += 24; + time->tm_mday--; + time->tm_yday--; + if (time->tm_mday < 1) { + time->tm_month--; + if (time->tm_month < 0) { + time->tm_month = 11; + time->tm_year--; + if (IsLeapYear(time->tm_year)) { + time->tm_yday = 365; + } + else { + time->tm_yday = 364; + } + } + time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } + time->tm_wday--; + if (time->tm_wday < 0) { + time->tm_wday = 6; + } + } else if (time->tm_hour > 23) { + /* Increment mday, yday, and wday */ + time->tm_hour -= 24; + time->tm_mday++; + time->tm_yday++; + if (time->tm_mday > + nDays[IsLeapYear(time->tm_year)][time->tm_month]) { + time->tm_mday = 1; + time->tm_month++; + if (time->tm_month > 11) { + time->tm_month = 0; + time->tm_year++; + time->tm_yday = 0; + } + } + time->tm_wday++; + if (time->tm_wday > 6) { + time->tm_wday = 0; + } + } +} + +PR_IMPLEMENT(void) +PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params) +{ + int daysInMonth; + PRInt32 numDays; + + /* Get back to GMT */ + time->tm_sec -= time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset; + time->tm_params.tp_gmt_offset = 0; + time->tm_params.tp_dst_offset = 0; + + /* Now normalize GMT */ + + if (time->tm_usec < 0 || time->tm_usec >= 1000000) { + time->tm_sec += time->tm_usec / 1000000; + time->tm_usec %= 1000000; + if (time->tm_usec < 0) { + time->tm_usec += 1000000; + time->tm_sec--; + } + } + + /* Note that we do not count leap seconds in this implementation */ + if (time->tm_sec < 0 || time->tm_sec >= 60) { + time->tm_min += time->tm_sec / 60; + time->tm_sec %= 60; + if (time->tm_sec < 0) { + time->tm_sec += 60; + time->tm_min--; + } + } + + if (time->tm_min < 0 || time->tm_min >= 60) { + time->tm_hour += time->tm_min / 60; + time->tm_min %= 60; + if (time->tm_min < 0) { + time->tm_min += 60; + time->tm_hour--; + } + } + + if (time->tm_hour < 0 || time->tm_hour >= 24) { + time->tm_mday += time->tm_hour / 24; + time->tm_hour %= 24; + if (time->tm_hour < 0) { + time->tm_hour += 24; + time->tm_mday--; + } + } + + /* Normalize month and year before mday */ + if (time->tm_month < 0 || time->tm_month >= 12) { + time->tm_year += time->tm_month / 12; + time->tm_month %= 12; + if (time->tm_month < 0) { + time->tm_month += 12; + time->tm_year--; + } + } + + /* Now that month and year are in proper range, normalize mday */ + + if (time->tm_mday < 1) { + /* mday too small */ + do { + /* the previous month */ + time->tm_month--; + if (time->tm_month < 0) { + time->tm_month = 11; + time->tm_year--; + } + time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } while (time->tm_mday < 1); + } else { + daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + while (time->tm_mday > daysInMonth) { + /* mday too large */ + time->tm_mday -= daysInMonth; + time->tm_month++; + if (time->tm_month > 11) { + time->tm_month = 0; + time->tm_year++; + } + daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } + } + + /* Recompute yday and wday */ + time->tm_yday = time->tm_mday + + lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month]; + + numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + time->tm_yday; + time->tm_wday = (numDays + 4) % 7; + if (time->tm_wday < 0) { + time->tm_wday += 7; + } + + /* Recompute time parameters */ + + time->tm_params = params(time); + + ApplySecOffset(time, time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset); +} + + +/* + *------------------------------------------------------------------------- + * + * PR_LocalTimeParameters -- + * + * returns the time parameters for the local time zone + * + * The following uses localtime() from the standard C library. + * (time.h) This is our fallback implementation. Unix, PC, and BeOS + * use this version. A platform may have its own machine-dependent + * implementation of this function. + * + *------------------------------------------------------------------------- + */ + +#if defined(HAVE_INT_LOCALTIME_R) + +/* + * In this case we could define the macro as + * #define MT_safe_localtime(timer, result) \ + * (localtime_r(timer, result) == 0 ? result : NULL) + * I chose to compare the return value of localtime_r with -1 so + * that I can catch the cases where localtime_r returns a pointer + * to struct tm. The macro definition above would not be able to + * detect such mistakes because it is legal to compare a pointer + * with 0. + */ + +#define MT_safe_localtime(timer, result) \ + (localtime_r(timer, result) == -1 ? NULL: result) + +#elif defined(HAVE_POINTER_LOCALTIME_R) + +#define MT_safe_localtime localtime_r + +#elif defined(_MSC_VER) + +/* Visual C++ has had localtime_s() since Visual C++ 2005. */ + +static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result) +{ + errno_t err = localtime_s(result, clock); + if (err != 0) { + errno = err; + return NULL; + } + return result; +} + +#else + +#define HAVE_LOCALTIME_MONITOR 1 /* We use 'monitor' to serialize our calls + * to localtime(). */ +static PRLock *monitor = NULL; + +static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result) +{ + struct tm *tmPtr; + int needLock = PR_Initialized(); /* We need to use a lock to protect + * against NSPR threads only when the + * NSPR thread system is activated. */ + + if (needLock) { + PR_Lock(monitor); + } + + /* + * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock' + * represents a time before midnight January 1, 1970. In + * that case, we also return a NULL pointer and the struct tm + * object pointed to by 'result' is not modified. + * + * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long + * hence, does not recognize negative values of clock as pre-1/1/70. + * We have to manually check (WIN16 only) for negative value of + * clock and return NULL. + * + * With negative values of clock, OS/2 returns the struct tm for + * clock plus ULONG_MAX. So we also have to check for the invalid + * structs returned for timezones west of Greenwich when clock == 0. + */ + + tmPtr = localtime(clock); + +#if defined(WIN16) || defined(XP_OS2) + if ( (PRInt32) *clock < 0 || + ( (PRInt32) *clock == 0 && tmPtr->tm_year != 70)) { + result = NULL; + } + else { + *result = *tmPtr; + } +#else + if (tmPtr) { + *result = *tmPtr; + } else { + result = NULL; + } +#endif /* WIN16 */ + + if (needLock) { + PR_Unlock(monitor); + } + + return result; +} + +#endif /* definition of MT_safe_localtime() */ + +void _PR_InitTime(void) +{ +#ifdef HAVE_LOCALTIME_MONITOR + monitor = PR_NewLock(); +#endif +#ifdef WINCE + _MD_InitTime(); +#endif +} + +void _PR_CleanupTime(void) +{ +#ifdef HAVE_LOCALTIME_MONITOR + if (monitor) { + PR_DestroyLock(monitor); + monitor = NULL; + } +#endif +#ifdef WINCE + _MD_CleanupTime(); +#endif +} + +#if defined(XP_UNIX) || defined(XP_PC) + +PR_IMPLEMENT(PRTimeParameters) +PR_LocalTimeParameters(const PRExplodedTime *gmt) +{ + + PRTimeParameters retVal; + struct tm localTime; + struct tm *localTimeResult; + time_t secs; + PRTime secs64; + PRInt64 usecPerSec; + PRInt64 usecPerSec_1; + PRInt64 maxInt32; + PRInt64 minInt32; + PRInt32 dayOffset; + PRInt32 offset2Jan1970; + PRInt32 offsetNew; + int isdst2Jan1970; + + /* + * Calculate the GMT offset. First, figure out what is + * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400 + * seconds, since the epoch) in local time. Then we calculate + * the difference between local time and GMT in seconds: + * gmt_offset = local_time - GMT + * + * Caveat: the validity of this calculation depends on two + * assumptions: + * 1. Daylight saving time was not in effect on Jan. 2, 1970. + * 2. The time zone of the geographic location has not changed + * since Jan. 2, 1970. + */ + + secs = 86400L; + localTimeResult = MT_safe_localtime(&secs, &localTime); + PR_ASSERT(localTimeResult != NULL); + if (localTimeResult == NULL) { + /* Shouldn't happen. Use safe fallback for optimized builds. */ + return PR_GMTParameters(gmt); + } + + /* GMT is 00:00:00, 2nd of Jan. */ + + offset2Jan1970 = (PRInt32)localTime.tm_sec + + 60L * (PRInt32)localTime.tm_min + + 3600L * (PRInt32)localTime.tm_hour + + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L); + + isdst2Jan1970 = localTime.tm_isdst; + + /* + * Now compute DST offset. We calculate the overall offset + * of local time from GMT, similar to above. The overall + * offset has two components: gmt offset and dst offset. + * We subtract gmt offset from the overall offset to get + * the dst offset. + * overall_offset = local_time - GMT + * overall_offset = gmt_offset + dst_offset + * ==> dst_offset = local_time - GMT - gmt_offset + */ + + secs64 = PR_ImplodeTime(gmt); /* This is still in microseconds */ + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_I2L(usecPerSec_1, PR_USEC_PER_SEC - 1); + /* Convert to seconds, truncating down (3.1 -> 3 and -3.1 -> -4) */ + if (LL_GE_ZERO(secs64)) { + LL_DIV(secs64, secs64, usecPerSec); + } else { + LL_NEG(secs64, secs64); + LL_ADD(secs64, secs64, usecPerSec_1); + LL_DIV(secs64, secs64, usecPerSec); + LL_NEG(secs64, secs64); + } + LL_I2L(maxInt32, PR_INT32_MAX); + LL_I2L(minInt32, PR_INT32_MIN); + if (LL_CMP(secs64, >, maxInt32) || LL_CMP(secs64, <, minInt32)) { + /* secs64 is too large or too small for time_t (32-bit integer) */ + retVal.tp_gmt_offset = offset2Jan1970; + retVal.tp_dst_offset = 0; + return retVal; + } + LL_L2I(secs, secs64); + + /* + * On Windows, localtime() (and our MT_safe_localtime() too) + * returns a NULL pointer for time before midnight January 1, + * 1970 GMT. In that case, we just use the GMT offset for + * Jan 2, 1970 and assume that DST was not in effect. + */ + + if (MT_safe_localtime(&secs, &localTime) == NULL) { + retVal.tp_gmt_offset = offset2Jan1970; + retVal.tp_dst_offset = 0; + return retVal; + } + + /* + * dayOffset is the offset between local time and GMT in + * the day component, which can only be -1, 0, or 1. We + * use the day of the week to compute dayOffset. + */ + + dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday; + + /* + * Need to adjust for wrapping around of day of the week from + * 6 back to 0. + */ + + if (dayOffset == -6) { + /* Local time is Sunday (0) and GMT is Saturday (6) */ + dayOffset = 1; + } else if (dayOffset == 6) { + /* Local time is Saturday (6) and GMT is Sunday (0) */ + dayOffset = -1; + } + + offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec + + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min) + + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour) + + 86400L * (PRInt32)dayOffset; + + if (localTime.tm_isdst <= 0) { + /* DST is not in effect */ + retVal.tp_gmt_offset = offsetNew; + retVal.tp_dst_offset = 0; + } else { + /* DST is in effect */ + if (isdst2Jan1970 <=0) { + /* + * DST was not in effect back in 2 Jan. 1970. + * Use the offset back then as the GMT offset, + * assuming the time zone has not changed since then. + */ + retVal.tp_gmt_offset = offset2Jan1970; + retVal.tp_dst_offset = offsetNew - offset2Jan1970; + } else { + /* + * DST was also in effect back in 2 Jan. 1970. + * Then our clever trick (or rather, ugly hack) fails. + * We will just assume DST offset is an hour. + */ + retVal.tp_gmt_offset = offsetNew - 3600; + retVal.tp_dst_offset = 3600; + } + } + + return retVal; +} + +#endif /* defined(XP_UNIX) || defined(XP_PC) */ + +/* + *------------------------------------------------------------------------ + * + * PR_USPacificTimeParameters -- + * + * The time parameters function for the US Pacific Time Zone. + * + *------------------------------------------------------------------------ + */ + +/* + * Returns the mday of the first sunday of the month, where + * mday and wday are for a given day in the month. + * mdays start with 1 (e.g. 1..31). + * wdays start with 0 and are in the range 0..6. 0 = Sunday. + */ +#define firstSunday(mday, wday) (((mday - wday + 7 - 1) % 7) + 1) + +/* + * Returns the mday for the N'th Sunday of the month, where + * mday and wday are for a given day in the month. + * mdays start with 1 (e.g. 1..31). + * wdays start with 0 and are in the range 0..6. 0 = Sunday. + * N has the following values: 0 = first, 1 = second (etc), -1 = last. + * ndays is the number of days in that month, the same value as the + * mday of the last day of the month. + */ +static PRInt32 +NthSunday(PRInt32 mday, PRInt32 wday, PRInt32 N, PRInt32 ndays) +{ + PRInt32 firstSun = firstSunday(mday, wday); + + if (N < 0) { + N = (ndays - firstSun) / 7; + } + return firstSun + (7 * N); +} + +typedef struct DSTParams { + PRInt8 dst_start_month; /* 0 = January */ + PRInt8 dst_start_Nth_Sunday; /* N as defined above */ + PRInt8 dst_start_month_ndays; /* ndays as defined above */ + PRInt8 dst_end_month; /* 0 = January */ + PRInt8 dst_end_Nth_Sunday; /* N as defined above */ + PRInt8 dst_end_month_ndays; /* ndays as defined above */ +} DSTParams; + +static const DSTParams dstParams[2] = { + /* year < 2007: First April Sunday - Last October Sunday */ + { 3, 0, 30, 9, -1, 31 }, + /* year >= 2007: Second March Sunday - First November Sunday */ + { 2, 1, 31, 10, 0, 30 } +}; + +PR_IMPLEMENT(PRTimeParameters) +PR_USPacificTimeParameters(const PRExplodedTime *gmt) +{ + const DSTParams *dst; + PRTimeParameters retVal; + PRExplodedTime st; + + /* + * Based on geographic location and GMT, figure out offset of + * standard time from GMT. In this example implementation, we + * assume the local time zone is US Pacific Time. + */ + + retVal.tp_gmt_offset = -8L * 3600L; + + /* + * Make a copy of GMT. Note that the tm_params field of this copy + * is ignored. + */ + + st.tm_usec = gmt->tm_usec; + st.tm_sec = gmt->tm_sec; + st.tm_min = gmt->tm_min; + st.tm_hour = gmt->tm_hour; + st.tm_mday = gmt->tm_mday; + st.tm_month = gmt->tm_month; + st.tm_year = gmt->tm_year; + st.tm_wday = gmt->tm_wday; + st.tm_yday = gmt->tm_yday; + + /* Apply the offset to GMT to obtain the local standard time */ + ApplySecOffset(&st, retVal.tp_gmt_offset); + + if (st.tm_year < 2007) { /* first April Sunday - Last October Sunday */ + dst = &dstParams[0]; + } else { /* Second March Sunday - First November Sunday */ + dst = &dstParams[1]; + } + + /* + * Apply the rules on standard time or GMT to obtain daylight saving + * time offset. In this implementation, we use the US DST rule. + */ + if (st.tm_month < dst->dst_start_month) { + retVal.tp_dst_offset = 0L; + } else if (st.tm_month == dst->dst_start_month) { + int NthSun = NthSunday(st.tm_mday, st.tm_wday, + dst->dst_start_Nth_Sunday, + dst->dst_start_month_ndays); + if (st.tm_mday < NthSun) { /* Before starting Sunday */ + retVal.tp_dst_offset = 0L; + } else if (st.tm_mday == NthSun) { /* Starting Sunday */ + /* 01:59:59 PST -> 03:00:00 PDT */ + if (st.tm_hour < 2) { + retVal.tp_dst_offset = 0L; + } else { + retVal.tp_dst_offset = 3600L; + } + } else { /* After starting Sunday */ + retVal.tp_dst_offset = 3600L; + } + } else if (st.tm_month < dst->dst_end_month) { + retVal.tp_dst_offset = 3600L; + } else if (st.tm_month == dst->dst_end_month) { + int NthSun = NthSunday(st.tm_mday, st.tm_wday, + dst->dst_end_Nth_Sunday, + dst->dst_end_month_ndays); + if (st.tm_mday < NthSun) { /* Before ending Sunday */ + retVal.tp_dst_offset = 3600L; + } else if (st.tm_mday == NthSun) { /* Ending Sunday */ + /* 01:59:59 PDT -> 01:00:00 PST */ + if (st.tm_hour < 1) { + retVal.tp_dst_offset = 3600L; + } else { + retVal.tp_dst_offset = 0L; + } + } else { /* After ending Sunday */ + retVal.tp_dst_offset = 0L; + } + } else { + retVal.tp_dst_offset = 0L; + } + return retVal; +} + +/* + *------------------------------------------------------------------------ + * + * PR_GMTParameters -- + * + * Returns the PRTimeParameters for Greenwich Mean Time. + * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0. + * + *------------------------------------------------------------------------ + */ + +PR_IMPLEMENT(PRTimeParameters) +PR_GMTParameters(const PRExplodedTime *gmt) +{ + PRTimeParameters retVal = { 0, 0 }; + return retVal; +} + +/* + * The following code implements PR_ParseTimeString(). It is based on + * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>. + */ + +/* + * We only recognize the abbreviations of a small subset of time zones + * in North America, Europe, and Japan. + * + * PST/PDT: Pacific Standard/Daylight Time + * MST/MDT: Mountain Standard/Daylight Time + * CST/CDT: Central Standard/Daylight Time + * EST/EDT: Eastern Standard/Daylight Time + * AST: Atlantic Standard Time + * NST: Newfoundland Standard Time + * GMT: Greenwich Mean Time + * BST: British Summer Time + * MET: Middle Europe Time + * EET: Eastern Europe Time + * JST: Japan Standard Time + */ + +typedef enum +{ + TT_UNKNOWN, + + TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT, + + TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN, + TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC, + + TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT, + TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST +} TIME_TOKEN; + +/* + * This parses a time/date string into a PRTime + * (microseconds after "1-Jan-1970 00:00:00 GMT"). + * It returns PR_SUCCESS on success, and PR_FAILURE + * if the time/date string can't be parsed. + * + * Many formats are handled, including: + * + * 14 Apr 89 03:20:12 + * 14 Apr 89 03:20 GMT + * Fri, 17 Mar 89 4:01:33 + * Fri, 17 Mar 89 4:01 GMT + * Mon Jan 16 16:12 PDT 1989 + * Mon Jan 16 16:12 +0130 1989 + * 6 May 1992 16:41-JST (Wednesday) + * 22-AUG-1993 10:59:12.82 + * 22-AUG-1993 10:59pm + * 22-AUG-1993 12:59am + * 22-AUG-1993 12:59 PM + * Friday, August 04, 1995 3:54 PM + * 06/21/95 04:24:34 PM + * 20/06/95 21:07 + * 95-06-08 19:32:48 EDT + * + * If the input string doesn't contain a description of the timezone, + * we consult the `default_to_gmt' to decide whether the string should + * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE). + * The correct value for this argument depends on what standard specified + * the time string which you are parsing. + */ + +PR_IMPLEMENT(PRStatus) +PR_ParseTimeStringToExplodedTime( + const char *string, + PRBool default_to_gmt, + PRExplodedTime *result) +{ + TIME_TOKEN dotw = TT_UNKNOWN; + TIME_TOKEN month = TT_UNKNOWN; + TIME_TOKEN zone = TT_UNKNOWN; + int zone_offset = -1; + int dst_offset = 0; + int date = -1; + PRInt32 year = -1; + int hour = -1; + int min = -1; + int sec = -1; + struct tm *localTimeResult; + + const char *rest = string; + + int iterations = 0; + + PR_ASSERT(string && result); + if (!string || !result) { + return PR_FAILURE; + } + + while (*rest) + { + + if (iterations++ > 1000) + { + return PR_FAILURE; + } + + switch (*rest) + { + case 'a': case 'A': + if (month == TT_UNKNOWN && + (rest[1] == 'p' || rest[1] == 'P') && + (rest[2] == 'r' || rest[2] == 'R')) { + month = TT_APR; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_AST; + } + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'g' || rest[2] == 'G')) { + month = TT_AUG; + } + break; + case 'b': case 'B': + if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_BST; + } + break; + case 'c': case 'C': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_CDT; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_CST; + } + break; + case 'd': case 'D': + if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'c' || rest[2] == 'C')) { + month = TT_DEC; + } + break; + case 'e': case 'E': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_EDT; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_EET; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_EST; + } + break; + case 'f': case 'F': + if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'b' || rest[2] == 'B')) { + month = TT_FEB; + } + else if (dotw == TT_UNKNOWN && + (rest[1] == 'r' || rest[1] == 'R') && + (rest[2] == 'i' || rest[2] == 'I')) { + dotw = TT_FRI; + } + break; + case 'g': case 'G': + if (zone == TT_UNKNOWN && + (rest[1] == 'm' || rest[1] == 'M') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_GMT; + } + break; + case 'j': case 'J': + if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'n' || rest[2] == 'N')) { + month = TT_JAN; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_JST; + } + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'l' || rest[2] == 'L')) { + month = TT_JUL; + } + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'n' || rest[2] == 'N')) { + month = TT_JUN; + } + break; + case 'm': case 'M': + if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'r' || rest[2] == 'R')) { + month = TT_MAR; + } + else if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'y' || rest[2] == 'Y')) { + month = TT_MAY; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_MDT; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_MET; + } + else if (dotw == TT_UNKNOWN && + (rest[1] == 'o' || rest[1] == 'O') && + (rest[2] == 'n' || rest[2] == 'N')) { + dotw = TT_MON; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_MST; + } + break; + case 'n': case 'N': + if (month == TT_UNKNOWN && + (rest[1] == 'o' || rest[1] == 'O') && + (rest[2] == 'v' || rest[2] == 'V')) { + month = TT_NOV; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_NST; + } + break; + case 'o': case 'O': + if (month == TT_UNKNOWN && + (rest[1] == 'c' || rest[1] == 'C') && + (rest[2] == 't' || rest[2] == 'T')) { + month = TT_OCT; + } + break; + case 'p': case 'P': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_PDT; + } + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) { + zone = TT_PST; + } + break; + case 's': case 'S': + if (dotw == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 't' || rest[2] == 'T')) { + dotw = TT_SAT; + } + else if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'p' || rest[2] == 'P')) { + month = TT_SEP; + } + else if (dotw == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'n' || rest[2] == 'N')) { + dotw = TT_SUN; + } + break; + case 't': case 'T': + if (dotw == TT_UNKNOWN && + (rest[1] == 'h' || rest[1] == 'H') && + (rest[2] == 'u' || rest[2] == 'U')) { + dotw = TT_THU; + } + else if (dotw == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'e' || rest[2] == 'E')) { + dotw = TT_TUE; + } + break; + case 'u': case 'U': + if (zone == TT_UNKNOWN && + (rest[1] == 't' || rest[1] == 'T') && + !(rest[2] >= 'A' && rest[2] <= 'Z') && + !(rest[2] >= 'a' && rest[2] <= 'z')) + /* UT is the same as GMT but UTx is not. */ + { + zone = TT_GMT; + } + break; + case 'w': case 'W': + if (dotw == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'd' || rest[2] == 'D')) { + dotw = TT_WED; + } + break; + + case '+': case '-': + { + const char *end; + int sign; + if (zone_offset != -1) + { + /* already got one... */ + rest++; + break; + } + if (zone != TT_UNKNOWN && zone != TT_GMT) + { + /* GMT+0300 is legal, but PST+0300 is not. */ + rest++; + break; + } + + sign = ((*rest == '+') ? 1 : -1); + rest++; /* move over sign */ + end = rest; + while (*end >= '0' && *end <= '9') { + end++; + } + if (rest == end) { /* no digits here */ + break; + } + + if ((end - rest) == 4) + /* offset in HHMM */ + zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) + + (((rest[2]-'0')*10) + (rest[3]-'0'))); + else if ((end - rest) == 2) + /* offset in hours */ + { + zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60; + } + else if ((end - rest) == 1) + /* offset in hours */ + { + zone_offset = (rest[0]-'0') * 60; + } + else + /* 3 or >4 */ + { + break; + } + + zone_offset *= sign; + zone = TT_GMT; + break; + } + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int tmp_hour = -1; + int tmp_min = -1; + int tmp_sec = -1; + const char *end = rest + 1; + while (*end >= '0' && *end <= '9') { + end++; + } + + /* end is now the first character after a range of digits. */ + + if (*end == ':') + { + if (hour >= 0 && min >= 0) { /* already got it */ + break; + } + + /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */ + if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + { + break; + } + if ((end - rest) == 2) + tmp_hour = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else { + tmp_hour = (rest[0]-'0'); + } + + /* move over the colon, and parse minutes */ + + rest = ++end; + while (*end >= '0' && *end <= '9') { + end++; + } + + if (end == rest) + /* no digits after first colon? */ + { + break; + } + if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + { + break; + } + if ((end - rest) == 2) + tmp_min = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else { + tmp_min = (rest[0]-'0'); + } + + /* now go for seconds */ + rest = end; + if (*rest == ':') { + rest++; + } + end = rest; + while (*end >= '0' && *end <= '9') { + end++; + } + + if (end == rest) + /* no digits after second colon - that's ok. */ + ; + else if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + { + break; + } + if ((end - rest) == 2) + tmp_sec = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else { + tmp_sec = (rest[0]-'0'); + } + + /* If we made it here, we've parsed hour and min, + and possibly sec, so it worked as a unit. */ + + /* skip over whitespace and see if there's an AM or PM + directly following the time. + */ + if (tmp_hour <= 12) + { + const char *s = end; + while (*s && (*s == ' ' || *s == '\t')) { + s++; + } + if ((s[0] == 'p' || s[0] == 'P') && + (s[1] == 'm' || s[1] == 'M')) + /* 10:05pm == 22:05, and 12:05pm == 12:05 */ + { + tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12); + } + else if (tmp_hour == 12 && + (s[0] == 'a' || s[0] == 'A') && + (s[1] == 'm' || s[1] == 'M')) + /* 12:05am == 00:05 */ + { + tmp_hour = 0; + } + } + + hour = tmp_hour; + min = tmp_min; + sec = tmp_sec; + rest = end; + break; + } + if ((*end == '/' || *end == '-') && + end[1] >= '0' && end[1] <= '9') + { + /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95 + or even 95-06-05... + #### But it doesn't handle 1995-06-22. + */ + int n1, n2, n3; + const char *s; + + if (month != TT_UNKNOWN) + /* if we saw a month name, this can't be. */ + { + break; + } + + s = rest; + + n1 = (*s++ - '0'); /* first 1 or 2 digits */ + if (*s >= '0' && *s <= '9') { + n1 = n1*10 + (*s++ - '0'); + } + + if (*s != '/' && *s != '-') { /* slash */ + break; + } + s++; + + if (*s < '0' || *s > '9') { /* second 1 or 2 digits */ + break; + } + n2 = (*s++ - '0'); + if (*s >= '0' && *s <= '9') { + n2 = n2*10 + (*s++ - '0'); + } + + if (*s != '/' && *s != '-') { /* slash */ + break; + } + s++; + + if (*s < '0' || *s > '9') { /* third 1, 2, 4, or 5 digits */ + break; + } + n3 = (*s++ - '0'); + if (*s >= '0' && *s <= '9') { + n3 = n3*10 + (*s++ - '0'); + } + + if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */ + { + n3 = n3*10 + (*s++ - '0'); + if (*s < '0' || *s > '9') { + break; + } + n3 = n3*10 + (*s++ - '0'); + if (*s >= '0' && *s <= '9') { + n3 = n3*10 + (*s++ - '0'); + } + } + + if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */ + (*s >= 'A' && *s <= 'Z') || + (*s >= 'a' && *s <= 'z')) { + break; + } + + /* Ok, we parsed three 1-2 digit numbers, with / or - + between them. Now decide what the hell they are + (DD/MM/YY or MM/DD/YY or YY/MM/DD.) + */ + + if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */ + { + if (n2 > 12) { + break; + } + if (n3 > 31) { + break; + } + year = n1; + if (year < 70) { + year += 2000; + } + else if (year < 100) { + year += 1900; + } + month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); + date = n3; + rest = s; + break; + } + + if (n1 > 12 && n2 > 12) /* illegal */ + { + rest = s; + break; + } + + if (n3 < 70) { + n3 += 2000; + } + else if (n3 < 100) { + n3 += 1900; + } + + if (n1 > 12) /* must be DD/MM/YY */ + { + date = n1; + month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); + year = n3; + } + else /* assume MM/DD/YY */ + { + /* #### In the ambiguous case, should we consult the + locale to find out the local default? */ + month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1); + date = n2; + year = n3; + } + rest = s; + } + else if ((*end >= 'A' && *end <= 'Z') || + (*end >= 'a' && *end <= 'z')) + /* Digits followed by non-punctuation - what's that? */ + ; + else if ((end - rest) == 5) /* five digits is a year */ + year = (year < 0 + ? ((rest[0]-'0')*10000L + + (rest[1]-'0')*1000L + + (rest[2]-'0')*100L + + (rest[3]-'0')*10L + + (rest[4]-'0')) + : year); + else if ((end - rest) == 4) /* four digits is a year */ + year = (year < 0 + ? ((rest[0]-'0')*1000L + + (rest[1]-'0')*100L + + (rest[2]-'0')*10L + + (rest[3]-'0')) + : year); + else if ((end - rest) == 2) /* two digits - date or year */ + { + int n = ((rest[0]-'0')*10 + + (rest[1]-'0')); + /* If we don't have a date (day of the month) and we see a number + less than 32, then assume that is the date. + + Otherwise, if we have a date and not a year, assume this is the + year. If it is less than 70, then assume it refers to the 21st + century. If it is two digits (>= 70), assume it refers to this + century. Otherwise, assume it refers to an unambiguous year. + + The world will surely end soon. + */ + if (date < 0 && n < 32) { + date = n; + } + else if (year < 0) + { + if (n < 70) { + year = 2000 + n; + } + else if (n < 100) { + year = 1900 + n; + } + else { + year = n; + } + } + /* else what the hell is this. */ + } + else if ((end - rest) == 1) { /* one digit - date */ + date = (date < 0 ? (rest[0]-'0') : date); + } + /* else, three or more than five digits - what's that? */ + + break; + } + } + + /* Skip to the end of this token, whether we parsed it or not. + Tokens are delimited by whitespace, or ,;-/ + But explicitly not :+-. + */ + while (*rest && + *rest != ' ' && *rest != '\t' && + *rest != ',' && *rest != ';' && + *rest != '-' && *rest != '+' && + *rest != '/' && + *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']') { + rest++; + } + /* skip over uninteresting chars. */ +SKIP_MORE: + while (*rest && + (*rest == ' ' || *rest == '\t' || + *rest == ',' || *rest == ';' || *rest == '/' || + *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')) { + rest++; + } + + /* "-" is ignored at the beginning of a token if we have not yet + parsed a year (e.g., the second "-" in "30-AUG-1966"), or if + the character after the dash is not a digit. */ + if (*rest == '-' && ((rest > string && + isalpha((unsigned char)rest[-1]) && year < 0) || + rest[1] < '0' || rest[1] > '9')) + { + rest++; + goto SKIP_MORE; + } + + } + + if (zone != TT_UNKNOWN && zone_offset == -1) + { + switch (zone) + { + case TT_PST: zone_offset = -8 * 60; break; + case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break; + case TT_MST: zone_offset = -7 * 60; break; + case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break; + case TT_CST: zone_offset = -6 * 60; break; + case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break; + case TT_EST: zone_offset = -5 * 60; break; + case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break; + case TT_AST: zone_offset = -4 * 60; break; + case TT_NST: zone_offset = -3 * 60 - 30; break; + case TT_GMT: zone_offset = 0 * 60; break; + case TT_BST: zone_offset = 0 * 60; dst_offset = 1 * 60; break; + case TT_MET: zone_offset = 1 * 60; break; + case TT_EET: zone_offset = 2 * 60; break; + case TT_JST: zone_offset = 9 * 60; break; + default: + PR_ASSERT (0); + break; + } + } + + /* If we didn't find a year, month, or day-of-the-month, we can't + possibly parse this, and in fact, mktime() will do something random + (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt + a numerologically significant date... */ + if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX) { + return PR_FAILURE; + } + + memset(result, 0, sizeof(*result)); + if (sec != -1) { + result->tm_sec = sec; + } + if (min != -1) { + result->tm_min = min; + } + if (hour != -1) { + result->tm_hour = hour; + } + if (date != -1) { + result->tm_mday = date; + } + if (month != TT_UNKNOWN) { + result->tm_month = (((int)month) - ((int)TT_JAN)); + } + if (year != -1) { + result->tm_year = year; + } + if (dotw != TT_UNKNOWN) { + result->tm_wday = (((int)dotw) - ((int)TT_SUN)); + } + /* + * Mainly to compute wday and yday, but normalized time is also required + * by the check below that works around a Visual C++ 2005 mktime problem. + */ + PR_NormalizeTime(result, PR_GMTParameters); + /* The remaining work is to set the gmt and dst offsets in tm_params. */ + + if (zone == TT_UNKNOWN && default_to_gmt) + { + /* No zone was specified, so pretend the zone was GMT. */ + zone = TT_GMT; + zone_offset = 0; + } + + if (zone_offset == -1) + { + /* no zone was specified, and we're to assume that everything + is local. */ + struct tm localTime; + time_t secs; + + PR_ASSERT(result->tm_month > -1 && + result->tm_mday > 0 && + result->tm_hour > -1 && + result->tm_min > -1 && + result->tm_sec > -1); + + /* + * To obtain time_t from a tm structure representing the local + * time, we call mktime(). However, we need to see if we are + * on 1-Jan-1970 or before. If we are, we can't call mktime() + * because mktime() will crash on win16. In that case, we + * calculate zone_offset based on the zone offset at + * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the + * date we are parsing to transform the date to GMT. We also + * do so if mktime() returns (time_t) -1 (time out of range). + */ + + /* month, day, hours, mins and secs are always non-negative + so we dont need to worry about them. */ + if(result->tm_year >= 1970) + { + PRInt64 usec_per_sec; + + localTime.tm_sec = result->tm_sec; + localTime.tm_min = result->tm_min; + localTime.tm_hour = result->tm_hour; + localTime.tm_mday = result->tm_mday; + localTime.tm_mon = result->tm_month; + localTime.tm_year = result->tm_year - 1900; + /* Set this to -1 to tell mktime "I don't care". If you set + it to 0 or 1, you are making assertions about whether the + date you are handing it is in daylight savings mode or not; + and if you're wrong, it will "fix" it for you. */ + localTime.tm_isdst = -1; + +#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */ + /* + * mktime will return (time_t) -1 if the input is a date + * after 23:59:59, December 31, 3000, US Pacific Time (not + * UTC as documented): + * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx + * But if the year is 3001, mktime also invokes the invalid + * parameter handler, causing the application to crash. This + * problem has been reported in + * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036. + * We avoid this crash by not calling mktime if the date is + * out of range. To use a simple test that works in any time + * zone, we consider year 3000 out of range as well. (See + * bug 480740.) + */ + if (result->tm_year >= 3000) { + /* Emulate what mktime would have done. */ + errno = EINVAL; + secs = (time_t) -1; + } else { + secs = mktime(&localTime); + } +#else + secs = mktime(&localTime); +#endif + if (secs != (time_t) -1) + { + PRTime usecs64; + LL_I2L(usecs64, secs); + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_MUL(usecs64, usecs64, usec_per_sec); + PR_ExplodeTime(usecs64, PR_LocalTimeParameters, result); + return PR_SUCCESS; + } + } + + /* So mktime() can't handle this case. We assume the + zone_offset for the date we are parsing is the same as + the zone offset on 00:00:00 2 Jan 1970 GMT. */ + secs = 86400; + localTimeResult = MT_safe_localtime(&secs, &localTime); + PR_ASSERT(localTimeResult != NULL); + if (localTimeResult == NULL) { + return PR_FAILURE; + } + zone_offset = localTime.tm_min + + 60 * localTime.tm_hour + + 1440 * (localTime.tm_mday - 2); + } + + result->tm_params.tp_gmt_offset = zone_offset * 60; + result->tm_params.tp_dst_offset = dst_offset * 60; + + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +PR_ParseTimeString( + const char *string, + PRBool default_to_gmt, + PRTime *result) +{ + PRExplodedTime tm; + PRStatus rv; + + rv = PR_ParseTimeStringToExplodedTime(string, + default_to_gmt, + &tm); + if (rv != PR_SUCCESS) { + return rv; + } + + *result = PR_ImplodeTime(&tm); + + return PR_SUCCESS; +} + +/* + ******************************************************************* + ******************************************************************* + ** + ** OLD COMPATIBILITY FUNCTIONS + ** + ******************************************************************* + ******************************************************************* + */ + + +/* + *----------------------------------------------------------------------- + * + * PR_FormatTime -- + * + * Format a time value into a buffer. Same semantics as strftime(). + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(PRUint32) +PR_FormatTime(char *buf, int buflen, const char *fmt, + const PRExplodedTime *time) +{ + size_t rv; + struct tm a; + struct tm *ap; + + if (time) { + ap = &a; + a.tm_sec = time->tm_sec; + a.tm_min = time->tm_min; + a.tm_hour = time->tm_hour; + a.tm_mday = time->tm_mday; + a.tm_mon = time->tm_month; + a.tm_wday = time->tm_wday; + a.tm_year = time->tm_year - 1900; + a.tm_yday = time->tm_yday; + a.tm_isdst = time->tm_params.tp_dst_offset ? 1 : 0; + + /* + * On some platforms, for example SunOS 4, struct tm has two + * additional fields: tm_zone and tm_gmtoff. + */ + +#if (__GLIBC__ >= 2) || defined(NETBSD) \ + || defined(OPENBSD) || defined(FREEBSD) \ + || defined(DARWIN) || defined(ANDROID) + a.tm_zone = NULL; + a.tm_gmtoff = time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset; +#endif + } else { + ap = NULL; + } + + rv = strftime(buf, buflen, fmt, ap); + if (!rv && buf && buflen > 0) { + /* + * When strftime fails, the contents of buf are indeterminate. + * Some callers don't check the return value from this function, + * so store an empty string in buf in case they try to print it. + */ + buf[0] = '\0'; + } + return rv; +} + + +/* + * The following string arrays and macros are used by PR_FormatTimeUSEnglish(). + */ + +static const char* abbrevDays[] = +{ + "Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; + +static const char* days[] = +{ + "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" +}; + +static const char* abbrevMonths[] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const char* months[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + + +/* + * Add a single character to the given buffer, incrementing the buffer pointer + * and decrementing the buffer size. Return 0 on error. + */ +#define ADDCHAR( buf, bufSize, ch ) \ +do \ +{ \ + if( bufSize < 1 ) \ + { \ + *(--buf) = '\0'; \ + return 0; \ + } \ + *buf++ = ch; \ + bufSize--; \ +} \ +while(0) + + +/* + * Add a string to the given buffer, incrementing the buffer pointer + * and decrementing the buffer size appropriately. Return 0 on error. + */ +#define ADDSTR( buf, bufSize, str ) \ +do \ +{ \ + PRUint32 strSize = strlen( str ); \ + if( strSize > bufSize ) \ + { \ + if( bufSize==0 ) \ + *(--buf) = '\0'; \ + else \ + *buf = '\0'; \ + return 0; \ + } \ + memcpy(buf, str, strSize); \ + buf += strSize; \ + bufSize -= strSize; \ +} \ +while(0) + +/* Needed by PR_FormatTimeUSEnglish() */ +static unsigned int pr_WeekOfYear(const PRExplodedTime* time, + unsigned int firstDayOfWeek); + + +/*********************************************************************************** + * + * Description: + * This is a dumbed down version of strftime that will format the date in US + * English regardless of the setting of the global locale. This functionality is + * needed to write things like MIME headers which must always be in US English. + * + **********************************************************************************/ + +PR_IMPLEMENT(PRUint32) +PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize, + const char* format, const PRExplodedTime* time ) +{ + char* bufPtr = buf; + const char* fmtPtr; + char tmpBuf[ 40 ]; + const int tmpBufSize = sizeof( tmpBuf ); + + + for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ ) + { + if( *fmtPtr != '%' ) + { + ADDCHAR( bufPtr, bufSize, *fmtPtr ); + } + else + { + switch( *(++fmtPtr) ) + { + case '%': + /* escaped '%' character */ + ADDCHAR( bufPtr, bufSize, '%' ); + break; + + case 'a': + /* abbreviated weekday name */ + ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] ); + break; + + case 'A': + /* full weekday name */ + ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] ); + break; + + case 'b': + /* abbreviated month name */ + ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] ); + break; + + case 'B': + /* full month name */ + ADDSTR(bufPtr, bufSize, months[ time->tm_month ] ); + break; + + case 'c': + /* Date and time. */ + PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'd': + /* day of month ( 01 - 31 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'H': + /* hour ( 00 - 23 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'I': + /* hour ( 01 - 12 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld", + (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'j': + /* day number of year ( 001 - 366 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'm': + /* month number ( 01 - 12 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'M': + /* minute ( 00 - 59 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'p': + /* locale's equivalent of either AM or PM */ + ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" ); + break; + + case 'S': + /* seconds ( 00 - 61 ), allows for leap seconds */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'U': + /* week number of year ( 00 - 53 ), Sunday is the first day of week 1 */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'w': + /* weekday number ( 0 - 6 ), Sunday = 0 */ + PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'W': + /* Week number of year ( 00 - 53 ), Monday is the first day of week 1 */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'x': + /* Date representation */ + PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'X': + /* Time representation. */ + PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'y': + /* year within century ( 00 - 99 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'Y': + /* year as ccyy ( for example 1986 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'Z': + /* Time zone name or no characters if no time zone exists. + * Since time zone name is supposed to be independant of locale, we + * defer to PR_FormatTime() for this option. + */ + PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + default: + /* Unknown format. Simply copy format into output buffer. */ + ADDCHAR( bufPtr, bufSize, '%' ); + ADDCHAR( bufPtr, bufSize, *fmtPtr ); + break; + + } + } + } + + ADDCHAR( bufPtr, bufSize, '\0' ); + return (PRUint32)(bufPtr - buf - 1); +} + + + +/*********************************************************************************** + * + * Description: + * Returns the week number of the year (0-53) for the given time. firstDayOfWeek + * is the day on which the week is considered to start (0=Sun, 1=Mon, ...). + * Week 1 starts the first time firstDayOfWeek occurs in the year. In other words, + * a partial week at the start of the year is considered week 0. + * + **********************************************************************************/ + +static unsigned int +pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek) +{ + int dayOfWeek; + int dayOfYear; + + /* Get the day of the year for the given time then adjust it to represent the + * first day of the week containing the given time. + */ + dayOfWeek = time->tm_wday - firstDayOfWeek; + if (dayOfWeek < 0) { + dayOfWeek += 7; + } + + dayOfYear = time->tm_yday - dayOfWeek; + + if( dayOfYear <= 0 ) + { + /* If dayOfYear is <= 0, it is in the first partial week of the year. */ + return 0; + } + + /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there + * are any days left over ( dayOfYear % 7 ). Because we are only counting to + * the first day of the week containing the given time, rather than to the + * actual day representing the given time, any days in week 0 will be "absorbed" + * as extra days in the given week. + */ + return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 ); + +} + diff --git a/nsprpub/pr/src/misc/prtpool.c b/nsprpub/pr/src/misc/prtpool.c new file mode 100644 index 0000000000..69f588ef6e --- /dev/null +++ b/nsprpub/pr/src/misc/prtpool.c @@ -0,0 +1,1236 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +/* + * Thread pools + * Thread pools create and manage threads to provide support for + * scheduling jobs onto one or more threads. + * + */ +#ifdef OPT_WINNT +#include <windows.h> +#endif + +/* + * worker thread + */ +typedef struct wthread { + PRCList links; + PRThread *thread; +} wthread; + +/* + * queue of timer jobs + */ +typedef struct timer_jobq { + PRCList list; + PRLock *lock; + PRCondVar *cv; + PRInt32 cnt; + PRCList wthreads; +} timer_jobq; + +/* + * queue of jobs + */ +typedef struct tp_jobq { + PRCList list; + PRInt32 cnt; + PRLock *lock; + PRCondVar *cv; + PRCList wthreads; +#ifdef OPT_WINNT + HANDLE nt_completion_port; +#endif +} tp_jobq; + +/* + * queue of IO jobs + */ +typedef struct io_jobq { + PRCList list; + PRPollDesc *pollfds; + PRInt32 npollfds; + PRJob **polljobs; + PRLock *lock; + PRInt32 cnt; + PRFileDesc *notify_fd; + PRCList wthreads; +} io_jobq; + +/* + * Threadpool + */ +struct PRThreadPool { + PRInt32 init_threads; + PRInt32 max_threads; + PRInt32 current_threads; + PRInt32 idle_threads; + PRUint32 stacksize; + tp_jobq jobq; + io_jobq ioq; + timer_jobq timerq; + PRLock *join_lock; /* used with jobp->join_cv */ + PRCondVar *shutdown_cv; + PRBool shutdown; +}; + +typedef enum io_op_type +{ JOB_IO_READ, JOB_IO_WRITE, JOB_IO_CONNECT, JOB_IO_ACCEPT } io_op_type; + +#ifdef OPT_WINNT +typedef struct NT_notifier { + OVERLAPPED overlapped; /* must be first */ + PRJob *jobp; +} NT_notifier; +#endif + +struct PRJob { + PRCList links; /* for linking jobs */ + PRBool on_ioq; /* job on ioq */ + PRBool on_timerq; /* job on timerq */ + PRJobFn job_func; + void *job_arg; + PRCondVar *join_cv; + PRBool join_wait; /* == PR_TRUE, when waiting to join */ + PRCondVar *cancel_cv; /* for cancelling IO jobs */ + PRBool cancel_io; /* for cancelling IO jobs */ + PRThreadPool *tpool; /* back pointer to thread pool */ + PRJobIoDesc *iod; + io_op_type io_op; + PRInt16 io_poll_flags; + PRNetAddr *netaddr; + PRIntervalTime timeout; /* relative value */ + PRIntervalTime absolute; +#ifdef OPT_WINNT + NT_notifier nt_notifier; +#endif +}; + +#define JOB_LINKS_PTR(_qp) \ + ((PRJob *) ((char *) (_qp) - offsetof(PRJob, links))) + +#define WTHREAD_LINKS_PTR(_qp) \ + ((wthread *) ((char *) (_qp) - offsetof(wthread, links))) + +#define JOINABLE_JOB(_jobp) (NULL != (_jobp)->join_cv) + +#define JOIN_NOTIFY(_jobp) \ + PR_BEGIN_MACRO \ + PR_Lock(_jobp->tpool->join_lock); \ + _jobp->join_wait = PR_FALSE; \ + PR_NotifyCondVar(_jobp->join_cv); \ + PR_Unlock(_jobp->tpool->join_lock); \ + PR_END_MACRO + +#define CANCEL_IO_JOB(jobp) \ + PR_BEGIN_MACRO \ + jobp->cancel_io = PR_FALSE; \ + jobp->on_ioq = PR_FALSE; \ + PR_REMOVE_AND_INIT_LINK(&jobp->links); \ + tp->ioq.cnt--; \ + PR_NotifyCondVar(jobp->cancel_cv); \ + PR_END_MACRO + +static void delete_job(PRJob *jobp); +static PRThreadPool * alloc_threadpool(void); +static PRJob * alloc_job(PRBool joinable, PRThreadPool *tp); +static void notify_ioq(PRThreadPool *tp); +static void notify_timerq(PRThreadPool *tp); + +/* + * locks are acquired in the following order + * + * tp->ioq.lock,tp->timerq.lock + * | + * V + * tp->jobq->lock + */ + +/* + * worker thread function + */ +static void wstart(void *arg) +{ + PRThreadPool *tp = (PRThreadPool *) arg; + PRCList *head; + + /* + * execute jobs until shutdown + */ + while (!tp->shutdown) { + PRJob *jobp; +#ifdef OPT_WINNT + BOOL rv; + DWORD unused, shutdown; + LPOVERLAPPED olp; + + PR_Lock(tp->jobq.lock); + tp->idle_threads++; + PR_Unlock(tp->jobq.lock); + rv = GetQueuedCompletionStatus(tp->jobq.nt_completion_port, + &unused, &shutdown, &olp, INFINITE); + + PR_ASSERT(rv); + if (shutdown) { + break; + } + jobp = ((NT_notifier *) olp)->jobp; + PR_Lock(tp->jobq.lock); + tp->idle_threads--; + tp->jobq.cnt--; + PR_Unlock(tp->jobq.lock); +#else + + PR_Lock(tp->jobq.lock); + while (PR_CLIST_IS_EMPTY(&tp->jobq.list) && (!tp->shutdown)) { + tp->idle_threads++; + PR_WaitCondVar(tp->jobq.cv, PR_INTERVAL_NO_TIMEOUT); + tp->idle_threads--; + } + if (tp->shutdown) { + PR_Unlock(tp->jobq.lock); + break; + } + head = PR_LIST_HEAD(&tp->jobq.list); + /* + * remove job from queue + */ + PR_REMOVE_AND_INIT_LINK(head); + tp->jobq.cnt--; + jobp = JOB_LINKS_PTR(head); + PR_Unlock(tp->jobq.lock); +#endif + + jobp->job_func(jobp->job_arg); + if (!JOINABLE_JOB(jobp)) { + delete_job(jobp); + } else { + JOIN_NOTIFY(jobp); + } + } + PR_Lock(tp->jobq.lock); + tp->current_threads--; + PR_Unlock(tp->jobq.lock); +} + +/* + * add a job to the work queue + */ +static void +add_to_jobq(PRThreadPool *tp, PRJob *jobp) +{ + /* + * add to jobq + */ +#ifdef OPT_WINNT + PR_Lock(tp->jobq.lock); + tp->jobq.cnt++; + PR_Unlock(tp->jobq.lock); + /* + * notify worker thread(s) + */ + PostQueuedCompletionStatus(tp->jobq.nt_completion_port, 0, + FALSE, &jobp->nt_notifier.overlapped); +#else + PR_Lock(tp->jobq.lock); + PR_APPEND_LINK(&jobp->links,&tp->jobq.list); + tp->jobq.cnt++; + if ((tp->idle_threads < tp->jobq.cnt) && + (tp->current_threads < tp->max_threads)) { + wthread *wthrp; + /* + * increment thread count and unlock the jobq lock + */ + tp->current_threads++; + PR_Unlock(tp->jobq.lock); + /* create new worker thread */ + wthrp = PR_NEWZAP(wthread); + if (wthrp) { + wthrp->thread = PR_CreateThread(PR_USER_THREAD, wstart, + tp, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,tp->stacksize); + if (NULL == wthrp->thread) { + PR_DELETE(wthrp); /* this sets wthrp to NULL */ + } + } + PR_Lock(tp->jobq.lock); + if (NULL == wthrp) { + tp->current_threads--; + } else { + PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads); + } + } + /* + * wakeup a worker thread + */ + PR_NotifyCondVar(tp->jobq.cv); + PR_Unlock(tp->jobq.lock); +#endif +} + +/* + * io worker thread function + */ +static void io_wstart(void *arg) +{ + PRThreadPool *tp = (PRThreadPool *) arg; + int pollfd_cnt, pollfds_used; + int rv; + PRCList *qp, *nextqp; + PRPollDesc *pollfds = NULL; + PRJob **polljobs = NULL; + int poll_timeout; + PRIntervalTime now; + + /* + * scan io_jobq + * construct poll list + * call PR_Poll + * for all fds, for which poll returns true, move the job to + * jobq and wakeup worker thread. + */ + while (!tp->shutdown) { + PRJob *jobp; + + pollfd_cnt = tp->ioq.cnt + 10; + if (pollfd_cnt > tp->ioq.npollfds) { + + /* + * re-allocate pollfd array if the current one is not large + * enough + */ + if (NULL != tp->ioq.pollfds) { + PR_Free(tp->ioq.pollfds); + } + tp->ioq.pollfds = (PRPollDesc *) PR_Malloc(pollfd_cnt * + (sizeof(PRPollDesc) + sizeof(PRJob *))); + PR_ASSERT(NULL != tp->ioq.pollfds); + /* + * array of pollfds + */ + pollfds = tp->ioq.pollfds; + tp->ioq.polljobs = (PRJob **) (&tp->ioq.pollfds[pollfd_cnt]); + /* + * parallel array of jobs + */ + polljobs = tp->ioq.polljobs; + tp->ioq.npollfds = pollfd_cnt; + } + + pollfds_used = 0; + /* + * add the notify fd; used for unblocking io thread(s) + */ + pollfds[pollfds_used].fd = tp->ioq.notify_fd; + pollfds[pollfds_used].in_flags = PR_POLL_READ; + pollfds[pollfds_used].out_flags = 0; + polljobs[pollfds_used] = NULL; + pollfds_used++; + /* + * fill in the pollfd array + */ + PR_Lock(tp->ioq.lock); + for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) { + nextqp = qp->next; + jobp = JOB_LINKS_PTR(qp); + if (jobp->cancel_io) { + CANCEL_IO_JOB(jobp); + continue; + } + if (pollfds_used == (pollfd_cnt)) { + break; + } + pollfds[pollfds_used].fd = jobp->iod->socket; + pollfds[pollfds_used].in_flags = jobp->io_poll_flags; + pollfds[pollfds_used].out_flags = 0; + polljobs[pollfds_used] = jobp; + + pollfds_used++; + } + if (!PR_CLIST_IS_EMPTY(&tp->ioq.list)) { + qp = tp->ioq.list.next; + jobp = JOB_LINKS_PTR(qp); + if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) { + poll_timeout = PR_INTERVAL_NO_TIMEOUT; + } + else if (PR_INTERVAL_NO_WAIT == jobp->timeout) { + poll_timeout = PR_INTERVAL_NO_WAIT; + } + else { + poll_timeout = jobp->absolute - PR_IntervalNow(); + if (poll_timeout <= 0) { /* already timed out */ + poll_timeout = PR_INTERVAL_NO_WAIT; + } + } + } else { + poll_timeout = PR_INTERVAL_NO_TIMEOUT; + } + PR_Unlock(tp->ioq.lock); + + /* + * XXXX + * should retry if more jobs have been added to the queue? + * + */ + PR_ASSERT(pollfds_used <= pollfd_cnt); + rv = PR_Poll(tp->ioq.pollfds, pollfds_used, poll_timeout); + + if (tp->shutdown) { + break; + } + + if (rv > 0) { + /* + * at least one io event is set + */ + PRStatus rval_status; + PRInt32 index; + + PR_ASSERT(pollfds[0].fd == tp->ioq.notify_fd); + /* + * reset the pollable event, if notified + */ + if (pollfds[0].out_flags & PR_POLL_READ) { + rval_status = PR_WaitForPollableEvent(tp->ioq.notify_fd); + PR_ASSERT(PR_SUCCESS == rval_status); + } + + for(index = 1; index < (pollfds_used); index++) { + PRInt16 events = pollfds[index].in_flags; + PRInt16 revents = pollfds[index].out_flags; + jobp = polljobs[index]; + + if ((revents & PR_POLL_NVAL) || /* busted in all cases */ + (revents & PR_POLL_ERR) || + ((events & PR_POLL_WRITE) && + (revents & PR_POLL_HUP))) { /* write op & hup */ + PR_Lock(tp->ioq.lock); + if (jobp->cancel_io) { + CANCEL_IO_JOB(jobp); + PR_Unlock(tp->ioq.lock); + continue; + } + PR_REMOVE_AND_INIT_LINK(&jobp->links); + tp->ioq.cnt--; + jobp->on_ioq = PR_FALSE; + PR_Unlock(tp->ioq.lock); + + /* set error */ + if (PR_POLL_NVAL & revents) { + jobp->iod->error = PR_BAD_DESCRIPTOR_ERROR; + } + else if (PR_POLL_HUP & revents) { + jobp->iod->error = PR_CONNECT_RESET_ERROR; + } + else { + jobp->iod->error = PR_IO_ERROR; + } + + /* + * add to jobq + */ + add_to_jobq(tp, jobp); + } else if (revents) { + /* + * add to jobq + */ + PR_Lock(tp->ioq.lock); + if (jobp->cancel_io) { + CANCEL_IO_JOB(jobp); + PR_Unlock(tp->ioq.lock); + continue; + } + PR_REMOVE_AND_INIT_LINK(&jobp->links); + tp->ioq.cnt--; + jobp->on_ioq = PR_FALSE; + PR_Unlock(tp->ioq.lock); + + if (jobp->io_op == JOB_IO_CONNECT) { + if (PR_GetConnectStatus(&pollfds[index]) == PR_SUCCESS) { + jobp->iod->error = 0; + } + else { + jobp->iod->error = PR_GetError(); + } + } else { + jobp->iod->error = 0; + } + + add_to_jobq(tp, jobp); + } + } + } + /* + * timeout processing + */ + now = PR_IntervalNow(); + PR_Lock(tp->ioq.lock); + for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) { + nextqp = qp->next; + jobp = JOB_LINKS_PTR(qp); + if (jobp->cancel_io) { + CANCEL_IO_JOB(jobp); + continue; + } + if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) { + break; + } + if ((PR_INTERVAL_NO_WAIT != jobp->timeout) && + ((PRInt32)(jobp->absolute - now) > 0)) { + break; + } + PR_REMOVE_AND_INIT_LINK(&jobp->links); + tp->ioq.cnt--; + jobp->on_ioq = PR_FALSE; + jobp->iod->error = PR_IO_TIMEOUT_ERROR; + add_to_jobq(tp, jobp); + } + PR_Unlock(tp->ioq.lock); + } +} + +/* + * timer worker thread function + */ +static void timer_wstart(void *arg) +{ + PRThreadPool *tp = (PRThreadPool *) arg; + PRCList *qp; + PRIntervalTime timeout; + PRIntervalTime now; + + /* + * call PR_WaitCondVar with minimum value of all timeouts + */ + while (!tp->shutdown) { + PRJob *jobp; + + PR_Lock(tp->timerq.lock); + if (PR_CLIST_IS_EMPTY(&tp->timerq.list)) { + timeout = PR_INTERVAL_NO_TIMEOUT; + } else { + PRCList *qp; + + qp = tp->timerq.list.next; + jobp = JOB_LINKS_PTR(qp); + + timeout = jobp->absolute - PR_IntervalNow(); + if (timeout <= 0) { + timeout = PR_INTERVAL_NO_WAIT; /* already timed out */ + } + } + if (PR_INTERVAL_NO_WAIT != timeout) { + PR_WaitCondVar(tp->timerq.cv, timeout); + } + if (tp->shutdown) { + PR_Unlock(tp->timerq.lock); + break; + } + /* + * move expired-timer jobs to jobq + */ + now = PR_IntervalNow(); + while (!PR_CLIST_IS_EMPTY(&tp->timerq.list)) { + qp = tp->timerq.list.next; + jobp = JOB_LINKS_PTR(qp); + + if ((PRInt32)(jobp->absolute - now) > 0) { + break; + } + /* + * job timed out + */ + PR_REMOVE_AND_INIT_LINK(&jobp->links); + tp->timerq.cnt--; + jobp->on_timerq = PR_FALSE; + add_to_jobq(tp, jobp); + } + PR_Unlock(tp->timerq.lock); + } +} + +static void +delete_threadpool(PRThreadPool *tp) +{ + if (NULL != tp) { + if (NULL != tp->shutdown_cv) { + PR_DestroyCondVar(tp->shutdown_cv); + } + if (NULL != tp->jobq.cv) { + PR_DestroyCondVar(tp->jobq.cv); + } + if (NULL != tp->jobq.lock) { + PR_DestroyLock(tp->jobq.lock); + } + if (NULL != tp->join_lock) { + PR_DestroyLock(tp->join_lock); + } +#ifdef OPT_WINNT + if (NULL != tp->jobq.nt_completion_port) { + CloseHandle(tp->jobq.nt_completion_port); + } +#endif + /* Timer queue */ + if (NULL != tp->timerq.cv) { + PR_DestroyCondVar(tp->timerq.cv); + } + if (NULL != tp->timerq.lock) { + PR_DestroyLock(tp->timerq.lock); + } + + if (NULL != tp->ioq.lock) { + PR_DestroyLock(tp->ioq.lock); + } + if (NULL != tp->ioq.pollfds) { + PR_Free(tp->ioq.pollfds); + } + if (NULL != tp->ioq.notify_fd) { + PR_DestroyPollableEvent(tp->ioq.notify_fd); + } + PR_Free(tp); + } + return; +} + +static PRThreadPool * +alloc_threadpool(void) +{ + PRThreadPool *tp; + + tp = (PRThreadPool *) PR_CALLOC(sizeof(*tp)); + if (NULL == tp) { + goto failed; + } + tp->jobq.lock = PR_NewLock(); + if (NULL == tp->jobq.lock) { + goto failed; + } + tp->jobq.cv = PR_NewCondVar(tp->jobq.lock); + if (NULL == tp->jobq.cv) { + goto failed; + } + tp->join_lock = PR_NewLock(); + if (NULL == tp->join_lock) { + goto failed; + } +#ifdef OPT_WINNT + tp->jobq.nt_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, + NULL, 0, 0); + if (NULL == tp->jobq.nt_completion_port) { + goto failed; + } +#endif + + tp->ioq.lock = PR_NewLock(); + if (NULL == tp->ioq.lock) { + goto failed; + } + + /* Timer queue */ + + tp->timerq.lock = PR_NewLock(); + if (NULL == tp->timerq.lock) { + goto failed; + } + tp->timerq.cv = PR_NewCondVar(tp->timerq.lock); + if (NULL == tp->timerq.cv) { + goto failed; + } + + tp->shutdown_cv = PR_NewCondVar(tp->jobq.lock); + if (NULL == tp->shutdown_cv) { + goto failed; + } + tp->ioq.notify_fd = PR_NewPollableEvent(); + if (NULL == tp->ioq.notify_fd) { + goto failed; + } + return tp; +failed: + delete_threadpool(tp); + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; +} + +/* Create thread pool */ +PR_IMPLEMENT(PRThreadPool *) +PR_CreateThreadPool(PRInt32 initial_threads, PRInt32 max_threads, + PRUint32 stacksize) +{ + PRThreadPool *tp; + PRThread *thr; + int i; + wthread *wthrp; + + tp = alloc_threadpool(); + if (NULL == tp) { + return NULL; + } + + tp->init_threads = initial_threads; + tp->max_threads = max_threads; + tp->stacksize = stacksize; + PR_INIT_CLIST(&tp->jobq.list); + PR_INIT_CLIST(&tp->ioq.list); + PR_INIT_CLIST(&tp->timerq.list); + PR_INIT_CLIST(&tp->jobq.wthreads); + PR_INIT_CLIST(&tp->ioq.wthreads); + PR_INIT_CLIST(&tp->timerq.wthreads); + tp->shutdown = PR_FALSE; + + PR_Lock(tp->jobq.lock); + for(i=0; i < initial_threads; ++i) { + + thr = PR_CreateThread(PR_USER_THREAD, wstart, + tp, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,stacksize); + PR_ASSERT(thr); + wthrp = PR_NEWZAP(wthread); + PR_ASSERT(wthrp); + wthrp->thread = thr; + PR_APPEND_LINK(&wthrp->links, &tp->jobq.wthreads); + } + tp->current_threads = initial_threads; + + thr = PR_CreateThread(PR_USER_THREAD, io_wstart, + tp, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize); + PR_ASSERT(thr); + wthrp = PR_NEWZAP(wthread); + PR_ASSERT(wthrp); + wthrp->thread = thr; + PR_APPEND_LINK(&wthrp->links, &tp->ioq.wthreads); + + thr = PR_CreateThread(PR_USER_THREAD, timer_wstart, + tp, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD,PR_JOINABLE_THREAD,stacksize); + PR_ASSERT(thr); + wthrp = PR_NEWZAP(wthread); + PR_ASSERT(wthrp); + wthrp->thread = thr; + PR_APPEND_LINK(&wthrp->links, &tp->timerq.wthreads); + + PR_Unlock(tp->jobq.lock); + return tp; +} + +static void +delete_job(PRJob *jobp) +{ + if (NULL != jobp) { + if (NULL != jobp->join_cv) { + PR_DestroyCondVar(jobp->join_cv); + jobp->join_cv = NULL; + } + if (NULL != jobp->cancel_cv) { + PR_DestroyCondVar(jobp->cancel_cv); + jobp->cancel_cv = NULL; + } + PR_DELETE(jobp); + } +} + +static PRJob * +alloc_job(PRBool joinable, PRThreadPool *tp) +{ + PRJob *jobp; + + jobp = PR_NEWZAP(PRJob); + if (NULL == jobp) { + goto failed; + } + if (joinable) { + jobp->join_cv = PR_NewCondVar(tp->join_lock); + jobp->join_wait = PR_TRUE; + if (NULL == jobp->join_cv) { + goto failed; + } + } else { + jobp->join_cv = NULL; + } +#ifdef OPT_WINNT + jobp->nt_notifier.jobp = jobp; +#endif + return jobp; +failed: + delete_job(jobp); + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; +} + +/* queue a job */ +PR_IMPLEMENT(PRJob *) +PR_QueueJob(PRThreadPool *tpool, PRJobFn fn, void *arg, PRBool joinable) +{ + PRJob *jobp; + + jobp = alloc_job(joinable, tpool); + if (NULL == jobp) { + return NULL; + } + + jobp->job_func = fn; + jobp->job_arg = arg; + jobp->tpool = tpool; + + add_to_jobq(tpool, jobp); + return jobp; +} + +/* queue a job, when a socket is readable or writeable */ +static PRJob * +queue_io_job(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg, + PRBool joinable, io_op_type op) +{ + PRJob *jobp; + PRIntervalTime now; + + jobp = alloc_job(joinable, tpool); + if (NULL == jobp) { + return NULL; + } + + /* + * Add a new job to io_jobq + * wakeup io worker thread + */ + + jobp->job_func = fn; + jobp->job_arg = arg; + jobp->tpool = tpool; + jobp->iod = iod; + if (JOB_IO_READ == op) { + jobp->io_op = JOB_IO_READ; + jobp->io_poll_flags = PR_POLL_READ; + } else if (JOB_IO_WRITE == op) { + jobp->io_op = JOB_IO_WRITE; + jobp->io_poll_flags = PR_POLL_WRITE; + } else if (JOB_IO_ACCEPT == op) { + jobp->io_op = JOB_IO_ACCEPT; + jobp->io_poll_flags = PR_POLL_READ; + } else if (JOB_IO_CONNECT == op) { + jobp->io_op = JOB_IO_CONNECT; + jobp->io_poll_flags = PR_POLL_WRITE|PR_POLL_EXCEPT; + } else { + delete_job(jobp); + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + + jobp->timeout = iod->timeout; + if ((PR_INTERVAL_NO_TIMEOUT == iod->timeout) || + (PR_INTERVAL_NO_WAIT == iod->timeout)) { + jobp->absolute = iod->timeout; + } else { + now = PR_IntervalNow(); + jobp->absolute = now + iod->timeout; + } + + + PR_Lock(tpool->ioq.lock); + + if (PR_CLIST_IS_EMPTY(&tpool->ioq.list) || + (PR_INTERVAL_NO_TIMEOUT == iod->timeout)) { + PR_APPEND_LINK(&jobp->links,&tpool->ioq.list); + } else if (PR_INTERVAL_NO_WAIT == iod->timeout) { + PR_INSERT_LINK(&jobp->links,&tpool->ioq.list); + } else { + PRCList *qp; + PRJob *tmp_jobp; + /* + * insert into the timeout-sorted ioq + */ + for (qp = tpool->ioq.list.prev; qp != &tpool->ioq.list; + qp = qp->prev) { + tmp_jobp = JOB_LINKS_PTR(qp); + if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) { + break; + } + } + PR_INSERT_AFTER(&jobp->links,qp); + } + + jobp->on_ioq = PR_TRUE; + tpool->ioq.cnt++; + /* + * notify io worker thread(s) + */ + PR_Unlock(tpool->ioq.lock); + notify_ioq(tpool); + return jobp; +} + +/* queue a job, when a socket is readable */ +PR_IMPLEMENT(PRJob *) +PR_QueueJob_Read(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, void * arg, + PRBool joinable) +{ + return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_READ)); +} + +/* queue a job, when a socket is writeable */ +PR_IMPLEMENT(PRJob *) +PR_QueueJob_Write(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn,void * arg, + PRBool joinable) +{ + return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_WRITE)); +} + + +/* queue a job, when a socket has a pending connection */ +PR_IMPLEMENT(PRJob *) +PR_QueueJob_Accept(PRThreadPool *tpool, PRJobIoDesc *iod, PRJobFn fn, + void * arg, PRBool joinable) +{ + return (queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_ACCEPT)); +} + +/* queue a job, when a socket can be connected */ +PR_IMPLEMENT(PRJob *) +PR_QueueJob_Connect(PRThreadPool *tpool, PRJobIoDesc *iod, + const PRNetAddr *addr, PRJobFn fn, void * arg, PRBool joinable) +{ + PRStatus rv; + PRErrorCode err; + + rv = PR_Connect(iod->socket, addr, PR_INTERVAL_NO_WAIT); + if ((rv == PR_FAILURE) && ((err = PR_GetError()) == PR_IN_PROGRESS_ERROR)) { + /* connection pending */ + return(queue_io_job(tpool, iod, fn, arg, joinable, JOB_IO_CONNECT)); + } + /* + * connection succeeded or failed; add to jobq right away + */ + if (rv == PR_FAILURE) { + iod->error = err; + } + else { + iod->error = 0; + } + return(PR_QueueJob(tpool, fn, arg, joinable)); + +} + +/* queue a job, when a timer expires */ +PR_IMPLEMENT(PRJob *) +PR_QueueJob_Timer(PRThreadPool *tpool, PRIntervalTime timeout, + PRJobFn fn, void * arg, PRBool joinable) +{ + PRIntervalTime now; + PRJob *jobp; + + if (PR_INTERVAL_NO_TIMEOUT == timeout) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + if (PR_INTERVAL_NO_WAIT == timeout) { + /* + * no waiting; add to jobq right away + */ + return(PR_QueueJob(tpool, fn, arg, joinable)); + } + jobp = alloc_job(joinable, tpool); + if (NULL == jobp) { + return NULL; + } + + /* + * Add a new job to timer_jobq + * wakeup timer worker thread + */ + + jobp->job_func = fn; + jobp->job_arg = arg; + jobp->tpool = tpool; + jobp->timeout = timeout; + + now = PR_IntervalNow(); + jobp->absolute = now + timeout; + + + PR_Lock(tpool->timerq.lock); + jobp->on_timerq = PR_TRUE; + if (PR_CLIST_IS_EMPTY(&tpool->timerq.list)) { + PR_APPEND_LINK(&jobp->links,&tpool->timerq.list); + } + else { + PRCList *qp; + PRJob *tmp_jobp; + /* + * insert into the sorted timer jobq + */ + for (qp = tpool->timerq.list.prev; qp != &tpool->timerq.list; + qp = qp->prev) { + tmp_jobp = JOB_LINKS_PTR(qp); + if ((PRInt32)(jobp->absolute - tmp_jobp->absolute) >= 0) { + break; + } + } + PR_INSERT_AFTER(&jobp->links,qp); + } + tpool->timerq.cnt++; + /* + * notify timer worker thread(s) + */ + notify_timerq(tpool); + PR_Unlock(tpool->timerq.lock); + return jobp; +} + +static void +notify_timerq(PRThreadPool *tp) +{ + /* + * wakeup the timer thread(s) + */ + PR_NotifyCondVar(tp->timerq.cv); +} + +static void +notify_ioq(PRThreadPool *tp) +{ + PRStatus rval_status; + + /* + * wakeup the io thread(s) + */ + rval_status = PR_SetPollableEvent(tp->ioq.notify_fd); + PR_ASSERT(PR_SUCCESS == rval_status); +} + +/* + * cancel a job + * + * XXXX: is this needed? likely to be removed + */ +PR_IMPLEMENT(PRStatus) +PR_CancelJob(PRJob *jobp) { + + PRStatus rval = PR_FAILURE; + PRThreadPool *tp; + + if (jobp->on_timerq) { + /* + * now, check again while holding the timerq lock + */ + tp = jobp->tpool; + PR_Lock(tp->timerq.lock); + if (jobp->on_timerq) { + jobp->on_timerq = PR_FALSE; + PR_REMOVE_AND_INIT_LINK(&jobp->links); + tp->timerq.cnt--; + PR_Unlock(tp->timerq.lock); + if (!JOINABLE_JOB(jobp)) { + delete_job(jobp); + } else { + JOIN_NOTIFY(jobp); + } + rval = PR_SUCCESS; + } else { + PR_Unlock(tp->timerq.lock); + } + } else if (jobp->on_ioq) { + /* + * now, check again while holding the ioq lock + */ + tp = jobp->tpool; + PR_Lock(tp->ioq.lock); + if (jobp->on_ioq) { + jobp->cancel_cv = PR_NewCondVar(tp->ioq.lock); + if (NULL == jobp->cancel_cv) { + PR_Unlock(tp->ioq.lock); + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return PR_FAILURE; + } + /* + * mark job 'cancelled' and notify io thread(s) + * XXXX: + * this assumes there is only one io thread; when there + * are multiple threads, the io thread processing this job + * must be notified. + */ + jobp->cancel_io = PR_TRUE; + PR_Unlock(tp->ioq.lock); /* release, reacquire ioq lock */ + notify_ioq(tp); + PR_Lock(tp->ioq.lock); + while (jobp->cancel_io) { + PR_WaitCondVar(jobp->cancel_cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(tp->ioq.lock); + PR_ASSERT(!jobp->on_ioq); + if (!JOINABLE_JOB(jobp)) { + delete_job(jobp); + } else { + JOIN_NOTIFY(jobp); + } + rval = PR_SUCCESS; + } else { + PR_Unlock(tp->ioq.lock); + } + } + if (PR_FAILURE == rval) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + } + return rval; +} + +/* join a job, wait until completion */ +PR_IMPLEMENT(PRStatus) +PR_JoinJob(PRJob *jobp) +{ + if (!JOINABLE_JOB(jobp)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + PR_Lock(jobp->tpool->join_lock); + while(jobp->join_wait) { + PR_WaitCondVar(jobp->join_cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(jobp->tpool->join_lock); + delete_job(jobp); + return PR_SUCCESS; +} + +/* shutdown threadpool */ +PR_IMPLEMENT(PRStatus) +PR_ShutdownThreadPool(PRThreadPool *tpool) +{ + PRStatus rval = PR_SUCCESS; + + PR_Lock(tpool->jobq.lock); + tpool->shutdown = PR_TRUE; + PR_NotifyAllCondVar(tpool->shutdown_cv); + PR_Unlock(tpool->jobq.lock); + + return rval; +} + +/* + * join thread pool + * wait for termination of worker threads + * reclaim threadpool resources + */ +PR_IMPLEMENT(PRStatus) +PR_JoinThreadPool(PRThreadPool *tpool) +{ + PRStatus rval = PR_SUCCESS; + PRCList *head; + PRStatus rval_status; + + PR_Lock(tpool->jobq.lock); + while (!tpool->shutdown) { + PR_WaitCondVar(tpool->shutdown_cv, PR_INTERVAL_NO_TIMEOUT); + } + + /* + * wakeup worker threads + */ +#ifdef OPT_WINNT + /* + * post shutdown notification for all threads + */ + { + int i; + for(i=0; i < tpool->current_threads; i++) { + PostQueuedCompletionStatus(tpool->jobq.nt_completion_port, 0, + TRUE, NULL); + } + } +#else + PR_NotifyAllCondVar(tpool->jobq.cv); +#endif + + /* + * wakeup io thread(s) + */ + notify_ioq(tpool); + + /* + * wakeup timer thread(s) + */ + PR_Lock(tpool->timerq.lock); + notify_timerq(tpool); + PR_Unlock(tpool->timerq.lock); + + while (!PR_CLIST_IS_EMPTY(&tpool->jobq.wthreads)) { + wthread *wthrp; + + head = PR_LIST_HEAD(&tpool->jobq.wthreads); + PR_REMOVE_AND_INIT_LINK(head); + PR_Unlock(tpool->jobq.lock); + wthrp = WTHREAD_LINKS_PTR(head); + rval_status = PR_JoinThread(wthrp->thread); + PR_ASSERT(PR_SUCCESS == rval_status); + PR_DELETE(wthrp); + PR_Lock(tpool->jobq.lock); + } + PR_Unlock(tpool->jobq.lock); + while (!PR_CLIST_IS_EMPTY(&tpool->ioq.wthreads)) { + wthread *wthrp; + + head = PR_LIST_HEAD(&tpool->ioq.wthreads); + PR_REMOVE_AND_INIT_LINK(head); + wthrp = WTHREAD_LINKS_PTR(head); + rval_status = PR_JoinThread(wthrp->thread); + PR_ASSERT(PR_SUCCESS == rval_status); + PR_DELETE(wthrp); + } + + while (!PR_CLIST_IS_EMPTY(&tpool->timerq.wthreads)) { + wthread *wthrp; + + head = PR_LIST_HEAD(&tpool->timerq.wthreads); + PR_REMOVE_AND_INIT_LINK(head); + wthrp = WTHREAD_LINKS_PTR(head); + rval_status = PR_JoinThread(wthrp->thread); + PR_ASSERT(PR_SUCCESS == rval_status); + PR_DELETE(wthrp); + } + + /* + * Delete queued jobs + */ + while (!PR_CLIST_IS_EMPTY(&tpool->jobq.list)) { + PRJob *jobp; + + head = PR_LIST_HEAD(&tpool->jobq.list); + PR_REMOVE_AND_INIT_LINK(head); + jobp = JOB_LINKS_PTR(head); + tpool->jobq.cnt--; + delete_job(jobp); + } + + /* delete io jobs */ + while (!PR_CLIST_IS_EMPTY(&tpool->ioq.list)) { + PRJob *jobp; + + head = PR_LIST_HEAD(&tpool->ioq.list); + PR_REMOVE_AND_INIT_LINK(head); + tpool->ioq.cnt--; + jobp = JOB_LINKS_PTR(head); + delete_job(jobp); + } + + /* delete timer jobs */ + while (!PR_CLIST_IS_EMPTY(&tpool->timerq.list)) { + PRJob *jobp; + + head = PR_LIST_HEAD(&tpool->timerq.list); + PR_REMOVE_AND_INIT_LINK(head); + tpool->timerq.cnt--; + jobp = JOB_LINKS_PTR(head); + delete_job(jobp); + } + + PR_ASSERT(0 == tpool->jobq.cnt); + PR_ASSERT(0 == tpool->ioq.cnt); + PR_ASSERT(0 == tpool->timerq.cnt); + + delete_threadpool(tpool); + return rval; +} diff --git a/nsprpub/pr/src/misc/prtrace.c b/nsprpub/pr/src/misc/prtrace.c new file mode 100644 index 0000000000..ef6b65104e --- /dev/null +++ b/nsprpub/pr/src/misc/prtrace.c @@ -0,0 +1,901 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** prtrace.c -- NSPR Trace Instrumentation +** +** Implement the API defined in prtrace.h +** +** +** +*/ + +#include <string.h> +#include "primpl.h" + + +#define DEFAULT_TRACE_BUFSIZE ( 1024 * 1024 ) +#define DEFAULT_BUFFER_SEGMENTS 2 + +/* +** Enumerate states in a RName structure +*/ +typedef enum TraceState +{ + Running = 1, + Suspended = 2 +} TraceState; + +/* +** Define QName structure +*/ +typedef struct QName +{ + PRCList link; + PRCList rNameList; + char name[PRTRACE_NAME_MAX+1]; +} QName; + +/* +** Define RName structure +*/ +typedef struct RName +{ + PRCList link; + PRLock *lock; + QName *qName; + TraceState state; + char name[PRTRACE_NAME_MAX+1]; + char desc[PRTRACE_DESC_MAX+1]; +} RName; + + +/* +** The Trace Facility database +** +*/ +static PRLogModuleInfo *lm; + +static PRLock *traceLock; /* Facility Lock */ +static PRCList qNameList; /* anchor to all QName structures */ +static TraceState traceState = Running; + +/* +** in-memory trace buffer controls +*/ +static PRTraceEntry *tBuf; /* pointer to buffer */ +static PRInt32 bufSize; /* size of buffer, in bytes, rounded up to sizeof(PRTraceEntry) */ +static volatile PRInt32 next; /* index to next PRTraceEntry */ +static PRInt32 last; /* index of highest numbered trace entry */ + +/* +** Real-time buffer capture controls +*/ +static PRInt32 fetchLastSeen = 0; +static PRBool fetchLostData = PR_FALSE; + +/* +** Buffer write-to-file controls +*/ +static PRLock *logLock; /* Sync lock */ +static PRCondVar *logCVar; /* Sync Condidtion Variable */ +/* +** Inter-thread state communication. +** Controling thread writes to logOrder under protection of logCVar +** the logging thread reads logOrder and sets logState on Notify. +** +** logSegments, logCount, logLostData must be read and written under +** protection of logLock, logCVar. +** +*/ +static enum LogState +{ + LogNotRunning, /* Initial state */ + LogReset, /* Causes logger to re-calc controls */ + LogActive, /* Logging in progress, set only by log thread */ + LogSuspend, /* Suspend Logging */ + LogResume, /* Resume Logging => LogActive */ + LogStop /* Stop the log thread */ +} logOrder, logState, localState; /* controlling state variables */ +static PRInt32 logSegments; /* Number of buffer segments */ +static PRInt32 logEntries; /* number of Trace Entries in the buffer */ +static PRInt32 logEntriesPerSegment; /* number of PRTraceEntries per buffer segment */ +static PRInt32 logSegSize; /* size of buffer segment */ +static PRInt32 logCount; /* number of segments pending output */ +static PRInt32 logLostData; /* number of lost log buffer segments */ + +/* +** end Trace Database +** +*/ + +/* +** _PR_InitializeTrace() -- Initialize the trace facility +*/ +static void NewTraceBuffer( PRInt32 size ) +{ + /* + ** calculate the size of the buffer + ** round down so that each segment has the same number of + ** trace entries + */ + logSegments = DEFAULT_BUFFER_SEGMENTS; + logEntries = size / sizeof(PRTraceEntry); + logEntriesPerSegment = logEntries / logSegments; + logEntries = logSegments * logEntriesPerSegment; + bufSize = logEntries * sizeof(PRTraceEntry); + logSegSize = logEntriesPerSegment * sizeof(PRTraceEntry); + PR_ASSERT( bufSize != 0); + PR_LOG( lm, PR_LOG_ERROR, + ("NewTraceBuffer: logSegments: %ld, logEntries: %ld, logEntriesPerSegment: %ld, logSegSize: %ld", + logSegments, logEntries, logEntriesPerSegment, logSegSize )); + + + tBuf = PR_Malloc( bufSize ); + if ( tBuf == NULL ) + { + PR_LOG( lm, PR_LOG_ERROR, + ("PRTrace: Failed to get trace buffer")); + PR_ASSERT( 0 ); + } + else + { + PR_LOG( lm, PR_LOG_NOTICE, + ("PRTrace: Got trace buffer of size: %ld, at %p", bufSize, tBuf)); + } + + next = 0; + last = logEntries -1; + logCount = 0; + logLostData = PR_TRUE; /* not really on first call */ + logOrder = LogReset; + +} /* end NewTraceBuffer() */ + +/* +** _PR_InitializeTrace() -- Initialize the trace facility +*/ +static void _PR_InitializeTrace( void ) +{ + /* The lock pointer better be null on this call */ + PR_ASSERT( traceLock == NULL ); + + traceLock = PR_NewLock(); + PR_ASSERT( traceLock != NULL ); + + PR_Lock( traceLock ); + + PR_INIT_CLIST( &qNameList ); + + lm = PR_NewLogModule("trace"); + + bufSize = DEFAULT_TRACE_BUFSIZE; + NewTraceBuffer( bufSize ); + + /* Initialize logging controls */ + logLock = PR_NewLock(); + logCVar = PR_NewCondVar( logLock ); + + PR_Unlock( traceLock ); + return; +} /* end _PR_InitializeTrace() */ + +/* +** Create a Trace Handle +*/ +PR_IMPLEMENT(PRTraceHandle) +PR_CreateTrace( + const char *qName, /* QName for this trace handle */ + const char *rName, /* RName for this trace handle */ + const char *description /* description for this trace handle */ +) +{ + QName *qnp; + RName *rnp; + PRBool matchQname = PR_FALSE; + + /* Self initialize, if necessary */ + if ( traceLock == NULL ) { + _PR_InitializeTrace(); + } + + /* Validate input arguments */ + PR_ASSERT( strlen(qName) <= PRTRACE_NAME_MAX ); + PR_ASSERT( strlen(rName) <= PRTRACE_NAME_MAX ); + PR_ASSERT( strlen(description) <= PRTRACE_DESC_MAX ); + + PR_LOG( lm, PR_LOG_DEBUG, + ("PRTRACE: CreateTrace: Qname: %s, RName: %s", qName, rName)); + + /* Lock the Facility */ + PR_Lock( traceLock ); + + /* Do we already have a matching QName? */ + if (!PR_CLIST_IS_EMPTY( &qNameList )) + { + qnp = (QName *) PR_LIST_HEAD( &qNameList ); + do { + if ( strcmp(qnp->name, qName) == 0) + { + matchQname = PR_TRUE; + break; + } + qnp = (QName *)PR_NEXT_LINK( &qnp->link ); + } while( qnp != (QName *)&qNameList ); + } + /* + ** If we did not find a matching QName, + ** allocate one and initialize it. + ** link it onto the qNameList. + ** + */ + if ( matchQname != PR_TRUE ) + { + qnp = PR_NEWZAP( QName ); + PR_ASSERT( qnp != NULL ); + PR_INIT_CLIST( &qnp->link ); + PR_INIT_CLIST( &qnp->rNameList ); + strcpy( qnp->name, qName ); + PR_APPEND_LINK( &qnp->link, &qNameList ); + } + + /* Do we already have a matching RName? */ + if (!PR_CLIST_IS_EMPTY( &qnp->rNameList )) + { + rnp = (RName *) PR_LIST_HEAD( &qnp->rNameList ); + do { + /* + ** No duplicate RNames are allowed within a QName + ** + */ + PR_ASSERT( strcmp(rnp->name, rName)); + rnp = (RName *)PR_NEXT_LINK( &rnp->link ); + } while( rnp != (RName *)&qnp->rNameList ); + } + + /* Get a new RName structure; initialize its members */ + rnp = PR_NEWZAP( RName ); + PR_ASSERT( rnp != NULL ); + PR_INIT_CLIST( &rnp->link ); + strcpy( rnp->name, rName ); + strcpy( rnp->desc, description ); + rnp->lock = PR_NewLock(); + rnp->state = Running; + if ( rnp->lock == NULL ) + { + PR_ASSERT(0); + } + + PR_APPEND_LINK( &rnp->link, &qnp->rNameList ); /* add RName to QName's rnList */ + rnp->qName = qnp; /* point the RName to the QName */ + + /* Unlock the Facility */ + PR_Unlock( traceLock ); + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Create: QName: %s %p, RName: %s %p\n\t", + qName, qnp, rName, rnp )); + + return((PRTraceHandle)rnp); +} /* end PR_CreateTrace() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_DestroyTrace( + PRTraceHandle handle /* Handle to be destroyed */ +) +{ + RName *rnp = (RName *)handle; + QName *qnp = rnp->qName; + + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting: QName: %s, RName: %s", + qnp->name, rnp->name)); + + /* Lock the Facility */ + PR_Lock( traceLock ); + + /* + ** Remove RName from the list of RNames in QName + ** and free RName + */ + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting RName: %s, %p", + rnp->name, rnp)); + PR_REMOVE_LINK( &rnp->link ); + PR_Free( rnp->lock ); + PR_DELETE( rnp ); + + /* + ** If this is the last RName within QName + ** remove QName from the qNameList and free it + */ + if ( PR_CLIST_IS_EMPTY( &qnp->rNameList ) ) + { + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: Deleting unused QName: %s, %p", + qnp->name, qnp)); + PR_REMOVE_LINK( &qnp->link ); + PR_DELETE( qnp ); + } + + /* Unlock the Facility */ + PR_Unlock( traceLock ); + return; +} /* end PR_DestroyTrace() */ + +/* +** Create a TraceEntry in the trace buffer +*/ +PR_IMPLEMENT(void) +PR_Trace( + PRTraceHandle handle, /* use this trace handle */ + PRUint32 userData0, /* User supplied data word 0 */ + PRUint32 userData1, /* User supplied data word 1 */ + PRUint32 userData2, /* User supplied data word 2 */ + PRUint32 userData3, /* User supplied data word 3 */ + PRUint32 userData4, /* User supplied data word 4 */ + PRUint32 userData5, /* User supplied data word 5 */ + PRUint32 userData6, /* User supplied data word 6 */ + PRUint32 userData7 /* User supplied data word 7 */ +) +{ + PRTraceEntry *tep; + PRInt32 mark; + + if ( (traceState == Suspended ) + || ( ((RName *)handle)->state == Suspended )) { + return; + } + + /* + ** Get the next trace entry slot w/ minimum delay + */ + PR_Lock( traceLock ); + + tep = &tBuf[next++]; + if ( next > last ) { + next = 0; + } + if ( fetchLostData == PR_FALSE && next == fetchLastSeen ) { + fetchLostData = PR_TRUE; + } + + mark = next; + + PR_Unlock( traceLock ); + + /* + ** We have a trace entry. Fill it in. + */ + tep->thread = PR_GetCurrentThread(); + tep->handle = handle; + tep->time = PR_Now(); + tep->userData[0] = userData0; + tep->userData[1] = userData1; + tep->userData[2] = userData2; + tep->userData[3] = userData3; + tep->userData[4] = userData4; + tep->userData[5] = userData5; + tep->userData[6] = userData6; + tep->userData[7] = userData7; + + /* When buffer segment is full, signal trace log thread to run */ + if (( mark % logEntriesPerSegment) == 0 ) + { + PR_Lock( logLock ); + logCount++; + PR_NotifyCondVar( logCVar ); + PR_Unlock( logLock ); + /* + ** Gh0D! This is awful! + ** Anyway, to minimize lost trace data segments, + ** I inserted the PR_Sleep(0) to cause a context switch + ** so that the log thread could run. + ** I know, it perturbs the universe and may cause + ** funny things to happen in the optimized builds. + ** Take it out, lose data; leave it in risk Heisenberg. + */ + /* PR_Sleep(0); */ + } + + return; +} /* end PR_Trace() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_SetTraceOption( + PRTraceOption command, /* One of the enumerated values */ + void *value /* command value or NULL */ +) +{ + RName * rnp; + + switch ( command ) + { + case PRTraceBufSize : + PR_Lock( traceLock ); + PR_Free( tBuf ); + bufSize = *(PRInt32 *)value; + NewTraceBuffer( bufSize ); + PR_Unlock( traceLock ); + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceBufSize: %ld", bufSize)); + break; + + case PRTraceEnable : + rnp = *(RName **)value; + rnp->state = Running; + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceEnable: %p", rnp)); + break; + + case PRTraceDisable : + rnp = *(RName **)value; + rnp->state = Suspended; + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceDisable: %p", rnp)); + break; + + case PRTraceSuspend : + traceState = Suspended; + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceSuspend")); + break; + + case PRTraceResume : + traceState = Running; + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceResume")); + break; + + case PRTraceSuspendRecording : + PR_Lock( logLock ); + logOrder = LogSuspend; + PR_NotifyCondVar( logCVar ); + PR_Unlock( logLock ); + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceSuspendRecording")); + break; + + case PRTraceResumeRecording : + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceResumeRecording")); + if ( logState != LogSuspend ) { + break; + } + PR_Lock( logLock ); + logOrder = LogResume; + PR_NotifyCondVar( logCVar ); + PR_Unlock( logLock ); + break; + + case PRTraceStopRecording : + PR_Lock( logLock ); + logOrder = LogStop; + PR_NotifyCondVar( logCVar ); + PR_Unlock( logLock ); + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceStopRecording")); + break; + + case PRTraceLockHandles : + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceLockTraceHandles")); + PR_Lock( traceLock ); + break; + + case PRTraceUnLockHandles : + PR_LOG( lm, PR_LOG_DEBUG, + ("PRSetTraceOption: PRTraceUnLockHandles")); + PR_Unlock( traceLock ); + break; + + default: + PR_LOG( lm, PR_LOG_ERROR, + ("PRSetTraceOption: Invalid command %ld", command )); + PR_ASSERT( 0 ); + break; + } /* end switch() */ + return; +} /* end PR_SetTraceOption() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_GetTraceOption( + PRTraceOption command, /* One of the enumerated values */ + void *value /* command value or NULL */ +) +{ + switch ( command ) + { + case PRTraceBufSize : + *((PRInt32 *)value) = bufSize; + PR_LOG( lm, PR_LOG_DEBUG, + ("PRGetTraceOption: PRTraceBufSize: %ld", bufSize )); + break; + + default: + PR_LOG( lm, PR_LOG_ERROR, + ("PRGetTraceOption: Invalid command %ld", command )); + PR_ASSERT( 0 ); + break; + } /* end switch() */ + return; +} /* end PR_GetTraceOption() */ + +/* +** +*/ +PR_IMPLEMENT(PRTraceHandle) +PR_GetTraceHandleFromName( + const char *qName, /* QName search argument */ + const char *rName /* RName search argument */ +) +{ + const char *qn, *rn, *desc; + PRTraceHandle qh, rh = NULL; + RName *rnp = NULL; + + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetTraceHandleFromName:\n\t" + "QName: %s, RName: %s", qName, rName )); + + qh = PR_FindNextTraceQname( NULL ); + while (qh != NULL) + { + rh = PR_FindNextTraceRname( NULL, qh ); + while ( rh != NULL ) + { + PR_GetTraceNameFromHandle( rh, &qn, &rn, &desc ); + if ( (strcmp( qName, qn ) == 0) + && (strcmp( rName, rn ) == 0 )) + { + rnp = (RName *)rh; + goto foundIt; + } + rh = PR_FindNextTraceRname( rh, qh ); + } + qh = PR_FindNextTraceQname( NULL ); + } + +foundIt: + PR_LOG( lm, PR_LOG_DEBUG, ("PR_Counter: GetConterHandleFromName: %p", rnp )); + return(rh); +} /* end PR_GetTraceHandleFromName() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_GetTraceNameFromHandle( + PRTraceHandle handle, /* handle as search argument */ + const char **qName, /* pointer to associated QName */ + const char **rName, /* pointer to associated RName */ + const char **description /* pointer to associated description */ +) +{ + RName *rnp = (RName *)handle; + QName *qnp = rnp->qName; + + *qName = qnp->name; + *rName = rnp->name; + *description = rnp->desc; + + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: GetConterNameFromHandle: " + "QNp: %p, RNp: %p,\n\tQName: %s, RName: %s, Desc: %s", + qnp, rnp, qnp->name, rnp->name, rnp->desc )); + + return; +} /* end PR_GetTraceNameFromHandle() */ + +/* +** +*/ +PR_IMPLEMENT(PRTraceHandle) +PR_FindNextTraceQname( + PRTraceHandle handle +) +{ + QName *qnp = (QName *)handle; + + if ( PR_CLIST_IS_EMPTY( &qNameList )) { + qnp = NULL; + } + else if ( qnp == NULL ) { + qnp = (QName *)PR_LIST_HEAD( &qNameList ); + } + else if ( PR_NEXT_LINK( &qnp->link ) == &qNameList ) { + qnp = NULL; + } + else { + qnp = (QName *)PR_NEXT_LINK( &qnp->link ); + } + + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextQname: Handle: %p, Returns: %p", + handle, qnp )); + + return((PRTraceHandle)qnp); +} /* end PR_FindNextTraceQname() */ + +/* +** +*/ +PR_IMPLEMENT(PRTraceHandle) +PR_FindNextTraceRname( + PRTraceHandle rhandle, + PRTraceHandle qhandle +) +{ + RName *rnp = (RName *)rhandle; + QName *qnp = (QName *)qhandle; + + + if ( PR_CLIST_IS_EMPTY( &qnp->rNameList )) { + rnp = NULL; + } + else if ( rnp == NULL ) { + rnp = (RName *)PR_LIST_HEAD( &qnp->rNameList ); + } + else if ( PR_NEXT_LINK( &rnp->link ) == &qnp->rNameList ) { + rnp = NULL; + } + else { + rnp = (RName *)PR_NEXT_LINK( &rnp->link ); + } + + PR_LOG( lm, PR_LOG_DEBUG, ("PRTrace: FindNextRname: Rhandle: %p, QHandle: %p, Returns: %p", + rhandle, qhandle, rnp )); + + return((PRTraceHandle)rnp); +} /* end PR_FindNextTraceRname() */ + +/* +** +*/ +static PRFileDesc * InitializeRecording( void ) +{ + char *logFileName; + PRFileDesc *logFile; + + /* Self initialize, if necessary */ + if ( traceLock == NULL ) { + _PR_InitializeTrace(); + } + + PR_LOG( lm, PR_LOG_DEBUG, + ("PR_RecordTraceEntries: begins")); + + logLostData = 0; /* reset at entry */ + logState = LogReset; + + /* Get the filename for the logfile from the environment */ + logFileName = PR_GetEnvSecure( "NSPR_TRACE_LOG" ); + if ( logFileName == NULL ) + { + PR_LOG( lm, PR_LOG_ERROR, + ("RecordTraceEntries: Environment variable not defined. Exiting")); + return NULL; + } + + /* Open the logfile */ + logFile = PR_Open( logFileName, PR_WRONLY | PR_CREATE_FILE, 0666 ); + if ( logFile == NULL ) + { + PR_LOG( lm, PR_LOG_ERROR, + ("RecordTraceEntries: Cannot open %s as trace log file. OS error: %ld", + logFileName, PR_GetOSError())); + return NULL; + } + return logFile; +} /* end InitializeRecording() */ + +/* +** +*/ +static void ProcessOrders( void ) +{ + switch ( logOrder ) + { + case LogReset : + logOrder = logState = localState; + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: LogReset")); + break; + + case LogSuspend : + localState = logOrder = logState = LogSuspend; + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: LogSuspend")); + break; + + case LogResume : + localState = logOrder = logState = LogActive; + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: LogResume")); + break; + + case LogStop : + logOrder = logState = LogStop; + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: LogStop")); + break; + + default : + PR_LOG( lm, PR_LOG_ERROR, + ("RecordTraceEntries: Invalid logOrder: %ld", logOrder )); + PR_ASSERT( 0 ); + break; + } /* end switch() */ + return ; +} /* end ProcessOrders() */ + +/* +** +*/ +static void WriteTraceSegment( PRFileDesc *logFile, void *buf, PRInt32 amount ) +{ + PRInt32 rc; + + + PR_LOG( lm, PR_LOG_ERROR, + ("WriteTraceSegment: Buffer: %p, Amount: %ld", buf, amount)); + rc = PR_Write( logFile, buf, amount ); + if ( rc == -1 ) + PR_LOG( lm, PR_LOG_ERROR, + ("RecordTraceEntries: PR_Write() failed. Error: %ld", PR_GetError() )); + else if ( rc != amount ) + PR_LOG( lm, PR_LOG_ERROR, + ("RecordTraceEntries: PR_Write() Tried to write: %ld, Wrote: %ld", amount, rc)); + else + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: PR_Write(): Buffer: %p, bytes: %ld", buf, amount)); + + return; +} /* end WriteTraceSegment() */ + +/* +** +*/ +PR_IMPLEMENT(void) +PR_RecordTraceEntries( + void +) +{ + PRFileDesc *logFile; + PRInt32 lostSegments; + PRInt32 currentSegment = 0; + void *buf; + PRBool doWrite; + + logFile = InitializeRecording(); + if ( logFile == NULL ) + { + PR_LOG( lm, PR_LOG_DEBUG, + ("PR_RecordTraceEntries: Failed to initialize")); + return; + } + + /* Do this until told to stop */ + while ( logState != LogStop ) + { + + PR_Lock( logLock ); + + while ( (logCount == 0) && ( logOrder == logState ) ) { + PR_WaitCondVar( logCVar, PR_INTERVAL_NO_TIMEOUT ); + } + + /* Handle state transitions */ + if ( logOrder != logState ) { + ProcessOrders(); + } + + /* recalculate local controls */ + if ( logCount ) + { + lostSegments = logCount - logSegments; + if ( lostSegments > 0 ) + { + logLostData += ( logCount - logSegments ); + logCount = (logCount % logSegments); + currentSegment = logCount; + PR_LOG( lm, PR_LOG_DEBUG, + ("PR_RecordTraceEntries: LostData segments: %ld", logLostData)); + } + else + { + logCount--; + } + + buf = tBuf + ( logEntriesPerSegment * currentSegment ); + if (++currentSegment >= logSegments ) { + currentSegment = 0; + } + doWrite = PR_TRUE; + } + else { + doWrite = PR_FALSE; + } + + PR_Unlock( logLock ); + + if ( doWrite == PR_TRUE ) + { + if ( localState != LogSuspend ) { + WriteTraceSegment( logFile, buf, logSegSize ); + } + else + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: PR_Write(): is suspended" )); + } + + } /* end while(logState...) */ + + PR_Close( logFile ); + PR_LOG( lm, PR_LOG_DEBUG, + ("RecordTraceEntries: exiting")); + return; +} /* end PR_RecordTraceEntries() */ + +/* +** +*/ +PR_IMPLEMENT(PRIntn) +PR_GetTraceEntries( + PRTraceEntry *buffer, /* where to write output */ + PRInt32 count, /* number to get */ + PRInt32 *found /* number you got */ +) +{ + PRInt32 rc; + PRInt32 copied = 0; + + PR_Lock( traceLock ); + + /* + ** Depending on where the LastSeen and Next indices are, + ** copy the trace buffer in one or two pieces. + */ + PR_LOG( lm, PR_LOG_ERROR, + ("PR_GetTraceEntries: Next: %ld, LastSeen: %ld", next, fetchLastSeen)); + + if ( fetchLastSeen <= next ) + { + while (( count-- > 0 ) && (fetchLastSeen < next )) + { + *(buffer + copied++) = *(tBuf + fetchLastSeen++); + } + PR_LOG( lm, PR_LOG_ERROR, + ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen)); + } + else /* copy in 2 parts */ + { + while ( count-- > 0 && fetchLastSeen <= last ) + { + *(buffer + copied++) = *(tBuf + fetchLastSeen++); + } + fetchLastSeen = 0; + + PR_LOG( lm, PR_LOG_ERROR, + ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen)); + + while ( count-- > 0 && fetchLastSeen < next ) + { + *(buffer + copied++) = *(tBuf + fetchLastSeen++); + } + PR_LOG( lm, PR_LOG_ERROR, + ("PR_GetTraceEntries: Copied: %ld, LastSeen: %ld", copied, fetchLastSeen)); + } + + *found = copied; + rc = ( fetchLostData == PR_TRUE )? 1 : 0; + fetchLostData = PR_FALSE; + + PR_Unlock( traceLock ); + return rc; +} /* end PR_GetTraceEntries() */ + +/* end prtrace.c */ diff --git a/nsprpub/pr/src/nspr.def b/nsprpub/pr/src/nspr.def new file mode 100644 index 0000000000..49a437b1fa --- /dev/null +++ b/nsprpub/pr/src/nspr.def @@ -0,0 +1,468 @@ +;+# +;+# This Source Code Form is subject to the terms of the Mozilla Public +;+# License, v. 2.0. If a copy of the MPL was not distributed with this +;+# file, You can obtain one at http://mozilla.org/MPL/2.0/. +;+# +;+# OK, this file is meant to support SUN, LINUX, AIX, OS/2 and WINDOWS +;+# 1. For all unix platforms, the string ";-" means "remove this line" +;+# 2. For all unix platforms, the string " DATA " will be removed from any +;+# line on which it occurs. +;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX. +;+# On AIX, lines containing ";+" will be removed. +;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed. +;+# 5. For all unix platforms, after the above processing has taken place, +;+# all characters after the first ";" on the line will be removed. +;+# And for AIX, the first ";" will also be removed. +;+# This file is passed directly to windows. Since ';' is a comment, all UNIX +;+# directives are hidden behind ";", ";+", and ";-" +;+# +;+NSPR_4.0 { +;+ global: +LIBRARY nspr4 ;- +EXPORTS ;- + LL_MaxInt; + LL_MinInt; + LL_Zero; + PR_Abort; + PR_AddToCounter; + PR_Accept; + PR_AcceptRead; + PR_Access; + PR_AddWaitFileDesc; + PR_AllocFileDesc; + PR_Assert; + PR_AtomicAdd; + PR_AtomicDecrement; + PR_AtomicIncrement; + PR_AtomicSet; + PR_AttachSharedMemory; + PR_AttachThread; + PR_Available; + PR_Available64; + PR_Bind; + PR_BlockClockInterrupts; + PR_BlockInterrupt; + PR_CEnterMonitor; + PR_CExitMonitor; + PR_CNotify; + PR_CNotifyAll; + PR_CSetOnMonitorRecycle; + PR_CWait; + PR_CallOnce; + PR_Calloc; + PR_CancelJob; + PR_CancelWaitFileDesc; + PR_CancelWaitGroup; + PR_CeilingLog2; + PR_ChangeFileDescNativeHandle; + PR_Cleanup; + PR_ClearInterrupt; + PR_ClearThreadGCAble; + PR_Close; + PR_CloseDir; + PR_CloseFileMap; + PR_CloseSemaphore; + PR_CloseSharedMemory; + PR_Connect; + PR_CreateCounter; + PR_ConvertIPv4AddrToIPv6; + PR_CreateAlarm; + PR_CreateFileMap; + PR_CreateIOLayerStub; + PR_CreateOrderedLock; + PR_CreateMWaitEnumerator; + PR_CreatePipe; + PR_CreateProcess; + PR_CreateProcessDetached; + PR_CreateSocketPollFd; + PR_CreateStack; + PR_CreateThread; + PR_CreateThreadGCAble; + PR_CreateTrace; + PR_CreateThreadPool; + PR_DecrementCounter; + PR_CreateWaitGroup; + PR_Delete; + PR_DeleteSemaphore; + PR_DeleteSharedMemory; + PR_DestroyAlarm; + PR_DestroyCounter; + PR_DestroyCondVar; + PR_DestroyLock; + PR_DestroyMWaitEnumerator; + PR_DestroyOrderedLock; + PR_DestroyMonitor; + PR_DestroyPollableEvent; + PR_DestroyProcessAttr; + PR_DestroyRWLock; + PR_DestroySem; + PR_DestroySocketPollFd; + PR_DestroyTrace; + PR_DestroyStack; + PR_DestroyWaitGroup; + PR_DetachProcess; + PR_DetachSharedMemory; + PR_DetachThread; + PR_DisableClockInterrupts; + PR_EnableClockInterrupts; + PR_EnterMonitor; + PR_EnumerateHostEnt; + PR_EnumerateThreads; + PR_EnumerateWaitGroup; + PR_ErrorInstallCallback; + PR_ErrorInstallTable; + PR_ErrorLanguages; + PR_ErrorToName; + PR_ErrorToString; + PR_ExitMonitor; + PR_ExplodeTime; + PR_ExportFileMapAsString; + PR_FD_CLR; + PR_FD_ISSET; + PR_FD_NCLR; + PR_FD_NISSET; + PR_FD_NSET; + PR_FD_SET; + PR_FD_ZERO; + PR_FileDesc2NativeHandle; + PR_FindSymbol; + PR_FindSymbolAndLibrary; + PR_FloorLog2; + PR_FormatTime; + PR_FindNextCounterQname; + PR_FindNextCounterRname; + PR_FindNextTraceQname; + PR_FindNextTraceRname; + PR_FormatTimeUSEnglish; + PR_Free; + PR_FreeLibraryName; + PR_GMTParameters; + PR_GetConnectStatus; + PR_GetCurrentThread; + PR_GetDefaultIOMethods; + PR_GetDescType; + PR_GetDirectorySeparator; + PR_GetCounter; + PR_GetCounterHandleFromName; + PR_GetCounterNameFromHandle; + PR_GetDirectorySepartor; + PR_GetEnv; + PR_GetError; + PR_GetErrorText; + PR_GetErrorTextLength; + PR_GetFileInfo; + PR_GetFileInfo64; + PR_GetFileMethods; + PR_GetGCRegisters; + PR_GetHostByAddr; + PR_GetHostByName; + PR_GetIPNodeByName; + PR_GetIdentitiesLayer; + PR_GetInheritedFD; + PR_GetInheritedFileMap; + PR_GetLayersIdentity; + PR_GetLibraryName; + PR_GetLibraryPath; + PR_GetMonitorEntryCount; + PR_GetNameForIdentity; + PR_GetOSError; + PR_GetOpenFileInfo; + PR_GetOpenFileInfo64; + PR_GetPageShift; + PR_GetPageSize; + PR_GetPeerName; + PR_GetPipeMethods; + PR_GetProtoByName; + PR_GetProtoByNumber; + PR_GetRandomNoise; + PR_GetSP; + PR_GetSockName; + PR_GetSocketOption; + PR_GetSpecialFD; + PR_GetStackSpaceLeft; + PR_GetSysfdTableMax; + PR_GetSystemInfo; + PR_GetTCPMethods; + PR_GetThreadAffinityMask; + PR_GetThreadID; + PR_GetThreadPriority; + PR_GetThreadPrivate; + PR_GetThreadScope; + PR_GetThreadState; + PR_GetThreadType; + PR_GetUDPMethods; + PR_GetUniqueIdentity; + PR_ImplodeTime; + PR_ImportFile; + PR_ImportFileMapFromString; + PR_ImportTCPSocket; + PR_ImportUDPSocket; + PR_GetTraceEntries; + PR_GetTraceHandleFromName; + PR_GetTraceNameFromHandle; + PR_GetTraceOption; + PR_Init; + PR_Initialize; + PR_InitializeNetAddr; + PR_Initialized; + PR_Interrupt; + PR_IntervalNow; + PR_IntervalToMicroseconds; + PR_IntervalToMilliseconds; + PR_IncrementCounter; + PR_IntervalToSeconds; + PR_IsNetAddrType; + PR_JoinJob; + PR_JoinThread; + PR_JoinThreadPool; + PR_KillProcess; + PR_Listen; + PR_LoadLibrary; + PR_LoadLibraryWithFlags; + PR_LoadStaticLibrary; + PR_LocalTimeParameters; + PR_Lock; + PR_LockFile; + PR_LogFlush; + PR_LogPrint; + PR_MakeDir; + PR_Malloc; + PR_MemMap; + PR_MemUnmap; + PR_MicrosecondsToInterval; + PR_MillisecondsToInterval; + PR_LockOrderedLock; + PR_MkDir; + PR_NetAddrToString; + PR_NewCondVar; + PR_NewLock; + PR_NewLogModule; + PR_NewMonitor; + PR_NewNamedMonitor; + PR_NewPollableEvent; + PR_NewProcessAttr; + PR_NewRWLock; + PR_NewSem; + PR_NewTCPSocket; + PR_NewTCPSocketPair; + PR_NewThreadPrivateIndex; + PR_NewUDPSocket; + PR_NormalizeTime; + PR_Notify; + PR_NotifyAll; + PR_NotifyAllCondVar; + PR_NotifyCondVar; + PR_Now; + PR_Open; + PR_OpenAnonFileMap; + PR_OpenDir; + PR_OpenFile; + PR_OpenSemaphore; + PR_OpenSharedMemory; + PR_OpenTCPSocket; + PR_OpenUDPSocket; + PR_ParseTimeString; + PR_Poll; + PR_PopIOLayer; + PR_PostSem; + PR_PostSemaphore; + PR_ProcessAttrSetCurrentDirectory; + PR_ProcessAttrSetInheritableFD; + PR_ProcessAttrSetInheritableFileMap; + PR_ProcessAttrSetStdioRedirect; + PR_ProcessExit; + PR_PushIOLayer; + PR_QueueJob; + PR_QueueJob_Accept; + PR_QueueJob_Connect; + PR_QueueJob_Read; + PR_QueueJob_Timer; + PR_QueueJob_Write; + PR_RWLock_Rlock; + PR_RWLock_Unlock; + PR_RWLock_Wlock; + PR_Read; + PR_ReadDir; + PR_Realloc; + PR_Recv; + PR_RecvFrom; + PR_Rename; + PR_ResetAlarm; + PR_ResetProcessAttr; + PR_ResumeAll; + PR_RmDir; + PR_ScanStackPointers; + PR_RecordTraceEntries; + PR_SecondsToInterval; + PR_Seek; + PR_Seek64; + PR_Select; + PR_Send; + PR_SendFile; + PR_SendTo; + PR_SetAlarm; + PR_SetConcurrency; + PR_SetError; + PR_SetErrorText; + PR_SetFDCacheSize; + PR_SetFDInheritable; + PR_SetLibraryPath; + PR_SetLogBuffering; + PR_SetLogFile; + PR_SetNetAddr; + PR_SetPollableEvent; + PR_SetSocketOption; + PR_SetCounter; + PR_SetStdioRedirect; + PR_SetSysfdTableSize; + PR_SetThreadAffinityMask; + PR_SetThreadDumpProc; + PR_SetThreadGCAble; + PR_SetThreadPriority; + PR_SetThreadPrivate; + PR_SetThreadRecycleMode; + PR_Shutdown; + PR_ShutdownThreadPool; + PR_Sleep; + PR_Socket; + PR_StackPop; + PR_StackPush; + PR_Stat; + PR_StringToNetAddr; + PR_SuspendAll; + PR_Sync; + PR_TLockFile; + PR_ThreadScanStackPointers; + PR_SetTraceOption; + PR_TicksPerSecond; + PR_TransmitFile; + PR_USPacificTimeParameters; + PR_UnblockClockInterrupts; + PR_UnblockInterrupt; + PR_UnloadLibrary; + PR_SubtractFromCounter; + PR_Unlock; + PR_UnlockFile; + PR_VersionCheck; + PR_Wait; + PR_WaitCondVar; + PR_WaitForPollableEvent; + PR_Trace; + PR_WaitProcess; + PR_WaitRecvReady; + PR_WaitSem; + PR_WaitSemaphore; + PR_Write; + PR_Writev; + PR_Yield; + PR_UnlockOrderedLock; + PR_cnvtf; + PR_dtoa; + PR_fprintf; + PR_htonl; + PR_htonll; + PR_htons; + PR_ntohl; + PR_ntohll; + PR_ntohs; + PR_smprintf; + PR_smprintf_free; + PR_snprintf; + PR_sprintf_append; + PR_sscanf; + PR_strtod; + PR_sxprintf; + PR_vfprintf; + PR_vsmprintf; + PR_vsnprintf; + PR_vsprintf_append; + PR_vsxprintf; + PRP_DestroyNakedCondVar; + PRP_NakedBroadcast; + PRP_NakedNotify; + PRP_NakedWait; + PRP_NewNakedCondVar; + PRP_TryLock; + libVersionPoint; +;+ local: *; +;+}; +;+ +;+NSPRprivate { +;+ global: + GetExecutionEnvironment; + PT_FPrintStats; + SetExecutionEnvironment; +;+ local: *; +;+}; +;+ +;+NSPR_4.1 { +;+ global: + PR_ConnectContinue; + PR_CreateIOLayer; + PR_EmulateAcceptRead; + PR_EmulateSendFile; + PR_FindFunctionSymbol; + PR_FindFunctionSymbolAndLibrary; + PR_GetMemMapAlignment; + PR_GetNumberOfProcessors; + PR_ImportPipe; + PR_SetEnv; +;+} NSPR_4.0; +;+ +;+NSPR_4.3 { +;+ global: + LL_MaxUint; + PR_CallOnceWithArg; + PR_GetLibraryFilePathname; +;+} NSPR_4.1; +;+ +;+NSPR_4.4 { +;+ global: + PR_GetPathSeparator; +;+} NSPR_4.3; +;+ +;+NSPR_4.5 { +;+ global: + PR_EnumerateAddrInfo; + PR_FreeAddrInfo; + PR_GetAddrInfoByName; + PR_GetCanonNameFromAddrInfo; +;+} NSPR_4.4; +;+ +;+NSPR_4.6 { +;+ global: + PR_GetPhysicalMemorySize; +;+} NSPR_4.5; +;+NSPR_4.7 { +;+ global: + PR_ParseTimeStringToExplodedTime; +;+} NSPR_4.6; +;+NSPR_4.8 { +;+ global: + PR_AssertCurrentThreadOwnsLock; + PR_AssertCurrentThreadInMonitor; +;+} NSPR_4.7; +;+NSPR_4.8.9 { +;+ global: + PR_GetVersion; +;+} NSPR_4.8; +;+NSPR_4.9.2 { +;+ global: + PR_GetThreadName; + PR_SetCurrentThreadName; +;+} NSPR_4.8.9; +;+NSPR_4.10.3 { +;+ global: + PR_SyncMemMap; +;+} NSPR_4.9.2; +;+# Function PR_DuplicateEnvironment had been added in NSPR 4.10.9, +;+# but we neglected to add it to nspr.def until NSPR 4.12 +;+NSPR_4.12 { +;+ global: + PR_DuplicateEnvironment; + PR_GetEnvSecure; +;+} NSPR_4.10.3; +;+NSPR_4.34 { +;+ global: + PR_GetPrefLoopbackAddrInfo; +;+} NSPR_4.12; diff --git a/nsprpub/pr/src/nspr.rc b/nsprpub/pr/src/nspr.rc new file mode 100644 index 0000000000..bdfb1bdc3f --- /dev/null +++ b/nsprpub/pr/src/nspr.rc @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prinit.h" +#include <winver.h> + +#define MY_LIBNAME "nspr" +#define MY_FILEDESCRIPTION "NSPR Library" + +#define STRINGIZE(x) #x +#define STRINGIZE2(x) STRINGIZE(x) +#define PR_VMAJOR_STR STRINGIZE2(PR_VMAJOR) + +#ifdef _DEBUG +#define MY_DEBUG_STR " (debug)" +#define MY_FILEFLAGS_1 VS_FF_DEBUG +#else +#define MY_DEBUG_STR "" +#define MY_FILEFLAGS_1 0x0L +#endif +#if PR_BETA +#define MY_FILEFLAGS_2 MY_FILEFLAGS_1|VS_FF_PRERELEASE +#else +#define MY_FILEFLAGS_2 MY_FILEFLAGS_1 +#endif + +#ifdef WINNT +#define MY_FILEOS VOS_NT_WINDOWS32 +#define MY_INTERNAL_NAME "lib" MY_LIBNAME PR_VMAJOR_STR +#else +#define MY_FILEOS VOS__WINDOWS32 +#define MY_INTERNAL_NAME MY_LIBNAME PR_VMAJOR_STR +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Version-information resource +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PR_VMAJOR,PR_VMINOR,PR_VPATCH,0 + PRODUCTVERSION PR_VMAJOR,PR_VMINOR,PR_VPATCH,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS MY_FILEFLAGS_2 + FILEOS MY_FILEOS + FILETYPE VFT_DLL + FILESUBTYPE 0x0L // not used + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "CompanyName", "Mozilla Foundation\0" + VALUE "FileDescription", MY_FILEDESCRIPTION MY_DEBUG_STR "\0" + VALUE "FileVersion", PR_VERSION "\0" + VALUE "InternalName", MY_INTERNAL_NAME "\0" + VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0" + VALUE "ProductName", "Netscape Portable Runtime\0" + VALUE "ProductVersion", PR_VERSION "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/nsprpub/pr/src/os2extra.def b/nsprpub/pr/src/os2extra.def new file mode 100644 index 0000000000..2cbf5ad4ca --- /dev/null +++ b/nsprpub/pr/src/os2extra.def @@ -0,0 +1,20 @@ +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + + ; + ; Support plugins that were explicitly linked to the Visual Age + ; version of nspr4.dll. + ; + PR_NewMonitor + PR_EnterMonitor + PR_ExitMonitor + PR_GetCurrentThread + PR_AttachThread + PR_DetachThread + ; + ; Exception handler functions that are used by nsAppRunner.cpp + ; + _PR_OS2_SetFloatExcpHandler + _PR_OS2_UnsetFloatExcpHandler + diff --git a/nsprpub/pr/src/prvrsion.c b/nsprpub/pr/src/prvrsion.c new file mode 100644 index 0000000000..09dcc66597 --- /dev/null +++ b/nsprpub/pr/src/prvrsion.c @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prinit.h" +#include "prvrsion.h" + +/************************************************************************/ +/**************************IDENTITY AND VERSIONING***********************/ +/************************************************************************/ +#include "_pr_bld.h" +#if !defined(_BUILD_TIME) +#ifdef HAVE_LONG_LONG +#define _BUILD_TIME 0 +#else +#define _BUILD_TIME {0, 0} +#endif +#endif +#if !defined(_BUILD_STRING) +#define _BUILD_STRING "" +#endif +#if !defined(_PRODUCTION) +#define _PRODUCTION "" +#endif +#if defined(DEBUG) +#define _DEBUG_STRING " (debug)" +#else +#define _DEBUG_STRING "" +#endif + +/* + * A trick to expand the PR_VMAJOR macro before concatenation. + */ +#define CONCAT(x, y) x ## y +#define CONCAT2(x, y) CONCAT(x, y) +#define VERSION_DESC_NAME CONCAT2(prVersionDescription_libnspr, PR_VMAJOR) + +PRVersionDescription VERSION_DESC_NAME = +{ + /* version */ 2, /* this is the only one supported */ + /* buildTime */ _BUILD_TIME, /* usecs since midnight 1/1/1970 GMT */ + /* buildTimeString */ _BUILD_STRING, /* ditto, but human readable */ + /* vMajor */ PR_VMAJOR, /* NSPR's version number */ + /* vMinor */ PR_VMINOR, /* and minor version */ + /* vPatch */ PR_VPATCH, /* and patch */ + /* beta */ PR_BETA, /* beta build boolean */ +#if defined(DEBUG) + /* debug */ PR_TRUE, /* a debug build */ +#else + /* debug */ PR_FALSE, /* an optomized build */ +#endif + /* special */ PR_FALSE, /* they're all special, but ... */ + /* filename */ _PRODUCTION, /* the produced library name */ + /* description */ "Portable runtime", /* what we are */ + /* security */ "N/A", /* not applicable here */ + /* copywrite */ "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.", + /* comment */ "License information: http://www.mozilla.org/MPL/", + /* specialString */ "" +}; + +#ifdef XP_UNIX + +/* + * Version information for the 'ident' and 'what commands + * + * NOTE: the first component of the concatenated rcsid string + * must not end in a '$' to prevent rcs keyword substitution. + */ +static char rcsid[] = "$Header: NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING " $"; +static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING; + +#endif /* XP_UNIX */ + +#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif +PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint(void) +{ +#ifdef XP_UNIX + /* + * Add dummy references to rcsid and sccsid to prevent them + * from being optimized away as unused variables. + */ + const char *dummy; + + dummy = rcsid; + dummy = sccsid; +#endif + return &VERSION_DESC_NAME; +} /* versionEntryPointType */ +#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC +#pragma GCC diagnostic pop +#endif + +/* prvrsion.c */ + diff --git a/nsprpub/pr/src/pthreads/Makefile.in b/nsprpub/pr/src/pthreads/Makefile.in new file mode 100644 index 0000000000..c8191c749a --- /dev/null +++ b/nsprpub/pr/src/pthreads/Makefile.in @@ -0,0 +1,35 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = \ + ptio.c \ + ptsynch.c \ + ptthread.c \ + ptmisc.c \ + $(NULL) + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + + diff --git a/nsprpub/pr/src/pthreads/ptio.c b/nsprpub/pr/src/pthreads/ptio.c new file mode 100644 index 0000000000..a2bc3e714d --- /dev/null +++ b/nsprpub/pr/src/pthreads/ptio.c @@ -0,0 +1,5387 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: ptio.c +** Descritpion: Implemenation of I/O methods for pthreads +*/ + +#if defined(_PR_PTHREADS) + +#if defined(_PR_POLL_WITH_SELECT) +#if !(defined(HPUX) && defined(_USE_BIG_FDS)) +/* set fd limit for select(), before including system header files */ +#define FD_SETSIZE (16 * 1024) +#endif +#endif + +#include <pthread.h> +#include <string.h> /* for memset() */ +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#if defined(DARWIN) +#include <sys/utsname.h> /* for uname */ +#endif +#if defined(SOLARIS) || defined(UNIXWARE) +#include <sys/filio.h> /* to pick up FIONREAD */ +#endif +#ifdef _PR_POLL_AVAILABLE +#include <poll.h> +#endif +#ifdef AIX +/* To pick up sysconf() */ +#include <unistd.h> +#include <dlfcn.h> /* for dlopen */ +#else +/* To pick up getrlimit() etc. */ +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#ifdef SOLARIS +/* + * Define HAVE_SENDFILEV if the system has the sendfilev() system call. + * Code built this way won't run on a system without sendfilev(). + * We can define HAVE_SENDFILEV by default when the minimum release + * of Solaris that NSPR supports has sendfilev(). + */ +#ifdef HAVE_SENDFILEV + +#include <sys/sendfile.h> + +#define SOLARIS_SENDFILEV(a, b, c, d) sendfilev((a), (b), (c), (d)) + +#else + +#include <dlfcn.h> /* for dlopen */ + +/* + * Match the definitions in <sys/sendfile.h>. + */ +typedef struct sendfilevec { + int sfv_fd; /* input fd */ + uint_t sfv_flag; /* flags */ + off_t sfv_off; /* offset to start reading from */ + size_t sfv_len; /* amount of data */ +} sendfilevec_t; + +#define SFV_FD_SELF (-2) + +/* + * extern ssize_t sendfilev(int, const struct sendfilevec *, int, size_t *); + */ +static ssize_t (*pt_solaris_sendfilev_fptr)() = NULL; + +#define SOLARIS_SENDFILEV(a, b, c, d) \ + (*pt_solaris_sendfilev_fptr)((a), (b), (c), (d)) + +#endif /* HAVE_SENDFILEV */ +#endif /* SOLARIS */ + +/* + * 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_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. + */ +#ifdef AIX +#ifdef SF_CLOSE +#define HAVE_SEND_FILE +#endif + +#ifdef HAVE_SEND_FILE + +#define AIX_SEND_FILE(a, b, c) send_file(a, b, c) + +#else /* HAVE_SEND_FILE */ + +/* + * The following definitions match those in <sys/socket.h> + * on AIX 4.3.2. + */ + +/* + * Structure for the send_file() system call + */ +struct sf_parms { + /* --------- header parms ---------- */ + void *header_data; /* Input/Output. Points to header buf */ + uint_t header_length; /* Input/Output. Length of the header */ + /* --------- file parms ------------ */ + int file_descriptor; /* Input. File descriptor of the file */ + unsigned long long file_size; /* Output. Size of the file */ + unsigned long long file_offset; /* Input/Output. Starting offset */ + long long file_bytes; /* Input/Output. no. of bytes to send */ + /* --------- trailer parms --------- */ + void *trailer_data; /* Input/Output. Points to trailer buf */ + uint_t trailer_length; /* Input/Output. Length of the trailer */ + /* --------- return info ----------- */ + unsigned long long bytes_sent; /* Output. no. of bytes sent */ +}; + +/* + * Flags for the send_file() system call + */ +#define SF_CLOSE 0x00000001 /* close the socket after completion */ +#define SF_REUSE 0x00000002 /* reuse socket. not supported */ +#define SF_DONT_CACHE 0x00000004 /* don't apply network buffer cache */ +#define SF_SYNC_CACHE 0x00000008 /* sync/update network buffer cache */ + +/* + * prototype: size_t send_file(int *, struct sf_parms *, uint_t); + */ +static ssize_t (*pt_aix_sendfile_fptr)() = NULL; + +#define AIX_SEND_FILE(a, b, c) (*pt_aix_sendfile_fptr)(a, b, c) + +#endif /* HAVE_SEND_FILE */ +#endif /* AIX */ + +#ifdef LINUX +#include <sys/sendfile.h> +#endif + +#include "primpl.h" + +#if defined(LINUX) || defined(ANDROID) +#include <netinet/in.h> +#endif + +#ifdef DARWIN +#include <netinet/in.h> +#include <netinet/ip.h> +#endif + +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ +#endif + +#ifdef LINUX +/* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */ +#ifndef TCP_CORK +#define TCP_CORK 3 +#endif +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif +#endif + +#ifdef _PR_IPV6_V6ONLY_PROBE +static PRBool _pr_ipv6_v6only_on_by_default; +#endif + +#if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11)) +#define _PRSelectFdSetArg_t int * +#elif defined(AIX4_1) +#define _PRSelectFdSetArg_t void * +#elif (defined(AIX) && !defined(AIX4_1)) \ + || defined(SOLARIS) \ + || defined(HPUX10_30) || defined(HPUX11) \ + || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) \ + || defined(BSDI) || defined(NTO) || defined(DARWIN) \ + || defined(UNIXWARE) || defined(RISCOS) +#define _PRSelectFdSetArg_t fd_set * +#else +#error "Cannot determine architecture" +#endif + +#if defined(SOLARIS) +#ifndef PROTO_SDP +/* on solaris, SDP is a new type of protocol */ +#define PROTO_SDP 257 +#endif +#define _PR_HAVE_SDP +#elif defined(LINUX) +#ifndef AF_INET_SDP +/* on linux, SDP is a new type of address family */ +#define AF_INET_SDP 27 +#endif +#define _PR_HAVE_SDP +#endif /* LINUX */ + +static PRFileDesc *pt_SetMethods( + PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported); + +static PRLock *_pr_flock_lock; /* For PR_LockFile() etc. */ +static PRCondVar *_pr_flock_cv; /* For PR_LockFile() etc. */ +static PRLock *_pr_rename_lock; /* For PR_Rename() */ + +/**************************************************************************/ + +/* These two functions are only used in assertions. */ +#if defined(DEBUG) + +PRBool IsValidNetAddr(const PRNetAddr *addr) +{ + if ((addr != NULL) + && (addr->raw.family != AF_UNIX) + && (addr->raw.family != PR_AF_INET6) + && (addr->raw.family != AF_INET)) { + return PR_FALSE; + } + return PR_TRUE; +} + +static PRBool IsValidNetAddrLen(const PRNetAddr *addr, PRInt32 addr_len) +{ + /* + * The definition of the length of a Unix domain socket address + * is not uniform, so we don't check it. + */ + if ((addr != NULL) + && (addr->raw.family != AF_UNIX) + && (PR_NETADDR_SIZE(addr) != addr_len)) { +#if defined(LINUX) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 1 + /* + * In glibc 2.1, struct sockaddr_in6 is 24 bytes. In glibc 2.2 + * and in the 2.4 kernel, struct sockaddr_in6 has the scope_id + * field and is 28 bytes. It is possible for socket functions + * to return an addr_len greater than sizeof(struct sockaddr_in6). + * We need to allow that. (Bugzilla bug #77264) + */ + if ((PR_AF_INET6 == addr->raw.family) + && (sizeof(addr->ipv6) == addr_len)) { + return PR_TRUE; + } +#endif + return PR_FALSE; + } + return PR_TRUE; +} + +#endif /* DEBUG */ + +/*****************************************************************************/ +/************************* I/O Continuation machinery ************************/ +/*****************************************************************************/ + +/* + * The polling interval defines the maximum amount of time that a thread + * might hang up before an interrupt is noticed. + */ +#define PT_DEFAULT_POLL_MSEC 5000 +#if defined(_PR_POLL_WITH_SELECT) +#define PT_DEFAULT_SELECT_SEC (PT_DEFAULT_POLL_MSEC/PR_MSEC_PER_SEC) +#define PT_DEFAULT_SELECT_USEC \ + ((PT_DEFAULT_POLL_MSEC % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC) +#endif + +/* + * pt_SockLen is the type for the length of a socket address + * structure, used in the address length argument to bind, + * connect, accept, getsockname, getpeername, etc. Posix.1g + * defines this type as socklen_t. It is size_t or int on + * most current systems. + */ +#if defined(HAVE_SOCKLEN_T) \ + || (defined(__GLIBC__) && __GLIBC__ >= 2) +typedef socklen_t pt_SockLen; +#elif (defined(AIX) && !defined(AIX4_1)) +typedef PRSize pt_SockLen; +#else +typedef PRIntn pt_SockLen; +#endif + +typedef struct pt_Continuation pt_Continuation; +typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revents); + +typedef enum pr_ContuationStatus +{ + pt_continuation_pending, + pt_continuation_done +} pr_ContuationStatus; + +struct pt_Continuation +{ + /* The building of the continuation operation */ + ContinuationFn function; /* what function to continue */ + union { + PRIntn osfd; + } arg1; /* #1 - the op's fd */ + union { + void* buffer; + } arg2; /* #2 - primary transfer buffer */ + union { + PRSize amount; /* #3 - size of 'buffer', or */ + pt_SockLen *addr_len; /* - length of address */ +#ifdef HPUX11 + /* + * For sendfile() + */ + 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 */ + union { + PRNetAddr *addr; + } arg5; /* #5 - send/recv address */ + +#ifdef HPUX11 + /* + * For sendfile() + */ + int filedesc; /* descriptor of file to send */ + int nbytes_to_send; /* size of header and file */ +#endif /* HPUX11 */ + +#ifdef SOLARIS + /* + * For sendfilev() + */ + int nbytes_to_send; /* size of header and file */ +#endif /* SOLARIS */ + +#ifdef LINUX + /* + * For sendfile() + */ + int in_fd; /* descriptor of file to send */ + off_t offset; + size_t count; +#endif /* LINUX */ + + PRIntervalTime timeout; /* client (relative) timeout */ + + PRInt16 event; /* flags for poll()'s events */ + + /* + ** The representation and notification of the results of the operation. + ** These function can either return an int return code or a pointer to + ** some object. + */ + union { + PRSize code; + void *object; + } result; + + PRIntn syserrno; /* in case it failed, why (errno) */ + pr_ContuationStatus status; /* the status of the operation */ +}; + +#if defined(DEBUG) + +PTDebug pt_debug; /* this is shared between several modules */ + +PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) +{ + PTDebug stats; + char buffer[100]; + PRExplodedTime tod; + PRInt64 elapsed, aMil; + stats = pt_debug; /* a copy */ + PR_ExplodeTime(stats.timeStarted, PR_LocalTimeParameters, &tod); + (void)PR_FormatTime(buffer, sizeof(buffer), "%T", &tod); + + LL_SUB(elapsed, PR_Now(), stats.timeStarted); + LL_I2L(aMil, 1000000); + LL_DIV(elapsed, elapsed, aMil); + + if (NULL != msg) { + PR_fprintf(debug_out, "%s", msg); + } + PR_fprintf( + debug_out, "\tstarted: %s[%lld]\n", buffer, elapsed); + PR_fprintf( + debug_out, "\tlocks [created: %u, destroyed: %u]\n", + stats.locks_created, stats.locks_destroyed); + PR_fprintf( + debug_out, "\tlocks [acquired: %u, released: %u]\n", + stats.locks_acquired, stats.locks_released); + PR_fprintf( + debug_out, "\tcvars [created: %u, destroyed: %u]\n", + stats.cvars_created, stats.cvars_destroyed); + PR_fprintf( + debug_out, "\tcvars [notified: %u, delayed_delete: %u]\n", + stats.cvars_notified, stats.delayed_cv_deletes); +} /* PT_FPrintStats */ + +#else + +PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) +{ + /* do nothing */ +} /* PT_FPrintStats */ + +#endif /* DEBUG */ + +#if defined(_PR_POLL_WITH_SELECT) +/* + * HPUX report the POLLHUP event for a socket when the + * shutdown(SHUT_WR) operation is called for the remote end, even though + * the socket is still writeable. Use select(), instead of poll(), to + * workaround this problem. + */ +static void pt_poll_now_with_select(pt_Continuation *op) +{ + PRInt32 msecs; + fd_set rd, wr, *rdp, *wrp; + struct timeval tv; + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRThread *self = PR_GetCurrentThread(); + + PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout); + PR_ASSERT(op->arg1.osfd < FD_SETSIZE); + + switch (op->timeout) { + case PR_INTERVAL_NO_TIMEOUT: + tv.tv_sec = PT_DEFAULT_SELECT_SEC; + tv.tv_usec = PT_DEFAULT_SELECT_USEC; + do + { + PRIntn rv; + + if (op->event & POLLIN) { + FD_ZERO(&rd); + FD_SET(op->arg1.osfd, &rd); + rdp = &rd; + } else { + rdp = NULL; + } + if (op->event & POLLOUT) { + FD_ZERO(&wr); + FD_SET(op->arg1.osfd, &wr); + wrp = ≀ + } else { + wrp = NULL; + } + + rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) { + continue; /* go around the loop again */ + } + + if (rv > 0) + { + PRInt16 revents = 0; + + if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) { + revents |= POLLIN; + } + if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) { + revents |= POLLOUT; + } + + if (op->function(op, revents)) { + op->status = pt_continuation_done; + } + } else if (rv == -1) { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + /* else, select timed out */ + } while (pt_continuation_done != op->status); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = op->timeout; + do + { + PRIntn rv; + + if (op->event & POLLIN) { + FD_ZERO(&rd); + FD_SET(op->arg1.osfd, &rd); + rdp = &rd; + } else { + rdp = NULL; + } + if (op->event & POLLOUT) { + FD_ZERO(&wr); + FD_SET(op->arg1.osfd, &wr); + wrp = ≀ + } else { + wrp = NULL; + } + + wait_for_remaining = PR_TRUE; + msecs = (PRInt32)PR_IntervalToMilliseconds(remaining); + if (msecs > PT_DEFAULT_POLL_MSEC) { + wait_for_remaining = PR_FALSE; + msecs = PT_DEFAULT_POLL_MSEC; + } + tv.tv_sec = msecs/PR_MSEC_PER_SEC; + tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC; + rv = select(op->arg1.osfd + 1, rdp, wrp, NULL, &tv); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if (rv > 0) { + PRInt16 revents = 0; + + if ((op->event & POLLIN) && FD_ISSET(op->arg1.osfd, &rd)) { + revents |= POLLIN; + } + if ((op->event & POLLOUT) && FD_ISSET(op->arg1.osfd, &wr)) { + revents |= POLLOUT; + } + + if (op->function(op, revents)) { + op->status = pt_continuation_done; + } + + } else if ((rv == 0) || + ((errno == EINTR) || (errno == EAGAIN))) { + if (rv == 0) { /* select timed out */ + if (wait_for_remaining) { + now += remaining; + } + else { + now += PR_MillisecondsToInterval(msecs); + } + } else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= op->timeout) { + op->result.code = -1; + op->syserrno = ETIMEDOUT; + op->status = pt_continuation_done; + } else { + remaining = op->timeout - elapsed; + } + } else { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + } while (pt_continuation_done != op->status); + break; + } + +} /* pt_poll_now_with_select */ + +#endif /* _PR_POLL_WITH_SELECT */ + +static void pt_poll_now(pt_Continuation *op) +{ + PRInt32 msecs; + PRIntervalTime epoch, now, elapsed, remaining; + PRBool wait_for_remaining; + PRThread *self = PR_GetCurrentThread(); + + PR_ASSERT(PR_INTERVAL_NO_WAIT != op->timeout); +#if defined (_PR_POLL_WITH_SELECT) + /* + * If the fd is small enough call the select-based poll operation + */ + if (op->arg1.osfd < FD_SETSIZE) { + pt_poll_now_with_select(op); + return; + } +#endif + + switch (op->timeout) { + case PR_INTERVAL_NO_TIMEOUT: + msecs = PT_DEFAULT_POLL_MSEC; + do + { + PRIntn rv; + struct pollfd tmp_pfd; + + tmp_pfd.revents = 0; + tmp_pfd.fd = op->arg1.osfd; + tmp_pfd.events = op->event; + + rv = poll(&tmp_pfd, 1, msecs); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) { + continue; /* go around the loop again */ + } + + if (rv > 0) + { + PRInt16 events = tmp_pfd.events; + PRInt16 revents = tmp_pfd.revents; + + if ((revents & POLLNVAL) /* busted in all cases */ + || ((events & POLLOUT) && (revents & POLLHUP))) + /* write op & hup */ + { + op->result.code = -1; + if (POLLNVAL & revents) { + op->syserrno = EBADF; + } + else if (POLLHUP & revents) { + op->syserrno = EPIPE; + } + op->status = pt_continuation_done; + } else { + if (op->function(op, revents)) { + op->status = pt_continuation_done; + } + } + } else if (rv == -1) { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + /* else, poll timed out */ + } while (pt_continuation_done != op->status); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = op->timeout; + do + { + PRIntn rv; + struct pollfd tmp_pfd; + + tmp_pfd.revents = 0; + tmp_pfd.fd = op->arg1.osfd; + tmp_pfd.events = op->event; + + wait_for_remaining = PR_TRUE; + msecs = (PRInt32)PR_IntervalToMilliseconds(remaining); + if (msecs > PT_DEFAULT_POLL_MSEC) + { + wait_for_remaining = PR_FALSE; + msecs = PT_DEFAULT_POLL_MSEC; + } + rv = poll(&tmp_pfd, 1, msecs); + + if (_PT_THREAD_INTERRUPTED(self)) + { + self->state &= ~PT_THREAD_ABORTED; + op->result.code = -1; + op->syserrno = EINTR; + op->status = pt_continuation_done; + return; + } + + if (rv > 0) + { + PRInt16 events = tmp_pfd.events; + PRInt16 revents = tmp_pfd.revents; + + if ((revents & POLLNVAL) /* busted in all cases */ + || ((events & POLLOUT) && (revents & POLLHUP))) + /* write op & hup */ + { + op->result.code = -1; + if (POLLNVAL & revents) { + op->syserrno = EBADF; + } + else if (POLLHUP & revents) { + op->syserrno = EPIPE; + } + op->status = pt_continuation_done; + } else { + if (op->function(op, revents)) + { + op->status = pt_continuation_done; + } + } + } else if ((rv == 0) || + ((errno == EINTR) || (errno == EAGAIN))) { + if (rv == 0) /* poll timed out */ + { + if (wait_for_remaining) { + now += remaining; + } + else { + now += PR_MillisecondsToInterval(msecs); + } + } + else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= op->timeout) { + op->result.code = -1; + op->syserrno = ETIMEDOUT; + op->status = pt_continuation_done; + } else { + remaining = op->timeout - elapsed; + } + } else { + op->result.code = -1; + op->syserrno = errno; + op->status = pt_continuation_done; + } + } while (pt_continuation_done != op->status); + break; + } + +} /* pt_poll_now */ + +static PRIntn pt_Continue(pt_Continuation *op) +{ + op->status = pt_continuation_pending; /* set default value */ + /* + * let each thread call poll directly + */ + pt_poll_now(op); + PR_ASSERT(pt_continuation_done == op->status); + return op->result.code; +} /* pt_Continue */ + +/*****************************************************************************/ +/*********************** specific continuation functions *********************/ +/*****************************************************************************/ +static PRBool pt_connect_cont(pt_Continuation *op, PRInt16 revents) +{ + op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd); + if (op->syserrno != 0) { + op->result.code = -1; + } else { + op->result.code = 0; + } + return PR_TRUE; /* this one is cooked */ +} /* pt_connect_cont */ + +static PRBool pt_accept_cont(pt_Continuation *op, PRInt16 revents) +{ + op->syserrno = 0; + op->result.code = accept( + op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len); + if (-1 == op->result.code) + { + op->syserrno = errno; + if (EWOULDBLOCK == errno || EAGAIN == errno || ECONNABORTED == errno) { + return PR_FALSE; /* do nothing - this one ain't finished */ + } + } + return PR_TRUE; +} /* pt_accept_cont */ + +static PRBool pt_read_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Any number of bytes will complete the operation. It need + * not (and probably will not) satisfy the request. The only + * error we continue is EWOULDBLOCK|EAGAIN. + */ + op->result.code = read( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_read_cont */ + +static PRBool pt_recv_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Any number of bytes will complete the operation. It need + * not (and probably will not) satisfy the request. The only + * error we continue is EWOULDBLOCK|EAGAIN. + */ +#if defined(SOLARIS) + if (0 == op->arg4.flags) + op->result.code = read( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + else + op->result.code = recv( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); +#else + op->result.code = recv( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); +#endif + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recv_cont */ + +static PRBool pt_send_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes; +#if defined(SOLARIS) + PRInt32 tmp_amount = op->arg3.amount; +#endif + /* + * We want to write the entire amount out, no matter how many + * tries it takes. Keep advancing the buffer and the decrementing + * the amount until the amount goes away. Return the total bytes + * (which should be the original amount) when finished (or an + * error). + */ +#if defined(SOLARIS) +retry: + bytes = write(op->arg1.osfd, op->arg2.buffer, tmp_amount); +#else + bytes = send( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); +#endif + op->syserrno = errno; + +#if defined(SOLARIS) + /* + * The write system call has been reported to return the ERANGE error + * on occasion. Try to write in smaller chunks to workaround this bug. + */ + if ((bytes == -1) && (op->syserrno == ERANGE)) + { + if (tmp_amount > 1) + { + tmp_amount = tmp_amount/2; /* half the bytes */ + goto retry; + } + } +#endif + + if (bytes >= 0) /* this is progress */ + { + char *bp = (char*)op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_send_cont */ + +static PRBool pt_write_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes; + /* + * We want to write the entire amount out, no matter how many + * tries it takes. Keep advancing the buffer and the decrementing + * the amount until the amount goes away. Return the total bytes + * (which should be the original amount) when finished (or an + * error). + */ + bytes = write(op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + if (bytes >= 0) /* this is progress */ + { + char *bp = (char*)op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_write_cont */ + +static PRBool pt_writev_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes; + struct iovec *iov = (struct iovec*)op->arg2.buffer; + /* + * Same rules as write, but continuing seems to be a bit more + * complicated. As the number of bytes sent grows, we have to + * redefine the vector we're pointing at. We might have to + * modify an individual vector parms or we might have to eliminate + * a pair altogether. + */ + bytes = writev(op->arg1.osfd, iov, op->arg3.amount); + op->syserrno = errno; + if (bytes >= 0) /* this is progress */ + { + PRIntn iov_index; + op->result.code += bytes; /* accumulate the number sent */ + for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index) + { + /* how much progress did we make in the i/o vector? */ + if (bytes < iov[iov_index].iov_len) + { + /* this element's not done yet */ + char **bp = (char**)&(iov[iov_index].iov_base); + iov[iov_index].iov_len -= bytes; /* there's that much left */ + *bp += bytes; /* starting there */ + break; /* go off and do that */ + } + bytes -= iov[iov_index].iov_len; /* that element's consumed */ + } + op->arg2.buffer = &iov[iov_index]; /* new start of array */ + op->arg3.amount -= iov_index; /* and array length */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_writev_cont */ + +static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents) +{ + PRIntn bytes = sendto( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags, + (struct sockaddr*)op->arg5.addr, PR_NETADDR_SIZE(op->arg5.addr)); + op->syserrno = errno; + if (bytes >= 0) /* this is progress */ + { + char *bp = (char*)op->arg2.buffer; + bp += bytes; /* adjust the buffer pointer */ + op->arg2.buffer = bp; + op->result.code += bytes; /* accumulate the number sent */ + op->arg3.amount -= bytes; /* and reduce the required count */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + if ((EWOULDBLOCK != op->syserrno) && (EAGAIN != op->syserrno)) + { + op->result.code = -1; + return PR_TRUE; + } + else { + return PR_FALSE; + } +} /* pt_sendto_cont */ + +static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents) +{ + pt_SockLen addr_len = sizeof(PRNetAddr); + op->result.code = recvfrom( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, + op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len); + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recvfrom_cont */ + +#ifdef AIX +static PRBool pt_aix_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + struct sf_parms *sf_struct = (struct sf_parms *) op->arg2.buffer; + ssize_t rv; + unsigned 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 { + return PR_FALSE; + } + + if (rv == 1) { /* more data to send */ + return PR_FALSE; + } + + return PR_TRUE; +} +#endif /* AIX */ + +#ifdef HPUX11 +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.file_spec.offset, + op->arg3.file_spec.nbytes, hdtrl, op->arg4.flags); + PR_ASSERT(count <= op->nbytes_to_send); + op->syserrno = errno; + + if (count != -1) { + op->result.code += count; + } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { + op->result.code = -1; + } else { + return PR_FALSE; + } + if (count != -1 && count < op->nbytes_to_send) { + if (count < hdtrl[0].iov_len) { + /* header not sent */ + + hdtrl[0].iov_base = ((char *) hdtrl[0].iov_base) + count; + hdtrl[0].iov_len -= count; + + } 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; + } + + return PR_TRUE; +} +#endif /* HPUX11 */ + +#ifdef SOLARIS +static PRBool pt_solaris_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + struct sendfilevec *vec = (struct sendfilevec *) op->arg2.buffer; + size_t xferred; + ssize_t count; + + count = SOLARIS_SENDFILEV(op->arg1.osfd, vec, op->arg3.amount, &xferred); + op->syserrno = errno; + PR_ASSERT((count == -1) || (count == xferred)); + + if (count == -1) { + if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN + && op->syserrno != EINTR) { + op->result.code = -1; + return PR_TRUE; + } + count = xferred; + } else if (count == 0) { + /* + * We are now at EOF. The file was truncated. Solaris sendfile is + * supposed to return 0 and no error in this case, though some versions + * may return -1 and EINVAL . + */ + op->result.code = -1; + op->syserrno = 0; /* will be treated as EOF */ + return PR_TRUE; + } + PR_ASSERT(count <= op->nbytes_to_send); + + op->result.code += count; + if (count < op->nbytes_to_send) { + op->nbytes_to_send -= count; + + while (count >= vec->sfv_len) { + count -= vec->sfv_len; + vec++; + op->arg3.amount--; + } + PR_ASSERT(op->arg3.amount > 0); + + vec->sfv_off += count; + vec->sfv_len -= count; + PR_ASSERT(vec->sfv_len > 0); + op->arg2.buffer = vec; + + return PR_FALSE; + } + + return PR_TRUE; +} +#endif /* SOLARIS */ + +#ifdef LINUX +static PRBool pt_linux_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + ssize_t rv; + off_t oldoffset; + + oldoffset = op->offset; + rv = sendfile(op->arg1.osfd, op->in_fd, &op->offset, op->count); + op->syserrno = errno; + + if (rv == -1) { + if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { + op->result.code = -1; + return PR_TRUE; + } + rv = 0; + } + PR_ASSERT(rv == op->offset - oldoffset); + op->result.code += rv; + if (rv < op->count) { + op->count -= rv; + return PR_FALSE; + } + return PR_TRUE; +} +#endif /* LINUX */ + +void _PR_InitIO(void) +{ +#if defined(DEBUG) + memset(&pt_debug, 0, sizeof(PTDebug)); + pt_debug.timeStarted = PR_Now(); +#endif + + _pr_flock_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_flock_lock); + _pr_flock_cv = PR_NewCondVar(_pr_flock_lock); + PR_ASSERT(NULL != _pr_flock_cv); + _pr_rename_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_rename_lock); + + _PR_InitFdCache(); /* do that */ + + _pr_stdin = pt_SetMethods(0, PR_DESC_FILE, PR_FALSE, PR_TRUE); + _pr_stdout = pt_SetMethods(1, PR_DESC_FILE, PR_FALSE, PR_TRUE); + _pr_stderr = pt_SetMethods(2, PR_DESC_FILE, PR_FALSE, PR_TRUE); + PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr); + +#ifdef _PR_IPV6_V6ONLY_PROBE + /* In Mac OS X v10.3 Panther Beta the IPV6_V6ONLY socket option + * is turned on by default, contrary to what RFC 3493, Section + * 5.3 says. So we have to turn it off. Find out whether we + * are running on such a system. + */ + { + int osfd; + osfd = socket(AF_INET6, SOCK_STREAM, 0); + if (osfd != -1) { + int on; + socklen_t optlen = sizeof(on); + if (getsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, + &on, &optlen) == 0) { + _pr_ipv6_v6only_on_by_default = on; + } + close(osfd); + } + } +#endif +} /* _PR_InitIO */ + +void _PR_CleanupIO(void) +{ + _PR_Putfd(_pr_stdin); + _pr_stdin = NULL; + _PR_Putfd(_pr_stdout); + _pr_stdout = NULL; + _PR_Putfd(_pr_stderr); + _pr_stderr = NULL; + + _PR_CleanupFdCache(); + + if (_pr_flock_cv) + { + PR_DestroyCondVar(_pr_flock_cv); + _pr_flock_cv = NULL; + } + if (_pr_flock_lock) + { + PR_DestroyLock(_pr_flock_lock); + _pr_flock_lock = NULL; + } + if (_pr_rename_lock) + { + PR_DestroyLock(_pr_rename_lock); + _pr_rename_lock = NULL; + } +} /* _PR_CleanupIO */ + +PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) +{ + PRFileDesc *result = NULL; + PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError); + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + switch (osfd) + { + case PR_StandardInput: result = _pr_stdin; break; + case PR_StandardOutput: result = _pr_stdout; break; + case PR_StandardError: result = _pr_stderr; break; + default: + (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + return result; +} /* PR_GetSpecialFD */ + +/*****************************************************************************/ +/***************************** I/O private methods ***************************/ +/*****************************************************************************/ + +static PRBool pt_TestAbort(void) +{ + PRThread *me = PR_GetCurrentThread(); + if(_PT_THREAD_INTERRUPTED(me)) + { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + me->state &= ~PT_THREAD_ABORTED; + return PR_TRUE; + } + return PR_FALSE; +} /* pt_TestAbort */ + +static void pt_MapError(void (*mapper)(PRIntn), PRIntn syserrno) +{ + switch (syserrno) + { + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; + default: + mapper(syserrno); + } +} /* pt_MapError */ + +static PRStatus pt_Close(PRFileDesc *fd) +{ + if ((NULL == fd) || (NULL == fd->secret) + || ((_PR_FILEDESC_OPEN != fd->secret->state) + && (_PR_FILEDESC_CLOSED != fd->secret->state))) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (_PR_FILEDESC_OPEN == fd->secret->state) + { + if (-1 == close(fd->secret->md.osfd)) + { + pt_MapError(_PR_MD_MAP_CLOSE_ERROR, errno); + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + } + _PR_Putfd(fd); + return PR_SUCCESS; +} /* pt_Close */ + +static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PRInt32 syserrno, bytes = -1; + + if (pt_TestAbort()) { + return bytes; + } + + bytes = read(fd->secret->md.osfd, buf, amount); + syserrno = errno; + + if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.timeout = PR_INTERVAL_NO_TIMEOUT; + op.function = pt_read_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno); + } + return bytes; +} /* pt_Read */ + +static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + + if (pt_TestAbort()) { + return bytes; + } + + bytes = write(fd->secret->md.osfd, buf, amount); + syserrno = errno; + + if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) + { + buf = (char *) buf + bytes; + amount -= bytes; + fNeedContinue = PR_TRUE; + } + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + bytes = 0; + fNeedContinue = PR_TRUE; + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.timeout = PR_INTERVAL_NO_TIMEOUT; + op.result.code = bytes; /* initialize the number sent */ + op.function = pt_write_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes == -1) { + pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno); + } + return bytes; +} /* pt_Write */ + +static PRInt32 pt_Writev( + PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout) +{ + PRIntn iov_index; + PRBool fNeedContinue = PR_FALSE; + PRInt32 syserrno, bytes, rv = -1; + struct iovec osiov_local[PR_MAX_IOVECTOR_SIZE], *osiov; + int osiov_len; + + if (pt_TestAbort()) { + return rv; + } + + /* Ensured by PR_Writev */ + PR_ASSERT(iov_len <= PR_MAX_IOVECTOR_SIZE); + + /* + * 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. + */ + osiov = osiov_local; + osiov_len = iov_len; + for (iov_index = 0; iov_index < osiov_len; iov_index++) + { + osiov[iov_index].iov_base = iov[iov_index].iov_base; + osiov[iov_index].iov_len = iov[iov_index].iov_len; + } + + rv = bytes = writev(fd->secret->md.osfd, osiov, osiov_len); + syserrno = errno; + + if (!fd->secret->nonblocking) + { + if (bytes >= 0) + { + /* + * 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 if (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + rv = 0; + fNeedContinue = PR_TRUE; + } + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)osiov; + op.arg3.amount = osiov_len; + op.timeout = timeout; + op.result.code = rv; + op.function = pt_writev_cont; + op.event = POLLOUT | POLLPRI; + rv = pt_Continue(&op); + syserrno = op.syserrno; + } + if (rv == -1) { + pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno); + } + return rv; +} /* pt_Writev */ + +static PRInt32 pt_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + return _PR_MD_LSEEK(fd, offset, whence); +} /* pt_Seek */ + +static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + return _PR_MD_LSEEK64(fd, offset, whence); +} /* pt_Seek64 */ + +static PRInt32 pt_Available_f(PRFileDesc *fd) +{ + PRInt32 result, cur, end; + + cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR); + + if (cur >= 0) { + end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END); + } + + if ((cur < 0) || (end < 0)) { + return -1; + } + + result = end - cur; + _PR_MD_LSEEK(fd, cur, PR_SEEK_SET); + + return result; +} /* pt_Available_f */ + +static PRInt64 pt_Available64_f(PRFileDesc *fd) +{ + PRInt64 result, cur, end; + PRInt64 minus_one; + + LL_I2L(minus_one, -1); + cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR); + + if (LL_GE_ZERO(cur)) { + end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END); + } + + if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) { + return minus_one; + } + + LL_SUB(result, end, cur); + (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET); + + return result; +} /* pt_Available64_f */ + +static PRInt32 pt_Available_s(PRFileDesc *fd) +{ + PRInt32 rv, bytes = -1; + if (pt_TestAbort()) { + return bytes; + } + + rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno); + } + return bytes; +} /* pt_Available_s */ + +static PRInt64 pt_Available64_s(PRFileDesc *fd) +{ + PRInt64 rv; + LL_I2L(rv, pt_Available_s(fd)); + return rv; +} /* pt_Available64_s */ + +static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info) +{ + PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, info); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_FileInfo */ + +static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + PRInt32 rv = _PR_MD_GETOPENFILEINFO64(fd, info); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_FileInfo64 */ + +static PRStatus pt_Synch(PRFileDesc *fd) +{ + return (NULL == fd) ? PR_FAILURE : PR_SUCCESS; +} /* pt_Synch */ + +static PRStatus pt_Fsync(PRFileDesc *fd) +{ + PRIntn rv = -1; + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = fsync(fd->secret->md.osfd); + if (rv < 0) { + pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Fsync */ + +static PRStatus pt_Connect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRIntn rv = -1, syserrno; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + addr_len = PR_NETADDR_SIZE(addr); +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + +#ifdef _PR_HAVE_SOCKADDR_LEN + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + rv = connect(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len); + syserrno = errno; + if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)addrp; + op.arg3.amount = addr_len; + op.timeout = timeout; + op.function = pt_connect_cont; + op.event = POLLOUT | POLLPRI; + rv = pt_Continue(&op); + syserrno = op.syserrno; + } + } + if (-1 == rv) { + pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Connect */ + +static PRStatus pt_ConnectContinue( + PRFileDesc *fd, PRInt16 out_flags) +{ + int err; + PRInt32 osfd; + + if (out_flags & PR_POLL_NVAL) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if ((out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR + | PR_POLL_HUP)) == 0) + { + PR_ASSERT(out_flags == 0); + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + + osfd = fd->secret->md.osfd; + + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) + { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_ConnectContinue */ + +PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) +{ + /* Find the NSPR layer and invoke its connectcontinue method */ + PRFileDesc *bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); + + if (NULL == bottom) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + return pt_ConnectContinue(bottom, pd->out_flags); +} /* PR_GetConnectStatus */ + +static PRFileDesc* pt_Accept( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRFileDesc *newfd = NULL; + PRIntn syserrno, osfd = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return newfd; + } + +#ifdef _PR_STRICT_ADDR_LEN + if (addr) + { + /* + * Set addr->raw.family just so that we can use the + * PR_NETADDR_SIZE macro. + */ + addr->raw.family = fd->secret->af; + addr_len = PR_NETADDR_SIZE(addr); + } +#endif + + osfd = accept(fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + syserrno = errno; + + if (osfd == -1) + { + if (fd->secret->nonblocking) { + goto failed; + } + + if (EWOULDBLOCK != syserrno && EAGAIN != syserrno + && ECONNABORTED != syserrno) { + goto failed; + } + else + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = addr; + op.arg3.addr_len = &addr_len; + op.timeout = timeout; + op.function = pt_accept_cont; + op.event = POLLIN | POLLPRI; + osfd = pt_Continue(&op); + syserrno = op.syserrno; + } + if (osfd < 0) { + goto failed; + } + } + } +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) { + addr->raw.family = PR_AF_INET6; + } +#endif + newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_TRUE, PR_FALSE); + if (newfd == NULL) { + close(osfd); /* $$$ whoops! this doesn't work $$$ */ + } + else + { + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); +#ifdef LINUX + /* + * On Linux, experiments showed that the accepted sockets + * inherit the TCP_NODELAY socket option of the listening + * socket. + */ + newfd->secret->md.tcp_nodelay = fd->secret->md.tcp_nodelay; +#endif + } + return newfd; + +failed: + pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno); + return NULL; +} /* pt_Accept */ + +static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr) +{ + PRIntn rv; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + if (addr->raw.family == AF_UNIX) + { + /* Disallow relative pathnames */ + if (addr->local.path[0] != '/' +#if defined(LINUX) + /* Linux has abstract socket address support */ + && addr->local.path[0] != 0 +#endif + ) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + } + +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + + addr_len = PR_NETADDR_SIZE(addr); +#ifdef _PR_HAVE_SOCKADDR_LEN + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + rv = bind(fd->secret->md.osfd, (struct sockaddr*)addrp, addr_len); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_BIND_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Bind */ + +static PRStatus pt_Listen(PRFileDesc *fd, PRIntn backlog) +{ + PRIntn rv; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = listen(fd->secret->md.osfd, backlog); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Listen */ + +static PRStatus pt_Shutdown(PRFileDesc *fd, PRIntn how) +{ + PRIntn rv = -1; + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = shutdown(fd->secret->md.osfd, how); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Shutdown */ + +static PRInt16 pt_Poll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + *out_flags = 0; + return in_flags; +} /* pt_Poll */ + +static PRInt32 pt_Recv( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRIntn osflags; + + if (0 == flags) { + osflags = 0; + } + else if (PR_MSG_PEEK == flags) + { + osflags = MSG_PEEK; + } + else + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return bytes; + } + + if (pt_TestAbort()) { + return bytes; + } + + /* recv() is a much slower call on pre-2.6 Solaris than read(). */ +#if defined(SOLARIS) + if (0 == osflags) { + bytes = read(fd->secret->md.osfd, buf, amount); + } + else { + bytes = recv(fd->secret->md.osfd, buf, amount, osflags); + } +#else + bytes = recv(fd->secret->md.osfd, buf, amount, osflags); +#endif + syserrno = errno; + + if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.arg4.flags = osflags; + op.timeout = timeout; + op.function = pt_recv_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno); + } + return bytes; +} /* pt_Recv */ + +static PRInt32 pt_SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return pt_Recv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} /* pt_SocketRead */ + +static PRInt32 pt_Send( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; +#if defined(SOLARIS) + PRInt32 tmp_amount = amount; +#endif + + if (pt_TestAbort()) { + return bytes; + } + + /* + * On pre-2.6 Solaris, send() is much slower than write(). + * On 2.6 and beyond, with in-kernel sockets, send() and + * write() are fairly equivalent in performance. + */ +#if defined(SOLARIS) + PR_ASSERT(0 == flags); +retry: + bytes = write(fd->secret->md.osfd, buf, tmp_amount); +#else + bytes = send(fd->secret->md.osfd, buf, amount, flags); +#endif + syserrno = errno; + +#if defined(SOLARIS) + /* + * The write system call has been reported to return the ERANGE error + * on occasion. Try to write in smaller chunks to workaround this bug. + */ + if ((bytes == -1) && (syserrno == ERANGE)) + { + if (tmp_amount > 1) + { + tmp_amount = tmp_amount/2; /* half the bytes */ + goto retry; + } + } +#endif + + if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) + { + bytes = -1; + syserrno = ETIMEDOUT; + } + else + { + buf = (char *) buf + bytes; + amount -= bytes; + fNeedContinue = PR_TRUE; + } + } + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else + { + bytes = 0; + fNeedContinue = PR_TRUE; + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.timeout = timeout; + op.result.code = bytes; /* initialize the number sent */ + op.function = pt_send_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes == -1) { + pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno); + } + return bytes; +} /* pt_Send */ + +static PRInt32 pt_SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return pt_Send(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} /* pt_SocketWrite */ + +static PRInt32 pt_SendTo( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return bytes; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); +#ifdef _PR_INET6 + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + + addr_len = PR_NETADDR_SIZE(addr); +#ifdef _PR_HAVE_SOCKADDR_LEN + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + bytes = sendto( + fd->secret->md.osfd, buf, amount, flags, + (struct sockaddr*)addrp, addr_len); + syserrno = errno; + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addrp; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); + } + return bytes; +} /* pt_SendTo */ + +#if defined(LINUX) || defined(DARWIN) +/* Linux uses SendTo to send data during TCP Fast Open. OSX uses connectx, but + * we will make it imitate the Linux's interface. */ +static PRInt32 pt_TCP_SendTo( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ +#if defined(LINUX) || HAS_CONNECTX + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + pt_SockLen addr_len; + const PRNetAddr *addrp = addr; +#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6) + PRNetAddr addrCopy; +#endif +#ifdef _PR_HAVE_SOCKADDR_LEN + PRUint16 md_af = addr->raw.family; +#endif + + if (pt_TestAbort()) { + return bytes; + } + + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + addr_len = PR_NETADDR_SIZE(addr); +#if defined(_PR_INET6) + if (addr->raw.family == PR_AF_INET6) { +#ifdef _PR_HAVE_SOCKADDR_LEN + md_af = AF_INET6; +#else + /* If _PR_INET6 is defined and it is PR_AF_INET6 we set family + * to AF_INET6. */ + addrCopy = *addr; + addrCopy.raw.family = AF_INET6; + addrp = &addrCopy; +#endif + } +#endif + +#ifdef _PR_HAVE_SOCKADDR_LEN + /* if _PR_HAVE_SOCKADDR_LEN is defined and it is PR_AF_INET6 we set family + * to AF_INET6 and we set address length. */ + addrCopy = *addr; + ((struct sockaddr*)&addrCopy)->sa_len = addr_len; + ((struct sockaddr*)&addrCopy)->sa_family = md_af; + addrp = &addrCopy; +#endif + +#ifndef HAS_CONNECTX + bytes = sendto( + fd->secret->md.osfd, buf, amount, MSG_FASTOPEN, + (struct sockaddr*)addrp, addr_len); +#else + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = (struct sockaddr *)addrp; + endpoints.sae_dstaddrlen = addr_len; + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = amount; + PRInt32 rv = connectx(fd->secret->md.osfd, &endpoints, SAE_ASSOCID_ANY, + CONNECT_DATA_IDEMPOTENT, iov, 1, &bytes, NULL); +#endif + syserrno = errno; + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + if (fNeedContinue == PR_TRUE) { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addrp; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) { + pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); + } + return bytes; +#else /* !HAS_CONNECTX */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#endif +} /* pt_TCP_SendTo */ +#endif /* LINUX || DARWIN */ + +static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRBool fNeedContinue = PR_FALSE; + PRInt32 syserrno, bytes = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return bytes; + } + + bytes = recvfrom( + fd->secret->md.osfd, buf, amount, flags, + (struct sockaddr*)addr, &addr_len); + syserrno = errno; + + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) { + syserrno = ETIMEDOUT; + } + else { + fNeedContinue = PR_TRUE; + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = addr; + op.timeout = timeout; + op.function = pt_recvfrom_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes >= 0) + { +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (addr && (AF_INET6 == addr->raw.family)) { + addr->raw.family = PR_AF_INET6; + } +#endif + } + else { + pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno); + } + return bytes; +} /* pt_RecvFrom */ + +#ifdef AIX +#ifndef HAVE_SEND_FILE +static pthread_once_t pt_aix_sendfile_once_block = PTHREAD_ONCE_INIT; + +static void pt_aix_sendfile_init_routine(void) +{ + void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); + pt_aix_sendfile_fptr = (ssize_t (*)()) dlsym(handle, "send_file"); + dlclose(handle); +} + +/* + * pt_AIXDispatchSendFile + */ +static PRInt32 pt_AIXDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + int rv; + + rv = pthread_once(&pt_aix_sendfile_once_block, + pt_aix_sendfile_init_routine); + PR_ASSERT(0 == rv); + if (pt_aix_sendfile_fptr) { + return pt_AIXSendFile(sd, sfd, flags, timeout); + } else { + return PR_EmulateSendFile(sd, sfd, flags, timeout); + } +} +#endif /* !HAVE_SEND_FILE */ + + +/* + * pt_AIXSendFile + * + * 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 + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the send_file() system + * call available in AIX 4.3.2. + */ + +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; + unsigned long long saved_file_offset; + long long saved_file_bytes; + + 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 = 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; + + saved_file_offset = sf_struct.file_offset; + saved_file_bytes = sf_struct.file_bytes; + + send_flags = 0; /* flags processed at the end */ + + /* The first argument to send_file() is int*. */ + PR_ASSERT(sizeof(int) == sizeof(sd->secret->md.osfd)); + do { + rv = AIX_SEND_FILE(&sd->secret->md.osfd, &sf_struct, send_flags); + } while (rv == -1 && (syserrno = errno) == EINTR); + + if (rv == -1) { + if (syserrno == EAGAIN || syserrno == EWOULDBLOCK) { + count = 0; /* Not a real error. Need to continue. */ + } else { + count = -1; + } + } 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))) { + pt_Continuation op; + + op.arg1.osfd = sd->secret->md.osfd; + op.arg2.buffer = &sf_struct; + op.arg4.flags = send_flags; + op.result.code = count; + op.timeout = timeout; + op.function = pt_aix_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + + if (count == -1) { + pt_MapError(_MD_aix_map_sendfile_error, syserrno); + return -1; + } + 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_HPUXSendFile + * + * 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 + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in HP-UX B.11.00. + */ + +static PRInt32 pt_HPUXSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + 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; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = 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 + * would prevent the subsequent sendfile() calls on that socket + * from working. So we don't use the SD_DISCONNECT flag. + */ + send_flags = 0; + + do { + count = sendfile(sd->secret->md.osfd, 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)) { + count = 0; + } + if (count != -1 && count < nbytes_to_send) { + pt_Continuation op; + + 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.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.arg1.osfd = sd->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_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + + if (count == -1) { + pt_MapError(_MD_hpux_map_sendfile_error, syserrno); + return -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == nbytes_to_send); + return count; +} + +#endif /* HPUX11 */ + +#ifdef SOLARIS + +/* + * pt_SolarisSendFile + * + * 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 + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfilev() system + * call available in Solaris 8. + */ + +static PRInt32 pt_SolarisSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + size_t nbytes_to_send, file_nbytes_to_send; + struct sendfilevec sfv_struct[3]; + int sfvcnt = 0; + size_t xferred; + PRInt32 count; + int syserrno; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = sfd->file_nbytes; + } + + nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send; + + if (sfd->hlen != 0) { + sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF; + sfv_struct[sfvcnt].sfv_flag = 0; + sfv_struct[sfvcnt].sfv_off = (off_t) sfd->header; + sfv_struct[sfvcnt].sfv_len = sfd->hlen; + sfvcnt++; + } + + if (file_nbytes_to_send != 0) { + sfv_struct[sfvcnt].sfv_fd = sfd->fd->secret->md.osfd; + sfv_struct[sfvcnt].sfv_flag = 0; + sfv_struct[sfvcnt].sfv_off = sfd->file_offset; + sfv_struct[sfvcnt].sfv_len = file_nbytes_to_send; + sfvcnt++; + } + + if (sfd->tlen != 0) { + sfv_struct[sfvcnt].sfv_fd = SFV_FD_SELF; + sfv_struct[sfvcnt].sfv_flag = 0; + sfv_struct[sfvcnt].sfv_off = (off_t) sfd->trailer; + sfv_struct[sfvcnt].sfv_len = sfd->tlen; + sfvcnt++; + } + + if (0 == sfvcnt) { + count = 0; + goto done; + } + + /* + * Strictly speaking, we may have sent some bytes when the + * sendfilev() is interrupted and we should retry it from an + * updated offset. We are not doing that here. + */ + count = SOLARIS_SENDFILEV(sd->secret->md.osfd, sfv_struct, + sfvcnt, &xferred); + + PR_ASSERT((count == -1) || (count == xferred)); + + if (count == -1) { + syserrno = errno; + if (syserrno == EINTR + || syserrno == EAGAIN || syserrno == EWOULDBLOCK) { + count = xferred; + } + } else if (count == 0) { + /* + * We are now at EOF. The file was truncated. Solaris sendfile is + * supposed to return 0 and no error in this case, though some versions + * may return -1 and EINVAL . + */ + count = -1; + syserrno = 0; /* will be treated as EOF */ + } + + if (count != -1 && count < nbytes_to_send) { + pt_Continuation op; + struct sendfilevec *vec = sfv_struct; + PRInt32 rem = count; + + while (rem >= vec->sfv_len) { + rem -= vec->sfv_len; + vec++; + sfvcnt--; + } + PR_ASSERT(sfvcnt > 0); + + vec->sfv_off += rem; + vec->sfv_len -= rem; + PR_ASSERT(vec->sfv_len > 0); + + op.arg1.osfd = sd->secret->md.osfd; + op.arg2.buffer = vec; + op.arg3.amount = sfvcnt; + op.arg4.flags = 0; + op.nbytes_to_send = nbytes_to_send - count; + op.result.code = count; + op.timeout = timeout; + op.function = pt_solaris_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + +done: + if (count == -1) { + pt_MapError(_MD_solaris_map_sendfile_error, syserrno); + return -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == nbytes_to_send); + return count; +} + +#ifndef HAVE_SENDFILEV +static pthread_once_t pt_solaris_sendfilev_once_block = PTHREAD_ONCE_INIT; + +static void pt_solaris_sendfilev_init_routine(void) +{ + void *handle; + PRBool close_it = PR_FALSE; + + /* + * We do not want to unload libsendfile.so. This handle is leaked + * intentionally. + */ + handle = dlopen("libsendfile.so", RTLD_LAZY | RTLD_GLOBAL); + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("dlopen(libsendfile.so) returns %p", handle)); + + if (NULL == handle) { + /* + * The dlopen(0, mode) call is to allow for the possibility that + * sendfilev() may become part of a standard system library in a + * future Solaris release. + */ + handle = dlopen(0, RTLD_LAZY | RTLD_GLOBAL); + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("dlopen(0) returns %p", handle)); + close_it = PR_TRUE; + } + pt_solaris_sendfilev_fptr = (ssize_t (*)()) dlsym(handle, "sendfilev"); + PR_LOG(_pr_io_lm, PR_LOG_DEBUG, + ("dlsym(sendfilev) returns %p", pt_solaris_sendfilev_fptr)); + + if (close_it) { + dlclose(handle); + } +} + +/* + * pt_SolarisDispatchSendFile + */ +static PRInt32 pt_SolarisDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + int rv; + + rv = pthread_once(&pt_solaris_sendfilev_once_block, + pt_solaris_sendfilev_init_routine); + PR_ASSERT(0 == rv); + if (pt_solaris_sendfilev_fptr) { + return pt_SolarisSendFile(sd, sfd, flags, timeout); + } else { + return PR_EmulateSendFile(sd, sfd, flags, timeout); + } +} +#endif /* !HAVE_SENDFILEV */ + +#endif /* SOLARIS */ + +#ifdef LINUX +/* + * pt_LinuxSendFile + * + * 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 + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in Linux kernel 2.2 or higher. + */ + +static PRInt32 pt_LinuxSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + size_t file_nbytes_to_send; + PRInt32 count = 0; + ssize_t rv; + int syserrno; + off_t offset; + PRBool tcp_cork_enabled = PR_FALSE; + int tcp_cork; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = sfd->file_nbytes; + } + + if ((sfd->hlen != 0 || sfd->tlen != 0) + && sd->secret->md.tcp_nodelay == 0) { + tcp_cork = 1; + if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, + &tcp_cork, sizeof tcp_cork) == 0) { + tcp_cork_enabled = PR_TRUE; + } else { + syserrno = errno; + if (syserrno != EINVAL) { + _PR_MD_MAP_SETSOCKOPT_ERROR(syserrno); + return -1; + } + /* + * The most likely reason for the EINVAL error is that + * TCP_NODELAY is set (with a function other than + * PR_SetSocketOption). This is not fatal, so we keep + * on going. + */ + PR_LOG(_pr_io_lm, PR_LOG_WARNING, + ("pt_LinuxSendFile: " + "setsockopt(TCP_CORK) failed with EINVAL\n")); + } + } + + if (sfd->hlen != 0) { + count = PR_Send(sd, sfd->header, sfd->hlen, 0, timeout); + if (count == -1) { + goto failed; + } + } + + if (file_nbytes_to_send != 0) { + offset = sfd->file_offset; + do { + rv = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, + &offset, file_nbytes_to_send); + } while (rv == -1 && (syserrno = errno) == EINTR); + if (rv == -1) { + if (syserrno != EAGAIN && syserrno != EWOULDBLOCK) { + _MD_linux_map_sendfile_error(syserrno); + count = -1; + goto failed; + } + rv = 0; + } + PR_ASSERT(rv == offset - sfd->file_offset); + count += rv; + + if (rv < file_nbytes_to_send) { + pt_Continuation op; + + op.arg1.osfd = sd->secret->md.osfd; + op.in_fd = sfd->fd->secret->md.osfd; + op.offset = offset; + op.count = file_nbytes_to_send - rv; + op.result.code = count; + op.timeout = timeout; + op.function = pt_linux_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + if (count == -1) { + pt_MapError(_MD_linux_map_sendfile_error, syserrno); + goto failed; + } + } + } + + if (sfd->tlen != 0) { + rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); + if (rv == -1) { + count = -1; + goto failed; + } + count += rv; + } + +failed: + if (tcp_cork_enabled) { + tcp_cork = 0; + if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, + &tcp_cork, sizeof tcp_cork) == -1 && count != -1) { + _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + count = -1; + } + } + if (count != -1) { + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == sfd->hlen + sfd->tlen + file_nbytes_to_send); + } + return count; +} +#endif /* LINUX */ + +#ifdef AIX +extern int _pr_aix_send_file_use_disabled; +#endif + +static PRInt32 pt_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_EmulateSendFile(sd, sfd, flags, timeout)); + } + else { + return(pt_AIXSendFile(sd, sfd, flags, timeout)); + } +#else + return(PR_EmulateSendFile(sd, sfd, flags, timeout)); + /* return(pt_AIXDispatchSendFile(sd, sfd, flags, timeout));*/ +#endif /* HAVE_SEND_FILE */ +#elif defined(SOLARIS) +#ifdef HAVE_SENDFILEV + return(pt_SolarisSendFile(sd, sfd, flags, timeout)); +#else + return(pt_SolarisDispatchSendFile(sd, sfd, flags, timeout)); +#endif /* HAVE_SENDFILEV */ +#elif defined(LINUX) + return(pt_LinuxSendFile(sd, sfd, flags, timeout)); +#else + return(PR_EmulateSendFile(sd, sfd, flags, timeout)); +#endif +} + +static PRInt32 pt_TransmitFile( + PRFileDesc *sd, PRFileDesc *fd, const void *headers, + PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + PRSendFileData sfd; + + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; + + return(pt_SendFile(sd, &sfd, flags, timeout)); +} /* pt_TransmitFile */ + +static PRInt32 pt_AcceptRead( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + PRInt32 rv = -1; + + if (pt_TestAbort()) { + return rv; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return rv; + } + + rv = PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); + return rv; +} /* pt_AcceptRead */ + +static PRStatus pt_GetSockName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRIntn rv = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = getsockname( + fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno); + return PR_FAILURE; + } +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); + return PR_SUCCESS; +} /* pt_GetSockName */ + +static PRStatus pt_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRIntn rv = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = getpeername( + fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno); + return PR_FAILURE; + } +#ifdef _PR_HAVE_SOCKADDR_LEN + /* ignore the sa_len field of struct sockaddr */ + if (addr) + { + addr->raw.family = ((struct sockaddr*)addr)->sa_family; + } +#endif /* _PR_HAVE_SOCKADDR_LEN */ +#ifdef _PR_INET6 + if (AF_INET6 == addr->raw.family) { + addr->raw.family = PR_AF_INET6; + } +#endif + PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE); + PR_ASSERT(IsValidNetAddrLen(addr, addr_len) == PR_TRUE); + return PR_SUCCESS; +} /* pt_GetPeerName */ + +static PRStatus pt_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) +{ + PRIntn rv; + pt_SockLen length; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call + */ + if (PR_SockOpt_Nonblocking == data->option) + { + data->value.non_blocking = fd->secret->nonblocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + length = sizeof(linger); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char *) &linger, &length); + PR_ASSERT((-1 == rv) || (sizeof(linger) == length)); + data->value.linger.polarity = + (linger.l_onoff) ? PR_TRUE : PR_FALSE; + data->value.linger.linger = + PR_SecondsToInterval(linger.l_linger); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + case PR_SockOpt_Broadcast: + case PR_SockOpt_Reuseport: + { + PRIntn value; + length = sizeof(PRIntn); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_McastLoopback: + { + PRUint8 xbool; + length = sizeof(xbool); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&xbool, &length); + PR_ASSERT((-1 == rv) || (sizeof(xbool) == length)); + data->value.mcast_loopback = (0 == xbool) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value; + length = sizeof(PRIntn); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + data->value.recv_buffer_size = value; + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + length = sizeof(PRUintn); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.ip_ttl, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + break; + } + case PR_SockOpt_McastTimeToLive: + { + PRUint8 ttl; + length = sizeof(ttl); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&ttl, &length); + PR_ASSERT((-1 == rv) || (sizeof(ttl) == length)); + data->value.mcast_ttl = ttl; + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + length = sizeof(mreq); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&mreq, &length); + PR_ASSERT((-1 == rv) || (sizeof(mreq) == length)); + data->value.add_member.mcaddr.inet.ip = + mreq.imr_multiaddr.s_addr; + data->value.add_member.ifaddr.inet.ip = + mreq.imr_interface.s_addr; + break; + } + case PR_SockOpt_McastInterface: + { + length = sizeof(data->value.mcast_if.inet.ip); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.mcast_if.inet.ip, &length); + PR_ASSERT((-1 == rv) + || (sizeof(data->value.mcast_if.inet.ip) == length)); + break; + } + case PR_SockOpt_DontFrag: + { +#if !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + rv = PR_FAILURE; +#else + PRIntn value; + length = sizeof(value); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, + &length); +#if defined(DARWIN) + data->value.dont_fragment = value; +#else + data->value.dont_fragment = (value == IP_PMTUDISC_DO) ? 1 : 0; +#endif +#endif /* !(!defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + if (-1 == rv) { + _PR_MD_MAP_GETSOCKOPT_ERROR(errno); + } + } + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_GetSocketOption */ + +static PRStatus pt_SetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data) +{ + PRIntn rv; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt call. + */ + if (PR_SockOpt_Nonblocking == data->option) + { + fd->secret->nonblocking = data->value.non_blocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + linger.l_onoff = data->value.linger.polarity; + linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger); + rv = setsockopt( + fd->secret->md.osfd, level, name, (char*)&linger, sizeof(linger)); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + case PR_SockOpt_Broadcast: + case PR_SockOpt_Reuseport: + { + PRIntn value = (data->value.reuse_addr) ? 1 : 0; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&value, sizeof(PRIntn)); +#ifdef LINUX + /* for pt_LinuxSendFile */ + if (name == TCP_NODELAY && rv == 0) { + fd->secret->md.tcp_nodelay = value; + } +#endif + break; + } + case PR_SockOpt_McastLoopback: + { + PRUint8 xbool = data->value.mcast_loopback ? 1 : 0; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&xbool, sizeof(xbool)); + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value = data->value.recv_buffer_size; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&value, sizeof(PRIntn)); + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.ip_ttl, sizeof(PRUintn)); + break; + } + case PR_SockOpt_McastTimeToLive: + { + PRUint8 ttl = data->value.mcast_ttl; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&ttl, sizeof(ttl)); + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = + data->value.add_member.mcaddr.inet.ip; + mreq.imr_interface.s_addr = + data->value.add_member.ifaddr.inet.ip; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&mreq, sizeof(mreq)); + break; + } + case PR_SockOpt_McastInterface: + { + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.mcast_if.inet.ip, + sizeof(data->value.mcast_if.inet.ip)); + break; + } + case PR_SockOpt_DontFrag: + { +#if !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID) + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + rv = PR_FAILURE; +#else + PRIntn value; +#if defined(LINUX) || defined(ANDROID) + value = (data->value.dont_fragment) ? IP_PMTUDISC_DO + : IP_PMTUDISC_DONT; +#elif defined(DARWIN) + value = data->value.dont_fragment; +#endif + rv = setsockopt( + fd->secret->md.osfd, level, name, (char*)&value, + sizeof(value)); +#endif /* !defined(DARWIN) && !defined(LINUX) && !defined(ANDROID)) */ + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + if (-1 == rv) { + _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + } + } + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_SetSocketOption */ + +/*****************************************************************************/ +/****************************** I/O method objects ***************************/ +/*****************************************************************************/ + +static PRIOMethods _pr_file_methods = { + PR_DESC_FILE, + pt_Close, + pt_Read, + pt_Write, + pt_Available_f, + pt_Available64_f, + pt_Fsync, + pt_Seek, + pt_Seek64, + pt_FileInfo, + pt_FileInfo64, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_pipe_methods = { + PR_DESC_PIPE, + pt_Close, + pt_Read, + pt_Write, + pt_Available_s, + pt_Available64_s, + pt_Synch, + (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, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_tcp_methods = { + PR_DESC_SOCKET_TCP, + pt_Close, + pt_SocketRead, + pt_SocketWrite, + pt_Available_s, + pt_Available64_s, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + pt_Writev, + pt_Connect, + pt_Accept, + pt_Bind, + pt_Listen, + pt_Shutdown, + pt_Recv, + pt_Send, + (PRRecvfromFN)_PR_InvalidInt, +#if defined(LINUX) || defined(DARWIN) + pt_TCP_SendTo, /* This is for TCP Fast Open. Linux uses SendTo function for this. OSX uses connectx, but we imitate Linux. */ +#else + (PRSendtoFN)_PR_InvalidInt, +#endif + pt_Poll, + pt_AcceptRead, + pt_TransmitFile, + pt_GetSockName, + pt_GetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + pt_GetSocketOption, + pt_SetSocketOption, + pt_SendFile, + pt_ConnectContinue, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_udp_methods = { + PR_DESC_SOCKET_UDP, + pt_Close, + pt_SocketRead, + pt_SocketWrite, + pt_Available_s, + pt_Available64_s, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + pt_Writev, + pt_Connect, + (PRAcceptFN)_PR_InvalidDesc, + pt_Bind, + pt_Listen, + pt_Shutdown, + pt_Recv, + pt_Send, + pt_RecvFrom, + pt_SendTo, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + pt_GetSockName, + pt_GetPeerName, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + pt_GetSocketOption, + pt_SetSocketOption, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +static PRIOMethods _pr_socketpollfd_methods = { + (PRDescType) 0, + (PRCloseFN)_PR_InvalidStatus, + (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, + pt_Poll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt, + (PRReservedFN)_PR_InvalidInt +}; + +#if defined(HPUX) || defined(SOLARIS) \ + || defined(LINUX) || defined(__GNU__) || defined(__GLIBC__) \ + || defined(AIX) || defined(FREEBSD) || defined(NETBSD) \ + || defined(OPENBSD) || defined(BSDI) || defined(NTO) \ + || defined(DARWIN) || defined(UNIXWARE) || defined(RISCOS) +#define _PR_FCNTL_FLAGS O_NONBLOCK +#else +#error "Can't determine architecture" +#endif + +/* + * Put a Unix file descriptor in non-blocking mode. + */ +static void pt_MakeFdNonblock(PRIntn osfd) +{ + PRIntn flags; + flags = fcntl(osfd, F_GETFL, 0); + flags |= _PR_FCNTL_FLAGS; + (void)fcntl(osfd, F_SETFL, flags); +} + +/* + * Put a Unix socket fd in non-blocking mode that can + * ideally be inherited by an accepted socket. + * + * Why doesn't pt_MakeFdNonblock do? This is to deal with + * the special case of HP-UX. HP-UX has three kinds of + * non-blocking modes for sockets: the fcntl() O_NONBLOCK + * and O_NDELAY flags and ioctl() FIOSNBIO request. Only + * the ioctl() FIOSNBIO form of non-blocking mode is + * inherited by an accepted socket. + * + * Other platforms just use the generic pt_MakeFdNonblock + * to put a socket in non-blocking mode. + */ +#ifdef HPUX +static void pt_MakeSocketNonblock(PRIntn osfd) +{ + PRIntn one = 1; + (void)ioctl(osfd, FIOSNBIO, &one); +} +#else +#define pt_MakeSocketNonblock pt_MakeFdNonblock +#endif + +static PRFileDesc *pt_SetMethods( + PRIntn osfd, PRDescType type, PRBool isAcceptedSocket, PRBool imported) +{ + PRFileDesc *fd = _PR_Getfd(); + + if (fd == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->secret->md.osfd = osfd; + fd->secret->state = _PR_FILEDESC_OPEN; + if (imported) { + fd->secret->inheritable = _PR_TRI_UNKNOWN; + } + else + { + /* By default, a Unix fd is not closed on exec. */ +#ifdef DEBUG + PRIntn flags; + flags = fcntl(osfd, F_GETFD, 0); + PR_ASSERT(0 == flags); +#endif + fd->secret->inheritable = _PR_TRI_TRUE; + } + switch (type) + { + case PR_DESC_FILE: + fd->methods = PR_GetFileMethods(); + break; + case PR_DESC_SOCKET_TCP: + fd->methods = PR_GetTCPMethods(); +#ifdef _PR_ACCEPT_INHERIT_NONBLOCK + if (!isAcceptedSocket) { + pt_MakeSocketNonblock(osfd); + } +#else + pt_MakeSocketNonblock(osfd); +#endif + break; + case PR_DESC_SOCKET_UDP: + fd->methods = PR_GetUDPMethods(); + pt_MakeFdNonblock(osfd); + break; + case PR_DESC_PIPE: + fd->methods = PR_GetPipeMethods(); + pt_MakeFdNonblock(osfd); + break; + default: + break; + } + } + return fd; +} /* pt_SetMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetFileMethods(void) +{ + return &_pr_file_methods; +} /* PR_GetFileMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetPipeMethods(void) +{ + return &_pr_pipe_methods; +} /* PR_GetPipeMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetTCPMethods(void) +{ + return &_pr_tcp_methods; +} /* PR_GetTCPMethods */ + +PR_IMPLEMENT(const PRIOMethods*) PR_GetUDPMethods(void) +{ + return &_pr_udp_methods; +} /* PR_GetUDPMethods */ + +static const PRIOMethods* PR_GetSocketPollFdMethods(void) +{ + return &_pr_socketpollfd_methods; +} /* PR_GetSocketPollFdMethods */ + +PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc( + PRInt32 osfd, const PRIOMethods *methods) +{ + PRFileDesc *fd = _PR_Getfd(); + + if (NULL == fd) { + goto failed; + } + + fd->methods = methods; + fd->secret->md.osfd = osfd; + /* Make fd non-blocking */ + if (osfd > 2) + { + /* Don't mess around with stdin, stdout or stderr */ + if (&_pr_tcp_methods == methods) { + pt_MakeSocketNonblock(osfd); + } + else { + pt_MakeFdNonblock(osfd); + } + } + fd->secret->state = _PR_FILEDESC_OPEN; + fd->secret->inheritable = _PR_TRI_UNKNOWN; + return fd; + +failed: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return fd; +} /* PR_AllocFileDesc */ + +#if !defined(_PR_INET6) || defined(_PR_INET6_PROBE) +PR_EXTERN(PRStatus) _pr_push_ipv6toipv4_layer(PRFileDesc *fd); +#if defined(_PR_INET6_PROBE) +extern PRBool _pr_ipv6_is_present(void); +PR_IMPLEMENT(PRBool) _pr_test_ipv6_socket() +{ + int osfd; + +#if defined(DARWIN) + /* + * Disable IPv6 if Darwin version is less than 7.0.0 (OS X 10.3). IPv6 on + * lesser versions is not ready for general use (see bug 222031). + */ + { + struct utsname u; + if (uname(&u) != 0 || atoi(u.release) < 7) { + return PR_FALSE; + } + } +#endif + + /* + * HP-UX only: HP-UX IPv6 Porting Guide (dated February 2001) + * suggests that we call open("/dev/ip6", O_RDWR) to determine + * whether IPv6 APIs and the IPv6 stack are on the system. + * Our portable test below seems to work fine, so I am using it. + */ + osfd = socket(AF_INET6, SOCK_STREAM, 0); + if (osfd != -1) { + close(osfd); + return PR_TRUE; + } + return PR_FALSE; +} +#endif /* _PR_INET6_PROBE */ +#endif + +PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRIntn osfd; + PRDescType ftype; + PRFileDesc *fd = NULL; +#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) + PRInt32 tmp_domain = domain; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (pt_TestAbort()) { + return NULL; + } + + if (PF_INET != domain + && PR_AF_INET6 != domain +#if defined(_PR_HAVE_SDP) + && PR_AF_INET_SDP != domain +#if defined(SOLARIS) + && PR_AF_INET6_SDP != domain +#endif /* SOLARIS */ +#endif /* _PR_HAVE_SDP */ + && PF_UNIX != domain) + { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return fd; + } + if (type == SOCK_STREAM) { + ftype = PR_DESC_SOCKET_TCP; + } + else if (type == SOCK_DGRAM) { + ftype = PR_DESC_SOCKET_UDP; + } + else + { + (void)PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return fd; + } +#if defined(_PR_HAVE_SDP) +#if defined(LINUX) + if (PR_AF_INET_SDP == domain) { + domain = AF_INET_SDP; + } +#elif defined(SOLARIS) + if (PR_AF_INET_SDP == domain) { + domain = AF_INET; + proto = PROTO_SDP; + } else if(PR_AF_INET6_SDP == domain) { + domain = AF_INET6; + proto = PROTO_SDP; + } +#endif /* SOLARIS */ +#endif /* _PR_HAVE_SDP */ +#if defined(_PR_INET6_PROBE) + if (PR_AF_INET6 == domain) { + domain = _pr_ipv6_is_present() ? AF_INET6 : AF_INET; + } +#elif defined(_PR_INET6) + if (PR_AF_INET6 == domain) { + domain = AF_INET6; + } +#else + if (PR_AF_INET6 == domain) { + domain = AF_INET; + } +#endif + + osfd = socket(domain, type, proto); + if (osfd == -1) { + pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno); + } + else + { +#ifdef _PR_IPV6_V6ONLY_PROBE + if ((domain == AF_INET6) && _pr_ipv6_v6only_on_by_default) + { + int on = 0; + (void)setsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, + &on, sizeof(on)); + } +#endif + fd = pt_SetMethods(osfd, ftype, PR_FALSE, PR_FALSE); + if (fd == NULL) { + close(osfd); + } + } +#ifdef _PR_NEED_SECRET_AF + if (fd != NULL) { + fd->secret->af = domain; + } +#endif +#if defined(_PR_INET6_PROBE) || !defined(_PR_INET6) + if (fd != NULL) { + /* + * For platforms with no support for IPv6 + * create layered socket for IPv4-mapped IPv6 addresses + */ + if (PR_AF_INET6 == tmp_domain && PR_AF_INET == domain) { + if (PR_FAILURE == _pr_push_ipv6toipv4_layer(fd)) { + PR_Close(fd); + fd = NULL; + } + } + } +#endif + return fd; +} /* PR_Socket */ + +/*****************************************************************************/ +/****************************** I/O public methods ***************************/ +/*****************************************************************************/ + +PR_IMPLEMENT(PRFileDesc*) PR_OpenFile( + const char *name, PRIntn flags, PRIntn mode) +{ + PRFileDesc *fd = NULL; + PRIntn syserrno, osfd = -1, osflags = 0;; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (pt_TestAbort()) { + return NULL; + } + + if (flags & PR_RDONLY) { + osflags |= O_RDONLY; + } + if (flags & PR_WRONLY) { + osflags |= O_WRONLY; + } + if (flags & PR_RDWR) { + osflags |= O_RDWR; + } + if (flags & PR_APPEND) { + osflags |= O_APPEND; + } + if (flags & PR_TRUNCATE) { + osflags |= O_TRUNC; + } + if (flags & PR_EXCL) { + osflags |= O_EXCL; + } + if (flags & PR_SYNC) + { +#if defined(O_SYNC) + osflags |= O_SYNC; +#elif defined(O_FSYNC) + osflags |= O_FSYNC; +#else +#error "Neither O_SYNC nor O_FSYNC is defined on this platform" +#endif + } + + /* + ** We have to hold the lock across the creation in order to + ** enforce the sematics of PR_Rename(). (see the latter for + ** more details) + */ + if (flags & PR_CREATE_FILE) + { + osflags |= O_CREAT; + if (NULL !=_pr_rename_lock) { + PR_Lock(_pr_rename_lock); + } + } + + osfd = _md_iovector._open64(name, osflags, mode); + syserrno = errno; + + if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock)) { + PR_Unlock(_pr_rename_lock); + } + + if (osfd == -1) { + pt_MapError(_PR_MD_MAP_OPEN_ERROR, syserrno); + } + else + { + fd = pt_SetMethods(osfd, PR_DESC_FILE, PR_FALSE, PR_FALSE); + if (fd == NULL) { + close(osfd); /* $$$ whoops! this is bad $$$ */ + } + } + return fd; +} /* PR_OpenFile */ + +PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode) +{ + return PR_OpenFile(name, flags, mode); +} /* PR_Open */ + +PR_IMPLEMENT(PRStatus) PR_Delete(const char *name) +{ + PRIntn rv = -1; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = unlink(name); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_UNLINK_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* PR_Delete */ + +PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how) +{ + PRIntn rv; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + switch (how) + { + case PR_ACCESS_READ_OK: + rv = access(name, R_OK); + break; + case PR_ACCESS_WRITE_OK: + rv = access(name, W_OK); + break; + case PR_ACCESS_EXISTS: + default: + rv = access(name, F_OK); + } + if (0 == rv) { + return PR_SUCCESS; + } + pt_MapError(_PR_MD_MAP_ACCESS_ERROR, errno); + return PR_FAILURE; + +} /* PR_Access */ + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info) +{ + PRInt32 rv = _PR_MD_GETFILEINFO(fn, info); + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_GetFileInfo */ + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info) +{ + PRInt32 rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + rv = _PR_MD_GETFILEINFO64(fn, info); + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_GetFileInfo64 */ + +PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to) +{ + PRIntn rv = -1; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + /* + ** We have to acquire a lock here to stiffle anybody trying to create + ** a new file at the same time. And we have to hold that lock while we + ** test to see if the file exists and do the rename. The other place + ** where the lock is held is in PR_Open() when possibly creating a + ** new file. + */ + + PR_Lock(_pr_rename_lock); + rv = access(to, F_OK); + if (0 == rv) + { + PR_SetError(PR_FILE_EXISTS_ERROR, 0); + rv = -1; + } + else + { + rv = rename(from, to); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_RENAME_ERROR, errno); + } + } + PR_Unlock(_pr_rename_lock); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* PR_Rename */ + +PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir) +{ + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (NULL != dir->md.d) + { + if (closedir(dir->md.d) == -1) + { + _PR_MD_MAP_CLOSEDIR_ERROR(errno); + return PR_FAILURE; + } + dir->md.d = NULL; + PR_DELETE(dir); + } + return PR_SUCCESS; +} /* PR_CloseDir */ + +PR_IMPLEMENT(PRStatus) PR_MakeDir(const char *name, PRIntn mode) +{ + PRInt32 rv = -1; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + /* + ** This lock is used to enforce rename semantics as described + ** in PR_Rename. + */ + if (NULL !=_pr_rename_lock) { + PR_Lock(_pr_rename_lock); + } + rv = mkdir(name, mode); + if (-1 == rv) { + pt_MapError(_PR_MD_MAP_MKDIR_ERROR, errno); + } + if (NULL !=_pr_rename_lock) { + PR_Unlock(_pr_rename_lock); + } + + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* PR_Makedir */ + +PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode) +{ + return PR_MakeDir(name, mode); +} /* PR_Mkdir */ + +PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name) +{ + PRInt32 rv; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + rv = rmdir(name); + if (0 == rv) { + return PR_SUCCESS; + } + pt_MapError(_PR_MD_MAP_RMDIR_ERROR, errno); + return PR_FAILURE; +} /* PR_Rmdir */ + + +PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name) +{ + DIR *osdir; + PRDir *dir = NULL; + + if (pt_TestAbort()) { + return dir; + } + + osdir = opendir(name); + if (osdir == NULL) { + pt_MapError(_PR_MD_MAP_OPENDIR_ERROR, errno); + } + else + { + dir = PR_NEWZAP(PRDir); + if (dir) { + dir->md.d = osdir; + } + else { + (void)closedir(osdir); + } + } + return dir; +} /* PR_OpenDir */ + +static PRInt32 _pr_poll_with_poll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRInt32 ready = 0; + /* + * For restarting poll() if it is interrupted by a signal. + * We use these variables to figure out how much time has + * elapsed and how much of the timeout still remains. + */ + PRIntervalTime start = 0, elapsed, remaining; + + if (pt_TestAbort()) { + return -1; + } + + if (0 == npds) { + PR_Sleep(timeout); + } + else + { +#define STACK_POLL_DESC_COUNT 64 + struct pollfd stack_syspoll[STACK_POLL_DESC_COUNT]; + struct pollfd *syspoll; + PRIntn index, msecs; + + if (npds <= STACK_POLL_DESC_COUNT) + { + syspoll = stack_syspoll; + } + else + { + PRThread *me = PR_GetCurrentThread(); + if (npds > me->syspoll_count) + { + PR_Free(me->syspoll_list); + me->syspoll_list = + (struct pollfd*)PR_MALLOC(npds * sizeof(struct pollfd)); + if (NULL == me->syspoll_list) + { + me->syspoll_count = 0; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + me->syspoll_count = npds; + } + syspoll = me->syspoll_list; + } + + for (index = 0; index < npds; ++index) + { + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (pds[index].in_flags & PR_POLL_READ) + { + in_flags_read = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_WRITE, + &out_flags_read); + } + if (pds[index].in_flags & PR_POLL_WRITE) + { + in_flags_write = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_READ, + &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one is ready right now */ + if (0 == ready) + { + /* + * We will return without calling the system + * poll function. So zero the out_flags + * fields of all the poll descriptors before + * this one. + */ + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; + pds[index].out_flags = out_flags_read | out_flags_write; + } + else + { + /* now locate the NSPR layer at the bottom of the stack */ + PRFileDesc *bottom = PR_GetIdentitiesLayer( + pds[index].fd, PR_NSPR_IO_LAYER); + /* ignore a socket without PR_NSPR_IO_LAYER available */ + + pds[index].out_flags = 0; /* pre-condition */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + syspoll[index].fd = bottom->secret->md.osfd; + syspoll[index].events = 0; + if (in_flags_read & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_READ; + syspoll[index].events |= POLLIN; + } + if (in_flags_read & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_WRITE; + syspoll[index].events |= POLLOUT; + } + if (in_flags_write & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_READ; + syspoll[index].events |= POLLIN; + } + if (in_flags_write & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_WRITE; + syspoll[index].events |= POLLOUT; + } + if (pds[index].in_flags & PR_POLL_EXCEPT) { + syspoll[index].events |= POLLPRI; + } + } + } + else + { + if (0 == ready) + { + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pds[index].out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + /* make poll() ignore this entry */ + syspoll[index].fd = -1; + syspoll[index].events = 0; + pds[index].out_flags = 0; + } + } + if (0 == ready) + { + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: msecs = 0; break; + case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break; + default: + msecs = PR_IntervalToMilliseconds(timeout); + start = PR_IntervalNow(); + } + +retry: + ready = poll(syspoll, npds, msecs); + if (-1 == ready) + { + PRIntn oserror = errno; + + if (EINTR == oserror) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else if (timeout == PR_INTERVAL_NO_WAIT) { + ready = 0; /* don't retry, just time out */ + } + else + { + elapsed = (PRIntervalTime) (PR_IntervalNow() + - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + msecs = PR_IntervalToMilliseconds(remaining); + goto retry; + } + } + } + else + { + _PR_MD_MAP_POLL_ERROR(oserror); + } + } + else if (ready > 0) + { + for (index = 0; index < npds; ++index) + { + PRInt16 out_flags = 0; + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (0 != syspoll[index].revents) + { + if (syspoll[index].revents & POLLIN) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_READ) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_READ) + { + out_flags |= PR_POLL_WRITE; + } + } + if (syspoll[index].revents & POLLOUT) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_WRITE) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_WRITE) + { + out_flags |= PR_POLL_WRITE; + } + } + if (syspoll[index].revents & POLLPRI) { + out_flags |= PR_POLL_EXCEPT; + } + if (syspoll[index].revents & POLLERR) { + out_flags |= PR_POLL_ERR; + } + if (syspoll[index].revents & POLLNVAL) { + out_flags |= PR_POLL_NVAL; + } + if (syspoll[index].revents & POLLHUP) { + out_flags |= PR_POLL_HUP; + } + } + } + pds[index].out_flags = out_flags; + } + } + } + } + return ready; + +} /* _pr_poll_with_poll */ + +#if defined(_PR_POLL_WITH_SELECT) +/* + * HPUX report the POLLHUP event for a socket when the + * shutdown(SHUT_WR) operation is called for the remote end, even though + * the socket is still writeable. Use select(), instead of poll(), to + * workaround this problem. + */ +static PRInt32 _pr_poll_with_select( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRInt32 ready = 0; + /* + * For restarting select() if it is interrupted by a signal. + * We use these variables to figure out how much time has + * elapsed and how much of the timeout still remains. + */ + PRIntervalTime start = 0, elapsed, remaining; + + if (pt_TestAbort()) { + return -1; + } + + if (0 == npds) { + PR_Sleep(timeout); + } + else + { +#define STACK_POLL_DESC_COUNT 64 + int stack_selectfd[STACK_POLL_DESC_COUNT]; + int *selectfd; + fd_set rd, wr, ex, *rdp = NULL, *wrp = NULL, *exp = NULL; + struct timeval tv, *tvp; + PRIntn index, msecs, maxfd = 0; + + if (npds <= STACK_POLL_DESC_COUNT) + { + selectfd = stack_selectfd; + } + else + { + PRThread *me = PR_GetCurrentThread(); + if (npds > me->selectfd_count) + { + PR_Free(me->selectfd_list); + me->selectfd_list = (int *)PR_MALLOC(npds * sizeof(int)); + if (NULL == me->selectfd_list) + { + me->selectfd_count = 0; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + me->selectfd_count = npds; + } + selectfd = me->selectfd_list; + } + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + for (index = 0; index < npds; ++index) + { + PRInt16 in_flags_read = 0, in_flags_write = 0; + PRInt16 out_flags_read = 0, out_flags_write = 0; + + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (pds[index].in_flags & PR_POLL_READ) + { + in_flags_read = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_WRITE, + &out_flags_read); + } + if (pds[index].in_flags & PR_POLL_WRITE) + { + in_flags_write = (pds[index].fd->methods->poll)( + pds[index].fd, + pds[index].in_flags & ~PR_POLL_READ, + &out_flags_write); + } + if ((0 != (in_flags_read & out_flags_read)) + || (0 != (in_flags_write & out_flags_write))) + { + /* this one is ready right now */ + if (0 == ready) + { + /* + * We will return without calling the system + * poll function. So zero the out_flags + * fields of all the poll descriptors before + * this one. + */ + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; + pds[index].out_flags = out_flags_read | out_flags_write; + } + else + { + /* now locate the NSPR layer at the bottom of the stack */ + PRFileDesc *bottom = PR_GetIdentitiesLayer( + pds[index].fd, PR_NSPR_IO_LAYER); + /* ignore a socket without PR_NSPR_IO_LAYER available */ + + pds[index].out_flags = 0; /* pre-condition */ + if ((NULL != bottom) + && (_PR_FILEDESC_OPEN == bottom->secret->state)) + { + if (0 == ready) + { + PRBool add_to_rd = PR_FALSE; + PRBool add_to_wr = PR_FALSE; + PRBool add_to_ex = PR_FALSE; + + selectfd[index] = bottom->secret->md.osfd; + if (in_flags_read & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_READ; + add_to_rd = PR_TRUE; + } + if (in_flags_read & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_READ_SYS_WRITE; + add_to_wr = PR_TRUE; + } + if (in_flags_write & PR_POLL_READ) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_READ; + add_to_rd = PR_TRUE; + } + if (in_flags_write & PR_POLL_WRITE) + { + pds[index].out_flags |= + _PR_POLL_WRITE_SYS_WRITE; + add_to_wr = PR_TRUE; + } + if (pds[index].in_flags & PR_POLL_EXCEPT) + { + add_to_ex = PR_TRUE; + } + if ((selectfd[index] > maxfd) && + (add_to_rd || add_to_wr || add_to_ex)) + { + maxfd = selectfd[index]; + /* + * If maxfd is too large to be used with + * select, fall back to calling poll. + */ + if (maxfd >= FD_SETSIZE) { + break; + } + } + if (add_to_rd) + { + FD_SET(bottom->secret->md.osfd, &rd); + rdp = &rd; + } + if (add_to_wr) + { + FD_SET(bottom->secret->md.osfd, &wr); + wrp = ≀ + } + if (add_to_ex) + { + FD_SET(bottom->secret->md.osfd, &ex); + exp = &ex; + } + } + } + else + { + if (0 == ready) + { + int i; + for (i = 0; i < index; i++) + { + pds[i].out_flags = 0; + } + } + ready += 1; /* this will cause an abrupt return */ + pds[index].out_flags = PR_POLL_NVAL; /* bogii */ + } + } + } + else + { + pds[index].out_flags = 0; + } + } + if (0 == ready) + { + if (maxfd >= FD_SETSIZE) + { + /* + * maxfd too large to be used with select, fall back to + * calling poll + */ + return(_pr_poll_with_poll(pds, npds, timeout)); + } + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: + tv.tv_sec = 0; + tv.tv_usec = 0; + tvp = &tv; + break; + case PR_INTERVAL_NO_TIMEOUT: + tvp = NULL; + break; + default: + msecs = PR_IntervalToMilliseconds(timeout); + tv.tv_sec = msecs/PR_MSEC_PER_SEC; + tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * PR_USEC_PER_MSEC; + tvp = &tv; + start = PR_IntervalNow(); + } + +retry: + ready = select(maxfd + 1, rdp, wrp, exp, tvp); + if (-1 == ready) + { + PRIntn oserror = errno; + + if ((EINTR == oserror) || (EAGAIN == oserror)) + { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } + else if (timeout == PR_INTERVAL_NO_WAIT) { + ready = 0; /* don't retry, just time out */ + } + else + { + elapsed = (PRIntervalTime) (PR_IntervalNow() + - start); + if (elapsed > timeout) { + ready = 0; /* timed out */ + } + else + { + remaining = timeout - elapsed; + msecs = PR_IntervalToMilliseconds(remaining); + tv.tv_sec = msecs/PR_MSEC_PER_SEC; + tv.tv_usec = (msecs % PR_MSEC_PER_SEC) * + PR_USEC_PER_MSEC; + goto retry; + } + } + } else if (EBADF == oserror) + { + /* find all the bad fds */ + ready = 0; + for (index = 0; index < npds; ++index) + { + pds[index].out_flags = 0; + if ((NULL != pds[index].fd) && + (0 != pds[index].in_flags)) + { + if (fcntl(selectfd[index], F_GETFL, 0) == -1) + { + pds[index].out_flags = PR_POLL_NVAL; + ready++; + } + } + } + } else { + _PR_MD_MAP_SELECT_ERROR(oserror); + } + } + else if (ready > 0) + { + for (index = 0; index < npds; ++index) + { + PRInt16 out_flags = 0; + if ((NULL != pds[index].fd) && (0 != pds[index].in_flags)) + { + if (FD_ISSET(selectfd[index], &rd)) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_READ) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_READ) + { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(selectfd[index], &wr)) + { + if (pds[index].out_flags + & _PR_POLL_READ_SYS_WRITE) + { + out_flags |= PR_POLL_READ; + } + if (pds[index].out_flags + & _PR_POLL_WRITE_SYS_WRITE) + { + out_flags |= PR_POLL_WRITE; + } + } + if (FD_ISSET(selectfd[index], &ex)) { + out_flags |= PR_POLL_EXCEPT; + } + } + pds[index].out_flags = out_flags; + } + } + } + } + return ready; + +} /* _pr_poll_with_select */ +#endif /* _PR_POLL_WITH_SELECT */ + +PR_IMPLEMENT(PRInt32) PR_Poll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ +#if defined(_PR_POLL_WITH_SELECT) + return(_pr_poll_with_select(pds, npds, timeout)); +#else + return(_pr_poll_with_poll(pds, npds, timeout)); +#endif +} + +PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags) +{ + struct dirent *dp; + + if (pt_TestAbort()) { + return NULL; + } + + for (;;) + { + errno = 0; + dp = readdir(dir->md.d); + if (NULL == dp) + { + pt_MapError(_PR_MD_MAP_READDIR_ERROR, errno); + return NULL; + } + if ((flags & PR_SKIP_DOT) + && ('.' == dp->d_name[0]) + && (0 == dp->d_name[1])) { + continue; + } + if ((flags & PR_SKIP_DOT_DOT) + && ('.' == dp->d_name[0]) + && ('.' == dp->d_name[1]) + && (0 == dp->d_name[2])) { + continue; + } + if ((flags & PR_SKIP_HIDDEN) && ('.' == dp->d_name[0])) { + continue; + } + break; + } + dir->d.name = dp->d_name; + return &dir->d; +} /* PR_ReadDir */ + +PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void) +{ + PRIntn domain = PF_INET; + + return PR_Socket(domain, SOCK_DGRAM, 0); +} /* PR_NewUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_NewTCPSocket(void) +{ + PRIntn domain = PF_INET; + + return PR_Socket(domain, SOCK_STREAM, 0); +} /* PR_NewTCPSocket */ + +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]) +{ + PRInt32 osfd[2]; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, osfd) == -1) { + pt_MapError(_PR_MD_MAP_SOCKETPAIR_ERROR, errno); + return PR_FAILURE; + } + + fds[0] = pt_SetMethods(osfd[0], PR_DESC_SOCKET_TCP, PR_FALSE, PR_FALSE); + if (fds[0] == NULL) { + close(osfd[0]); + close(osfd[1]); + return PR_FAILURE; + } + fds[1] = pt_SetMethods(osfd[1], PR_DESC_SOCKET_TCP, PR_FALSE, PR_FALSE); + if (fds[1] == NULL) { + PR_Close(fds[0]); + close(osfd[1]); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* PR_NewTCPSocketPair */ + +PR_IMPLEMENT(PRStatus) PR_CreatePipe( + PRFileDesc **readPipe, + PRFileDesc **writePipe +) +{ + int pipefd[2]; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + if (pipe(pipefd) == -1) + { + /* XXX map pipe error */ + PR_SetError(PR_UNKNOWN_ERROR, errno); + return PR_FAILURE; + } + *readPipe = pt_SetMethods(pipefd[0], PR_DESC_PIPE, PR_FALSE, PR_FALSE); + if (NULL == *readPipe) + { + close(pipefd[0]); + close(pipefd[1]); + return PR_FAILURE; + } + *writePipe = pt_SetMethods(pipefd[1], PR_DESC_PIPE, PR_FALSE, PR_FALSE); + if (NULL == *writePipe) + { + PR_Close(*readPipe); + close(pipefd[1]); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +/* +** Set the inheritance attribute of a file descriptor. +*/ +PR_IMPLEMENT(PRStatus) PR_SetFDInheritable( + PRFileDesc *fd, + PRBool inheritable) +{ + /* + * Only a non-layered, NSPR file descriptor can be inherited + * by a child process. + */ + if (fd->identity != PR_NSPR_IO_LAYER) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + if (fd->secret->inheritable != inheritable) + { + if (fcntl(fd->secret->md.osfd, F_SETFD, + inheritable ? 0 : FD_CLOEXEC) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + fd->secret->inheritable = (_PRTriStateBool) inheritable; + } + return PR_SUCCESS; +} + +/*****************************************************************************/ +/***************************** I/O friends methods ***************************/ +/*****************************************************************************/ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_FILE, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } + return fd; +} /* PR_ImportFile */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportPipe(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_PIPE, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } + return fd; +} /* PR_ImportPipe */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } +#ifdef _PR_NEED_SECRET_AF + if (NULL != fd) { + fd->secret->af = PF_INET; + } +#endif + return fd; +} /* PR_ImportTCPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportUDPSocket(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP, PR_FALSE, PR_TRUE); + if (NULL == fd) { + close(osfd); + } + return fd; +} /* PR_ImportUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_CreateSocketPollFd(PRInt32 osfd) +{ + PRFileDesc *fd; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + fd = _PR_Getfd(); + + if (fd == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + else + { + fd->secret->md.osfd = osfd; + fd->secret->inheritable = _PR_TRI_FALSE; + fd->secret->state = _PR_FILEDESC_OPEN; + fd->methods = PR_GetSocketPollFdMethods(); + } + + return fd; +} /* PR_CreateSocketPollFD */ + +PR_IMPLEMENT(PRStatus) PR_DestroySocketPollFd(PRFileDesc *fd) +{ + if (NULL == fd) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + fd->secret->state = _PR_FILEDESC_CLOSED; + _PR_Putfd(fd); + return PR_SUCCESS; +} /* PR_DestroySocketPollFd */ + +PR_IMPLEMENT(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *bottom) +{ + PRInt32 osfd = -1; + bottom = (NULL == bottom) ? + NULL : PR_GetIdentitiesLayer(bottom, PR_NSPR_IO_LAYER); + if (NULL == bottom) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + else { + osfd = bottom->secret->md.osfd; + } + return osfd; +} /* PR_FileDesc2NativeHandle */ + +PR_IMPLEMENT(void) PR_ChangeFileDescNativeHandle(PRFileDesc *fd, + PRInt32 handle) +{ + if (fd) { + fd->secret->md.osfd = handle; + } +} /* PR_ChangeFileDescNativeHandle*/ + +PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_Lock(_pr_flock_lock); + while (-1 == fd->secret->lockCount) { + PR_WaitCondVar(_pr_flock_cv, PR_INTERVAL_NO_TIMEOUT); + } + if (0 == fd->secret->lockCount) + { + fd->secret->lockCount = -1; + PR_Unlock(_pr_flock_lock); + status = _PR_MD_LOCKFILE(fd->secret->md.osfd); + PR_Lock(_pr_flock_lock); + fd->secret->lockCount = (PR_SUCCESS == status) ? 1 : 0; + PR_NotifyAllCondVar(_pr_flock_cv); + } + else + { + fd->secret->lockCount += 1; + } + PR_Unlock(_pr_flock_lock); + + return status; +} /* PR_LockFile */ + +PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_Lock(_pr_flock_lock); + if (0 == fd->secret->lockCount) + { + status = _PR_MD_TLOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) { + fd->secret->lockCount = 1; + } + } + else { + fd->secret->lockCount += 1; + } + PR_Unlock(_pr_flock_lock); + + return status; +} /* PR_TLockFile */ + +PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) { + return PR_FAILURE; + } + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 1) + { + status = _PR_MD_UNLOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) { + fd->secret->lockCount = 0; + } + } + else { + fd->secret->lockCount -= 1; + } + PR_Unlock(_pr_flock_lock); + + return status; +} + +/* + * The next two entry points should not be in the API, but they are + * defined here for historical (or hysterical) reasons. + */ + +PR_IMPLEMENT(PRInt32) PR_GetSysfdTableMax(void) +{ +#if defined(AIX) + return sysconf(_SC_OPEN_MAX); +#else + struct rlimit rlim; + + if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + + return rlim.rlim_max; +#endif +} + +PR_IMPLEMENT(PRInt32) PR_SetSysfdTableSize(PRIntn table_size) +{ +#if defined(AIX) + return -1; +#else + struct rlimit rlim; + PRInt32 tableMax = PR_GetSysfdTableMax(); + + if (tableMax < 0) { + return -1; + } + rlim.rlim_max = tableMax; + + /* Grow as much as we can; even if too big */ + if ( rlim.rlim_max < table_size ) { + rlim.rlim_cur = rlim.rlim_max; + } + else { + rlim.rlim_cur = table_size; + } + + if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + return -1; + } + + return rlim.rlim_cur; +#endif +} + +/* + * PR_Stat is supported for backward compatibility; some existing Java + * code uses it. New code should use PR_GetFileInfo. + */ + +#ifndef NO_NSPR_10_SUPPORT +PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_Stat", "PR_GetFileInfo"); + } + + if (pt_TestAbort()) { + return -1; + } + + if (-1 == stat(name, buf)) { + pt_MapError(_PR_MD_MAP_STAT_ERROR, errno); + return -1; + } else { + return 0; + } +} +#endif /* ! NO_NSPR_10_SUPPORT */ + + +PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_ZERO (PR_Select)", "PR_Poll"); + } + memset(set, 0, sizeof(PR_fd_set)); +} + +PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_SET (PR_Select)", "PR_Poll"); + } + PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC ); + + set->harray[set->hsize++] = fh; +} + +PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index, index2; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_CLR (PR_Select)", "PR_Poll"); + } + + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + for (index2=index; index2 < (set->hsize-1); index2++) { + set->harray[index2] = set->harray[index2+1]; + } + set->hsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_ISSET (PR_Select)", "PR_Poll"); + } + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + return 1; + } + return 0; +} + +PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_NSET (PR_Select)", "PR_Poll"); + } + PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC ); + + set->narray[set->nsize++] = fd; +} + +PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index, index2; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_NCLR (PR_Select)", "PR_Poll"); + } + + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + for (index2=index; index2 < (set->nsize-1); index2++) { + set->narray[index2] = set->narray[index2+1]; + } + set->nsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index; + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete("PR_FD_NISSET (PR_Select)", "PR_Poll"); + } + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + return 1; + } + return 0; +} + +#include <sys/types.h> +#include <sys/time.h> +#if !defined(HPUX) \ + && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__) +#include <sys/select.h> +#endif + +static PRInt32 +_PR_getset(PR_fd_set *pr_set, fd_set *set) +{ + PRUint32 index; + PRInt32 max = 0; + + if (!pr_set) { + return 0; + } + + FD_ZERO(set); + + /* First set the pr file handle osfds */ + for (index=0; index<pr_set->hsize; index++) { + FD_SET(pr_set->harray[index]->secret->md.osfd, set); + if (pr_set->harray[index]->secret->md.osfd > max) { + max = pr_set->harray[index]->secret->md.osfd; + } + } + /* Second set the native osfds */ + for (index=0; index<pr_set->nsize; index++) { + FD_SET(pr_set->narray[index], set); + if (pr_set->narray[index] > max) { + max = pr_set->narray[index]; + } + } + return max; +} + +static void +_PR_setset(PR_fd_set *pr_set, fd_set *set) +{ + PRUint32 index, last_used; + + if (!pr_set) { + return; + } + + for (last_used=0, index=0; index<pr_set->hsize; index++) { + if ( FD_ISSET(pr_set->harray[index]->secret->md.osfd, set) ) { + pr_set->harray[last_used++] = pr_set->harray[index]; + } + } + pr_set->hsize = last_used; + + for (last_used=0, index=0; index<pr_set->nsize; index++) { + if ( FD_ISSET(pr_set->narray[index], set) ) { + pr_set->narray[last_used++] = pr_set->narray[index]; + } + } + pr_set->nsize = last_used; +} + +PR_IMPLEMENT(PRInt32) PR_Select( + PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr, + PR_fd_set *pr_ex, PRIntervalTime timeout) +{ + fd_set rd, wr, ex; + struct timeval tv, *tvp; + PRInt32 max, max_fd; + PRInt32 rv; + /* + * For restarting select() if it is interrupted by a Unix signal. + * We use these variables to figure out how much time has elapsed + * and how much of the timeout still remains. + */ + PRIntervalTime start = 0, elapsed, remaining; + + static PRBool unwarned = PR_TRUE; + if (unwarned) { + unwarned = _PR_Obsolete( "PR_Select", "PR_Poll"); + } + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + max_fd = _PR_getset(pr_rd, &rd); + max_fd = (max = _PR_getset(pr_wr, &wr))>max_fd?max:max_fd; + max_fd = (max = _PR_getset(pr_ex, &ex))>max_fd?max:max_fd; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + tvp = NULL; + } else { + tv.tv_sec = (PRInt32)PR_IntervalToSeconds(timeout); + tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + start = PR_IntervalNow(); + } + +retry: + rv = select(max_fd + 1, (_PRSelectFdSetArg_t) &rd, + (_PRSelectFdSetArg_t) &wr, (_PRSelectFdSetArg_t) &ex, tvp); + + if (rv == -1 && errno == EINTR) { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } else { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + rv = 0; /* timed out */ + } else { + remaining = timeout - elapsed; + tv.tv_sec = (PRInt32)PR_IntervalToSeconds(remaining); + tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds( + remaining - PR_SecondsToInterval(tv.tv_sec)); + goto retry; + } + } + } + + if (rv > 0) { + _PR_setset(pr_rd, &rd); + _PR_setset(pr_wr, &wr); + _PR_setset(pr_ex, &ex); + } else if (rv == -1) { + pt_MapError(_PR_MD_MAP_SELECT_ERROR, errno); + } + return rv; +} +#endif /* defined(_PR_PTHREADS) */ + +#ifdef MOZ_UNICODE +/* ================ UTF16 Interfaces ================================ */ +PR_IMPLEMENT(PRFileDesc*) PR_OpenFileUTF16( + const PRUnichar *name, PRIntn flags, PRIntn mode) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRStatus) PR_CloseDirUTF16(PRDir *dir) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRDirUTF16*) PR_OpenDirUTF16(const PRUnichar *name) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRDirEntryUTF16*) PR_ReadDirUTF16(PRDirUTF16 *dir, PRDirFlags flags) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64UTF16(const PRUnichar *fn, PRFileInfo64 *info) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} +/* ================ UTF16 Interfaces ================================ */ +#endif /* MOZ_UNICODE */ + +/* ptio.c */ diff --git a/nsprpub/pr/src/pthreads/ptmisc.c b/nsprpub/pr/src/pthreads/ptmisc.c new file mode 100644 index 0000000000..4069f585ab --- /dev/null +++ b/nsprpub/pr/src/pthreads/ptmisc.c @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: ptmisc.c +** Descritpion: Implemenation of miscellaneous methods for pthreads +*/ + +#if defined(_PR_PTHREADS) + +#include "primpl.h" + +#include <stdio.h> +#ifdef SOLARIS +#include <thread.h> +#endif + +#define PT_LOG(f) + +void _PR_InitCPUs(void) { + PT_LOG("_PR_InitCPUs") +} +void _PR_InitStacks(void) { + PT_LOG("_PR_InitStacks") +} + +PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) +{ +#ifdef SOLARIS + thr_setconcurrency(numCPUs); +#else + PT_LOG("PR_SetConcurrency"); +#endif +} + +PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 flag) +{ + PT_LOG("PR_SetThreadRecycleMode") +} + +#endif /* defined(_PR_PTHREADS) */ + +/* ptmisc.c */ diff --git a/nsprpub/pr/src/pthreads/ptsynch.c b/nsprpub/pr/src/pthreads/ptsynch.c new file mode 100644 index 0000000000..26f58b2d0f --- /dev/null +++ b/nsprpub/pr/src/pthreads/ptsynch.c @@ -0,0 +1,1295 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: ptsynch.c +** Descritpion: Implemenation for thread synchronization using pthreads +** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h +*/ + +#if defined(_PR_PTHREADS) + +#include "primpl.h" +#include "obsolete/prsem.h" + +#include <string.h> +#include <pthread.h> +#include <sys/time.h> + +static pthread_mutexattr_t _pt_mattr; +static pthread_condattr_t _pt_cvar_attr; + +#if defined(DEBUG) +extern PTDebug pt_debug; /* this is shared between several modules */ +#endif /* defined(DEBUG) */ + +#if defined(FREEBSD) +/* + * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK. + * Newer versions return EBUSY. We still need to support both. + */ +static int +pt_pthread_mutex_is_locked(pthread_mutex_t *m) +{ + int rv = pthread_mutex_trylock(m); + return (EBUSY == rv || EDEADLK == rv); +} +#endif + +/**************************************************************/ +/**************************************************************/ +/*****************************LOCKS****************************/ +/**************************************************************/ +/**************************************************************/ + +void _PR_InitLocks(void) +{ + int rv; + rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr); + PR_ASSERT(0 == rv); + +#if (defined(LINUX) && (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)) || \ + (defined(FREEBSD) && __FreeBSD_version > 700055) + rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP); + PR_ASSERT(0 == rv); +#endif + + rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr); + PR_ASSERT(0 == rv); +} + +static void pt_PostNotifies(PRLock *lock, PRBool unlock) +{ + PRIntn index, rv; + _PT_Notified post; + _PT_Notified *notified, *prev = NULL; + /* + * Time to actually notify any conditions that were affected + * while the lock was held. Get a copy of the list that's in + * the lock structure and then zero the original. If it's + * linked to other such structures, we own that storage. + */ + post = lock->notified; /* a safe copy; we own the lock */ + +#if defined(DEBUG) + memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */ +#else + lock->notified.length = 0; /* these are really sufficient */ + lock->notified.link = NULL; +#endif + + /* should (may) we release lock before notifying? */ + if (unlock) + { + rv = pthread_mutex_unlock(&lock->mutex); + PR_ASSERT(0 == rv); + } + + notified = &post; /* this is where we start */ + do + { + for (index = 0; index < notified->length; ++index) + { + PRCondVar *cv = notified->cv[index].cv; + PR_ASSERT(NULL != cv); + PR_ASSERT(0 != notified->cv[index].times); + if (-1 == notified->cv[index].times) + { + rv = pthread_cond_broadcast(&cv->cv); + PR_ASSERT(0 == rv); + } + else + { + while (notified->cv[index].times-- > 0) + { + rv = pthread_cond_signal(&cv->cv); + PR_ASSERT(0 == rv); + } + } +#if defined(DEBUG) + pt_debug.cvars_notified += 1; + if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) + { + pt_debug.delayed_cv_deletes += 1; + PR_DestroyCondVar(cv); + } +#else /* defined(DEBUG) */ + if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) { + PR_DestroyCondVar(cv); + } +#endif /* defined(DEBUG) */ + } + prev = notified; + notified = notified->link; + if (&post != prev) { + PR_DELETE(prev); + } + } while (NULL != notified); +} /* pt_PostNotifies */ + +PR_IMPLEMENT(PRLock*) PR_NewLock(void) +{ + PRIntn rv; + PRLock *lock; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + lock = PR_NEWZAP(PRLock); + if (lock != NULL) + { + rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr); + PR_ASSERT(0 == rv); + } +#if defined(DEBUG) + pt_debug.locks_created += 1; +#endif + return lock; +} /* PR_NewLock */ + +PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) +{ + PRIntn rv; + PR_ASSERT(NULL != lock); + PR_ASSERT(PR_FALSE == lock->locked); + PR_ASSERT(0 == lock->notified.length); + PR_ASSERT(NULL == lock->notified.link); + rv = pthread_mutex_destroy(&lock->mutex); + PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(lock, 0xaf, sizeof(PRLock)); + pt_debug.locks_destroyed += 1; +#endif + PR_Free(lock); +} /* PR_DestroyLock */ + +PR_IMPLEMENT(void) PR_Lock(PRLock *lock) +{ + /* Nb: PR_Lock must not call PR_GetCurrentThread to access the |id| or + * |tid| field of the current thread's PRThread structure because + * _pt_root calls PR_Lock before setting thred->id and thred->tid. */ + PRIntn rv; + PR_ASSERT(lock != NULL); + rv = pthread_mutex_lock(&lock->mutex); + PR_ASSERT(0 == rv); + PR_ASSERT(0 == lock->notified.length); + PR_ASSERT(NULL == lock->notified.link); + PR_ASSERT(PR_FALSE == lock->locked); + /* Nb: the order of the next two statements is not critical to + * the correctness of PR_AssertCurrentThreadOwnsLock(), but + * this particular order makes the assertion more likely to + * catch errors. */ + lock->owner = pthread_self(); + lock->locked = PR_TRUE; +#if defined(DEBUG) + pt_debug.locks_acquired += 1; +#endif +} /* PR_Lock */ + +PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) +{ + pthread_t self = pthread_self(); + PRIntn rv; + + PR_ASSERT(lock != NULL); + PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex)); + PR_ASSERT(PR_TRUE == lock->locked); + PR_ASSERT(pthread_equal(lock->owner, self)); + + if (!lock->locked || !pthread_equal(lock->owner, self)) { + return PR_FAILURE; + } + + lock->locked = PR_FALSE; + if (0 == lock->notified.length) /* shortcut */ + { + rv = pthread_mutex_unlock(&lock->mutex); + PR_ASSERT(0 == rv); + } + else { + pt_PostNotifies(lock, PR_TRUE); + } + +#if defined(DEBUG) + pt_debug.locks_released += 1; +#endif + return PR_SUCCESS; +} /* PR_Unlock */ + +PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock) +{ + /* Nb: the order of the |locked| and |owner==me| checks is not critical + * to the correctness of PR_AssertCurrentThreadOwnsLock(), but + * this particular order makes the assertion more likely to + * catch errors. */ + PR_ASSERT(lock->locked && pthread_equal(lock->owner, pthread_self())); +} + +/**************************************************************/ +/**************************************************************/ +/***************************CONDITIONS*************************/ +/**************************************************************/ +/**************************************************************/ + + +/* + * This code is used to compute the absolute time for the wakeup. + * It's moderately ugly, so it's defined here and called in a + * couple of places. + */ +#define PT_NANOPERMICRO 1000UL +#define PT_BILLION 1000000000UL + +static PRIntn pt_TimedWait( + pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout) +{ + int rv; + struct timeval now; + struct timespec tmo; + PRUint32 ticks = PR_TicksPerSecond(); + + tmo.tv_sec = (PRInt32)(timeout / ticks); + tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks)); + tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec); + + /* pthreads wants this in absolute time, off we go ... */ + (void)GETTIMEOFDAY(&now); + /* that one's usecs, this one's nsecs - grrrr! */ + tmo.tv_sec += now.tv_sec; + tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); + tmo.tv_sec += tmo.tv_nsec / PT_BILLION; + tmo.tv_nsec %= PT_BILLION; + + rv = pthread_cond_timedwait(cv, ml, &tmo); + + /* NSPR doesn't report timeouts */ + return (rv == ETIMEDOUT) ? 0 : rv; +} /* pt_TimedWait */ + + +/* + * Notifies just get posted to the protecting mutex. The + * actual notification is done when the lock is released so that + * MP systems don't contend for a lock that they can't have. + */ +static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast) +{ + PRIntn index = 0; + _PT_Notified *notified = &cvar->lock->notified; + + PR_ASSERT(PR_TRUE == cvar->lock->locked); + PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); + PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); + + while (1) + { + for (index = 0; index < notified->length; ++index) + { + if (notified->cv[index].cv == cvar) + { + if (broadcast) { + notified->cv[index].times = -1; + } + else if (-1 != notified->cv[index].times) { + notified->cv[index].times += 1; + } + return; /* we're finished */ + } + } + /* if not full, enter new CV in this array */ + if (notified->length < PT_CV_NOTIFIED_LENGTH) { + break; + } + + /* if there's no link, create an empty array and link it */ + if (NULL == notified->link) { + notified->link = PR_NEWZAP(_PT_Notified); + } + notified = notified->link; + } + + /* A brand new entry in the array */ + (void)PR_ATOMIC_INCREMENT(&cvar->notify_pending); + notified->cv[index].times = (broadcast) ? -1 : 1; + notified->cv[index].cv = cvar; + notified->length += 1; +} /* pt_PostNotifyToCvar */ + +PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock) +{ + PRCondVar *cv = PR_NEW(PRCondVar); + PR_ASSERT(lock != NULL); + if (cv != NULL) + { + int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); + PR_ASSERT(0 == rv); + if (0 == rv) + { + cv->lock = lock; + cv->notify_pending = 0; +#if defined(DEBUG) + pt_debug.cvars_created += 1; +#endif + } + else + { + PR_DELETE(cv); + cv = NULL; + } + } + return cv; +} /* PR_NewCondVar */ + +PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar) +{ + if (0 > PR_ATOMIC_DECREMENT(&cvar->notify_pending)) + { + PRIntn rv = pthread_cond_destroy(&cvar->cv); +#if defined(DEBUG) + PR_ASSERT(0 == rv); + memset(cvar, 0xaf, sizeof(PRCondVar)); + pt_debug.cvars_destroyed += 1; +#else + (void)rv; +#endif + PR_Free(cvar); + } +} /* PR_DestroyCondVar */ + +PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout) +{ + PRIntn rv; + PRThread *thred = PR_GetCurrentThread(); + + PR_ASSERT(cvar != NULL); + /* We'd better be locked */ + PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); + PR_ASSERT(PR_TRUE == cvar->lock->locked); + /* and it better be by us */ + PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); + + if (_PT_THREAD_INTERRUPTED(thred)) { + goto aborted; + } + + /* + * The thread waiting is used for PR_Interrupt + */ + thred->waiting = cvar; /* this is where we're waiting */ + + /* + * If we have pending notifies, post them now. + * + * This is not optimal. We're going to post these notifies + * while we're holding the lock. That means on MP systems + * that they are going to collide for the lock that we will + * hold until we actually wait. + */ + if (0 != cvar->lock->notified.length) { + pt_PostNotifies(cvar->lock, PR_FALSE); + } + + /* + * We're surrendering the lock, so clear out the locked field. + */ + cvar->lock->locked = PR_FALSE; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex); + } + else { + rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout); + } + + /* We just got the lock back - this better be empty */ + PR_ASSERT(PR_FALSE == cvar->lock->locked); + cvar->lock->locked = PR_TRUE; + cvar->lock->owner = pthread_self(); + + PR_ASSERT(0 == cvar->lock->notified.length); + thred->waiting = NULL; /* and now we're not */ + if (_PT_THREAD_INTERRUPTED(thred)) { + goto aborted; + } + if (rv != 0) + { + _PR_MD_MAP_DEFAULT_ERROR(rv); + return PR_FAILURE; + } + return PR_SUCCESS; + +aborted: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thred->state &= ~PT_THREAD_ABORTED; + return PR_FAILURE; +} /* PR_WaitCondVar */ + +PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar != NULL); + pt_PostNotifyToCvar(cvar, PR_FALSE); + return PR_SUCCESS; +} /* PR_NotifyCondVar */ + +PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar != NULL); + pt_PostNotifyToCvar(cvar, PR_TRUE); + return PR_SUCCESS; +} /* PR_NotifyAllCondVar */ + +/**************************************************************/ +/**************************************************************/ +/***************************MONITORS***************************/ +/**************************************************************/ +/**************************************************************/ + +/* + * Notifies just get posted to the monitor. The actual notification is done + * when the monitor is fully exited so that MP systems don't contend for a + * monitor that they can't enter. + */ +static void pt_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast) +{ + PR_ASSERT(NULL != mon); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon); + + /* mon->notifyTimes is protected by the monitor, so we don't need to + * acquire mon->lock. + */ + if (broadcast) { + mon->notifyTimes = -1; + } + else if (-1 != mon->notifyTimes) { + mon->notifyTimes += 1; + } +} /* pt_PostNotifyToMonitor */ + +static void pt_PostNotifiesFromMonitor(pthread_cond_t *cv, PRIntn times) +{ + PRIntn rv; + + /* + * Time to actually notify any waits that were affected while the monitor + * was entered. + */ + PR_ASSERT(NULL != cv); + PR_ASSERT(0 != times); + if (-1 == times) + { + rv = pthread_cond_broadcast(cv); + PR_ASSERT(0 == rv); + } + else + { + while (times-- > 0) + { + rv = pthread_cond_signal(cv); + PR_ASSERT(0 == rv); + } + } +} /* pt_PostNotifiesFromMonitor */ + +PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void) +{ + PRMonitor *mon; + int rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + mon = PR_NEWZAP(PRMonitor); + if (mon == NULL) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + rv = _PT_PTHREAD_MUTEX_INIT(mon->lock, _pt_mattr); + PR_ASSERT(0 == rv); + if (0 != rv) { + goto error1; + } + + _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); + + rv = _PT_PTHREAD_COND_INIT(mon->entryCV, _pt_cvar_attr); + PR_ASSERT(0 == rv); + if (0 != rv) { + goto error2; + } + + rv = _PT_PTHREAD_COND_INIT(mon->waitCV, _pt_cvar_attr); + PR_ASSERT(0 == rv); + if (0 != rv) { + goto error3; + } + + mon->notifyTimes = 0; + mon->entryCount = 0; + mon->refCount = 1; + mon->name = NULL; + return mon; + +error3: + pthread_cond_destroy(&mon->entryCV); +error2: + pthread_mutex_destroy(&mon->lock); +error1: + PR_Free(mon); + _PR_MD_MAP_DEFAULT_ERROR(rv); + return NULL; +} /* PR_NewMonitor */ + +PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) +{ + PRMonitor* mon = PR_NewMonitor(); + if (mon) { + mon->name = name; + } + return mon; +} + +PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) +{ + int rv; + + PR_ASSERT(mon != NULL); + if (PR_ATOMIC_DECREMENT(&mon->refCount) == 0) + { + rv = pthread_cond_destroy(&mon->waitCV); PR_ASSERT(0 == rv); + rv = pthread_cond_destroy(&mon->entryCV); PR_ASSERT(0 == rv); + rv = pthread_mutex_destroy(&mon->lock); PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(mon, 0xaf, sizeof(PRMonitor)); +#endif + PR_Free(mon); + } +} /* PR_DestroyMonitor */ + +/* The GC uses this; it is quite arguably a bad interface. I'm just + * duplicating it for now - XXXMB + */ +PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon) +{ + pthread_t self = pthread_self(); + PRIntn rv; + PRIntn count = 0; + + rv = pthread_mutex_lock(&mon->lock); + PR_ASSERT(0 == rv); + if (pthread_equal(mon->owner, self)) { + count = mon->entryCount; + } + rv = pthread_mutex_unlock(&mon->lock); + PR_ASSERT(0 == rv); + return count; +} + +PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon) +{ +#if defined(DEBUG) || defined(FORCE_PR_ASSERT) + PRIntn rv; + + rv = pthread_mutex_lock(&mon->lock); + PR_ASSERT(0 == rv); + PR_ASSERT(mon->entryCount != 0 && + pthread_equal(mon->owner, pthread_self())); + rv = pthread_mutex_unlock(&mon->lock); + PR_ASSERT(0 == rv); +#endif +} + +PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) +{ + pthread_t self = pthread_self(); + PRIntn rv; + + PR_ASSERT(mon != NULL); + rv = pthread_mutex_lock(&mon->lock); + PR_ASSERT(0 == rv); + if (mon->entryCount != 0) + { + if (pthread_equal(mon->owner, self)) { + goto done; + } + while (mon->entryCount != 0) + { + rv = pthread_cond_wait(&mon->entryCV, &mon->lock); + PR_ASSERT(0 == rv); + } + } + /* and now I have the monitor */ + PR_ASSERT(0 == mon->notifyTimes); + PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner)); + _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner); + +done: + mon->entryCount += 1; + rv = pthread_mutex_unlock(&mon->lock); + PR_ASSERT(0 == rv); +} /* PR_EnterMonitor */ + +PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) +{ + pthread_t self = pthread_self(); + PRIntn rv; + PRBool notifyEntryWaiter = PR_FALSE; + PRIntn notifyTimes = 0; + + PR_ASSERT(mon != NULL); + rv = pthread_mutex_lock(&mon->lock); + PR_ASSERT(0 == rv); + /* the entries should be > 0 and we'd better be the owner */ + PR_ASSERT(mon->entryCount > 0); + PR_ASSERT(pthread_equal(mon->owner, self)); + if (mon->entryCount == 0 || !pthread_equal(mon->owner, self)) + { + rv = pthread_mutex_unlock(&mon->lock); + PR_ASSERT(0 == rv); + return PR_FAILURE; + } + + mon->entryCount -= 1; /* reduce by one */ + if (mon->entryCount == 0) + { + /* and if it transitioned to zero - notify an entry waiter */ + /* make the owner unknown */ + _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); + notifyEntryWaiter = PR_TRUE; + notifyTimes = mon->notifyTimes; + mon->notifyTimes = 0; + /* We will access the members of 'mon' after unlocking mon->lock. + * Add a reference. */ + PR_ATOMIC_INCREMENT(&mon->refCount); + } + rv = pthread_mutex_unlock(&mon->lock); + PR_ASSERT(0 == rv); + if (notifyEntryWaiter) + { + if (notifyTimes) { + pt_PostNotifiesFromMonitor(&mon->waitCV, notifyTimes); + } + rv = pthread_cond_signal(&mon->entryCV); + PR_ASSERT(0 == rv); + /* We are done accessing the members of 'mon'. Release the + * reference. */ + PR_DestroyMonitor(mon); + } + return PR_SUCCESS; +} /* PR_ExitMonitor */ + +PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout) +{ + PRStatus rv; + PRUint32 saved_entries; + pthread_t saved_owner; + + PR_ASSERT(mon != NULL); + rv = pthread_mutex_lock(&mon->lock); + PR_ASSERT(0 == rv); + /* the entries better be positive */ + PR_ASSERT(mon->entryCount > 0); + /* and it better be owned by us */ + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); + + /* tuck these away 'till later */ + saved_entries = mon->entryCount; + mon->entryCount = 0; + _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner); + _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner); + /* + * If we have pending notifies, post them now. + * + * This is not optimal. We're going to post these notifies + * while we're holding the lock. That means on MP systems + * that they are going to collide for the lock that we will + * hold until we actually wait. + */ + if (0 != mon->notifyTimes) + { + pt_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); + mon->notifyTimes = 0; + } + rv = pthread_cond_signal(&mon->entryCV); + PR_ASSERT(0 == rv); + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + rv = pthread_cond_wait(&mon->waitCV, &mon->lock); + } + else { + rv = pt_TimedWait(&mon->waitCV, &mon->lock, timeout); + } + PR_ASSERT(0 == rv); + + while (mon->entryCount != 0) + { + rv = pthread_cond_wait(&mon->entryCV, &mon->lock); + PR_ASSERT(0 == rv); + } + PR_ASSERT(0 == mon->notifyTimes); + /* reinstate the interesting information */ + mon->entryCount = saved_entries; + _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner); + + rv = pthread_mutex_unlock(&mon->lock); + PR_ASSERT(0 == rv); + return rv; +} /* PR_Wait */ + +PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) +{ + pt_PostNotifyToMonitor(mon, PR_FALSE); + return PR_SUCCESS; +} /* PR_Notify */ + +PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) +{ + pt_PostNotifyToMonitor(mon, PR_TRUE); + return PR_SUCCESS; +} /* PR_NotifyAll */ + +/**************************************************************/ +/**************************************************************/ +/**************************SEMAPHORES**************************/ +/**************************************************************/ +/**************************************************************/ +PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( + "PR_PostSem", "locks & condition variables"); + PR_Lock(semaphore->cvar->lock); + PR_NotifyCondVar(semaphore->cvar); + semaphore->count += 1; + PR_Unlock(semaphore->cvar->lock); +} /* PR_PostSem */ + +PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore) +{ + PRStatus status = PR_SUCCESS; + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( + "PR_WaitSem", "locks & condition variables"); + PR_Lock(semaphore->cvar->lock); + while ((semaphore->count == 0) && (PR_SUCCESS == status)) { + status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT); + } + if (PR_SUCCESS == status) { + semaphore->count -= 1; + } + PR_Unlock(semaphore->cvar->lock); + return status; +} /* PR_WaitSem */ + +PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( + "PR_DestroySem", "locks & condition variables"); + PR_DestroyLock(semaphore->cvar->lock); + PR_DestroyCondVar(semaphore->cvar); + PR_Free(semaphore); +} /* PR_DestroySem */ + +PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) +{ + PRSemaphore *semaphore; + static PRBool unwarned = PR_TRUE; + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (unwarned) unwarned = _PR_Obsolete( + "PR_NewSem", "locks & condition variables"); + + semaphore = PR_NEWZAP(PRSemaphore); + if (NULL != semaphore) + { + PRLock *lock = PR_NewLock(); + if (NULL != lock) + { + semaphore->cvar = PR_NewCondVar(lock); + if (NULL != semaphore->cvar) + { + semaphore->count = value; + return semaphore; + } + PR_DestroyLock(lock); + } + PR_Free(semaphore); + } + return NULL; +} + +/* + * Define the interprocess named semaphore functions. + * There are three implementations: + * 1. POSIX semaphore based; + * 2. System V semaphore based; + * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR). + */ + +#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 + { +#ifdef HPUX + /* Pass 0 as the mode and value arguments to work around a bug. */ + sem->sem = sem_open(osname, 0, 0, 0); +#else + sem->sem = sem_open(osname, 0); +#endif + } + if ((sem_t *) -1 == sem->sem) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_Free(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_Free(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; +} + +#elif defined(_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)) \ + || (defined(FREEBSD) && __FreeBSD_version < 1200059) \ + || defined(OPENBSD) || defined(BSDI) \ + || defined(DARWIN) +/* 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_Free(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_Free(sem); + return NULL; + } + return sem; + } + + if (errno != EEXIST || flags & PR_SEM_EXCL) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_Free(sem); + return NULL; + } + } + + sem->semid = semget(key, 1, NSPR_SEM_MODE); + if (sem->semid == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_Free(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_Free(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_Free(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; +} + +#else /* neither POSIX nor System V semaphores are available */ + +PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( + const char *name, + PRIntn flags, + PRIntn mode, + PRUintn value) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +#endif /* end of interprocess named semaphore functions */ + +/**************************************************************/ +/**************************************************************/ +/******************ROUTINES FOR DCE EMULATION******************/ +/**************************************************************/ +/**************************************************************/ + +#include "prpdce.h" + +PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) +{ + PRIntn rv = pthread_mutex_trylock(&lock->mutex); + if (rv == 0) + { + PR_ASSERT(PR_FALSE == lock->locked); + lock->locked = PR_TRUE; + lock->owner = pthread_self(); + } + /* XXX set error code? */ + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PRP_TryLock */ + +PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void) +{ + PRCondVar *cv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + cv = PR_NEW(PRCondVar); + if (cv != NULL) + { + int rv; + rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); + PR_ASSERT(0 == rv); + if (0 == rv) + { + cv->lock = _PR_NAKED_CV_LOCK; + } + else + { + PR_DELETE(cv); + cv = NULL; + } + } + return cv; +} /* PRP_NewNakedCondVar */ + +PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar) +{ + int rv; + rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(cvar, 0xaf, sizeof(PRCondVar)); +#endif + PR_Free(cvar); +} /* PRP_DestroyNakedCondVar */ + +PR_IMPLEMENT(PRStatus) PRP_NakedWait( + PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout) +{ + PRIntn rv; + PR_ASSERT(cvar != NULL); + /* XXX do we really want to assert this in a naked wait? */ + PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex)); + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + rv = pthread_cond_wait(&cvar->cv, &ml->mutex); + } + else { + rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout); + } + if (rv != 0) + { + _PR_MD_MAP_DEFAULT_ERROR(rv); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* PRP_NakedWait */ + +PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar) +{ + int rv; + PR_ASSERT(cvar != NULL); + rv = pthread_cond_signal(&cvar->cv); + PR_ASSERT(0 == rv); + return PR_SUCCESS; +} /* PRP_NakedNotify */ + +PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar) +{ + int rv; + PR_ASSERT(cvar != NULL); + rv = pthread_cond_broadcast(&cvar->cv); + PR_ASSERT(0 == rv); + return PR_SUCCESS; +} /* PRP_NakedBroadcast */ + +#endif /* defined(_PR_PTHREADS) */ + +/* ptsynch.c */ diff --git a/nsprpub/pr/src/pthreads/ptthread.c b/nsprpub/pr/src/pthreads/ptthread.c new file mode 100644 index 0000000000..92c9c7889e --- /dev/null +++ b/nsprpub/pr/src/pthreads/ptthread.c @@ -0,0 +1,1749 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: ptthread.c +** Descritpion: Implemenation for threds using pthreds +** Exports: ptthread.h +*/ + +#if defined(_PR_PTHREADS) + +#include "prlog.h" +#include "primpl.h" +#include "prpdce.h" + +#include <pthread.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <dlfcn.h> + +#if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY) +#include <pthread_np.h> +#endif + +#if defined(ANDROID) +#include <sys/prctl.h> +#endif + +#ifdef _PR_NICE_PRIORITY_SCHEDULING +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#include <sys/resource.h> +#ifndef HAVE_GETTID +#define gettid() (syscall(SYS_gettid)) +#endif +#endif + +/* + * Record whether or not we have the privilege to set the scheduling + * policy and priority of threads. 0 means that privilege is available. + * EPERM means that privilege is not available. + */ + +static PRIntn pt_schedpriv = 0; +extern PRLock *_pr_sleeplock; + +static struct _PT_Bookeeping +{ + PRLock *ml; /* a lock to protect ourselves */ + PRCondVar *cv; /* used to signal global things */ + PRInt32 system, user; /* a count of the two different types */ + PRUintn this_many; /* number of threads allowed for exit */ + pthread_key_t key; /* thread private data key */ + PRBool keyCreated; /* whether 'key' should be deleted */ + PRThread *first, *last; /* list of threads we know about */ +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + PRInt32 minPrio, maxPrio; /* range of scheduling priorities */ +#endif +} pt_book = {0}; + +static void _pt_thread_death(void *arg); +static void _pt_thread_death_internal(void *arg, PRBool callDestructors); +static void init_pthread_gc_support(void); + +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 +static PRIntn pt_PriorityMap(PRThreadPriority pri) +{ +#ifdef NTO + /* This priority algorithm causes lots of problems on Neutrino + * for now I have just hard coded everything to run at priority 10 + * until I can come up with a new algorithm. + * Jerry.Kirk@Nexwarecorp.com + */ + return 10; +#else + return pt_book.minPrio + + pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST; +#endif +} +#elif defined(_PR_NICE_PRIORITY_SCHEDULING) +/* + * This functions maps higher priorities to lower nice values relative to the + * nice value specified in the |nice| parameter. The corresponding relative + * adjustments are: + * + * PR_PRIORITY_LOW +1 + * PR_PRIORITY_NORMAL 0 + * PR_PRIORITY_HIGH -1 + * PR_PRIORITY_URGENT -2 + */ +static int pt_RelativePriority(int nice, PRThreadPriority pri) +{ + return nice + (1 - pri); +} +#endif + +/* +** Initialize a stack for a native pthread thread +*/ +static void _PR_InitializeStack(PRThreadStack *ts) +{ + if( ts && (ts->stackTop == 0) ) { + ts->allocBase = (char *) &ts; + ts->allocSize = ts->stackSize; + + /* + ** Setup stackTop and stackBottom values. + */ +#ifdef HAVE_STACK_GROWING_UP + ts->stackBottom = ts->allocBase + ts->stackSize; + ts->stackTop = ts->allocBase; +#else + ts->stackTop = ts->allocBase; + ts->stackBottom = ts->allocBase - ts->stackSize; +#endif + } +} + +static void *_pt_root(void *arg) +{ + PRIntn rv; + PRThread *thred = (PRThread*)arg; + PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE; + pthread_t id = pthread_self(); +#ifdef _PR_NICE_PRIORITY_SCHEDULING + pid_t tid; +#endif + +#ifdef _PR_NICE_PRIORITY_SCHEDULING + /* + * We need to know the kernel thread ID of each thread in order to + * set its nice value hence we do it here instead of at creation time. + */ + tid = gettid(); + errno = 0; + rv = getpriority(PRIO_PROCESS, 0); + + /* If we cannot read the main thread's nice value don't try to change the + * new thread's nice value. */ + if (errno == 0) { + setpriority(PRIO_PROCESS, tid, + pt_RelativePriority(rv, thred->priority)); + } +#endif + + /* Set up the thread stack information */ + _PR_InitializeStack(thred->stack); + + /* + * Set within the current thread the pointer to our object. + * This object will be deleted when the thread termintates, + * whether in a join or detached (see _PR_InitThreads()). + */ + rv = pthread_setspecific(pt_book.key, thred); + PR_ASSERT(0 == rv); + + /* make the thread visible to the rest of the runtime */ + PR_Lock(pt_book.ml); + /* + * Both the parent thread and this new thread set thred->id. + * The new thread must ensure that thred->id is set before + * it executes its startFunc. The parent thread must ensure + * that thred->id is set before PR_CreateThread() returns. + * Both threads set thred->id while holding pt_book.ml and + * use thred->idSet to ensure thred->id is written only once. + */ + if (!thred->idSet) + { + thred->id = id; + thred->idSet = PR_TRUE; + } + else + { + PR_ASSERT(pthread_equal(thred->id, id)); + } + +#ifdef _PR_NICE_PRIORITY_SCHEDULING + thred->tid = tid; + PR_NotifyAllCondVar(pt_book.cv); +#endif + + /* If this is a GCABLE thread, set its state appropriately */ + if (thred->suspend & PT_THREAD_SETGCABLE) { + thred->state |= PT_THREAD_GCABLE; + } + thred->suspend = 0; + + thred->prev = pt_book.last; + if (pt_book.last) { + pt_book.last->next = thred; + } + else { + pt_book.first = thred; + } + thred->next = NULL; + pt_book.last = thred; + PR_Unlock(pt_book.ml); + + thred->startFunc(thred->arg); /* make visible to the client */ + + /* unhook the thread from the runtime */ + PR_Lock(pt_book.ml); + /* + * At this moment, PR_CreateThread() may not have set thred->id yet. + * It is safe for a detached thread to free thred only after + * PR_CreateThread() has accessed thred->id and thred->idSet. + */ + if (detached) + { + while (!thred->okToDelete) { + PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); + } + } + + if (thred->state & PT_THREAD_SYSTEM) { + pt_book.system -= 1; + } + else if (--pt_book.user == pt_book.this_many) { + PR_NotifyAllCondVar(pt_book.cv); + } + if (NULL == thred->prev) { + pt_book.first = thred->next; + } + else { + thred->prev->next = thred->next; + } + if (NULL == thred->next) { + pt_book.last = thred->prev; + } + else { + thred->next->prev = thred->prev; + } + PR_Unlock(pt_book.ml); + + /* + * Here we set the pthread's backpointer to the PRThread to NULL. + * Otherwise the destructor would get called eagerly as the thread + * returns to the pthread runtime. The joining thread would them be + * the proud possessor of a dangling reference. However, this is the + * last chance to delete the object if the thread is detached, so + * just let the destructor do the work. + */ + if (PR_FALSE == detached) + { + /* Call TPD destructors on this thread. */ + _PR_DestroyThreadPrivate(thred); + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); + } + + return NULL; +} /* _pt_root */ + +static PRThread* pt_AttachThread(void) +{ + PRThread *thred = NULL; + + /* + * NSPR must have been initialized when PR_AttachThread is called. + * We cannot have PR_AttachThread call implicit initialization + * because if multiple threads call PR_AttachThread simultaneously, + * NSPR may be initialized more than once. + * We can't call any function that calls PR_GetCurrentThread() + * either (e.g., PR_SetError()) as that will result in infinite + * recursion. + */ + if (!_pr_initialized) { + return NULL; + } + + /* PR_NEWZAP must not call PR_GetCurrentThread() */ + thred = PR_NEWZAP(PRThread); + if (NULL != thred) + { + int rv; + + thred->priority = PR_PRIORITY_NORMAL; + thred->id = pthread_self(); + thred->idSet = PR_TRUE; +#ifdef _PR_NICE_PRIORITY_SCHEDULING + thred->tid = gettid(); +#endif + rv = pthread_setspecific(pt_book.key, thred); + PR_ASSERT(0 == rv); + + thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN; + PR_Lock(pt_book.ml); + + /* then put it into the list */ + thred->prev = pt_book.last; + if (pt_book.last) { + pt_book.last->next = thred; + } + else { + pt_book.first = thred; + } + thred->next = NULL; + pt_book.last = thred; + PR_Unlock(pt_book.ml); + + } + return thred; /* may be NULL */ +} /* pt_AttachThread */ + +static PRThread* _PR_CreateThread( + PRThreadType type, void (*start)(void *arg), + void *arg, PRThreadPriority priority, PRThreadScope scope, + PRThreadState state, PRUint32 stackSize, PRBool isGCAble) +{ + int rv; + PRThread *thred; + pthread_attr_t tattr; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) { + priority = PR_PRIORITY_FIRST; + } + else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) { + priority = PR_PRIORITY_LAST; + } + + rv = _PT_PTHREAD_ATTR_INIT(&tattr); + PR_ASSERT(0 == rv); + + if (EPERM != pt_schedpriv) + { +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + struct sched_param schedule; +#endif + +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED); + PR_ASSERT(0 == rv); +#endif + + /* Use the default scheduling policy */ + +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + rv = pthread_attr_getschedparam(&tattr, &schedule); + PR_ASSERT(0 == rv); + schedule.sched_priority = pt_PriorityMap(priority); + rv = pthread_attr_setschedparam(&tattr, &schedule); + PR_ASSERT(0 == rv); +#ifdef NTO + rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */ + PR_ASSERT(0 == rv); +#endif +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING > 0 */ + } + + rv = pthread_attr_setdetachstate(&tattr, + ((PR_JOINABLE_THREAD == state) ? + PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED)); + PR_ASSERT(0 == rv); + + /* + * If stackSize is 0, we use the default pthread stack size. + */ + if (stackSize) + { +#ifdef _MD_MINIMUM_STACK_SIZE + if (stackSize < _MD_MINIMUM_STACK_SIZE) { + stackSize = _MD_MINIMUM_STACK_SIZE; + } +#endif + rv = pthread_attr_setstacksize(&tattr, stackSize); + PR_ASSERT(0 == rv); + } + + thred = PR_NEWZAP(PRThread); + if (NULL == thred) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno); + goto done; + } + else + { + pthread_t id; + + thred->arg = arg; + thred->startFunc = start; + thred->priority = priority; + if (PR_UNJOINABLE_THREAD == state) { + thred->state |= PT_THREAD_DETACHED; + } + + if (PR_LOCAL_THREAD == scope) { + scope = PR_GLOBAL_THREAD; + } + + if (PR_GLOBAL_BOUND_THREAD == scope) { +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); + if (rv) { + /* + * system scope not supported + */ + scope = PR_GLOBAL_THREAD; + /* + * reset scope + */ + rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS); + PR_ASSERT(0 == rv); + } +#endif + } + if (PR_GLOBAL_THREAD == scope) { + thred->state |= PT_THREAD_GLOBAL; + } + else if (PR_GLOBAL_BOUND_THREAD == scope) { + thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND); + } + else { /* force it global */ + thred->state |= PT_THREAD_GLOBAL; + } + if (PR_SYSTEM_THREAD == type) { + thred->state |= PT_THREAD_SYSTEM; + } + + thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0; + + thred->stack = PR_NEWZAP(PRThreadStack); + if (thred->stack == NULL) { + PRIntn oserr = errno; + PR_Free(thred); /* all that work ... poof! */ + PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr); + thred = NULL; /* and for what? */ + goto done; + } + thred->stack->stackSize = stackSize; + thred->stack->thr = thred; + +#ifdef PT_NO_SIGTIMEDWAIT + pthread_mutex_init(&thred->suspendResumeMutex,NULL); + pthread_cond_init(&thred->suspendResumeCV,NULL); +#endif + + /* make the thread counted to the rest of the runtime */ + PR_Lock(pt_book.ml); + if (PR_SYSTEM_THREAD == type) { + pt_book.system += 1; + } + else { + pt_book.user += 1; + } + PR_Unlock(pt_book.ml); + + /* + * We pass a pointer to a local copy (instead of thred->id) + * to pthread_create() because who knows what wacky things + * pthread_create() may be doing to its argument. + */ + rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); + + if (EPERM == rv) + { + /* Remember that we don't have thread scheduling privilege. */ + pt_schedpriv = EPERM; + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("_PR_CreateThread: no thread scheduling privilege")); + /* Try creating the thread again without setting priority. */ +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED); + PR_ASSERT(0 == rv); +#endif + rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred); + } + + if (0 != rv) + { + PRIntn oserr = rv; + PR_Lock(pt_book.ml); + if (thred->state & PT_THREAD_SYSTEM) { + pt_book.system -= 1; + } + else if (--pt_book.user == pt_book.this_many) { + PR_NotifyAllCondVar(pt_book.cv); + } + PR_Unlock(pt_book.ml); + + PR_Free(thred->stack); + PR_Free(thred); /* all that work ... poof! */ + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr); + thred = NULL; /* and for what? */ + goto done; + } + + PR_Lock(pt_book.ml); + /* + * Both the parent thread and this new thread set thred->id. + * The parent thread must ensure that thred->id is set before + * PR_CreateThread() returns. (See comments in _pt_root().) + */ + if (!thred->idSet) + { + thred->id = id; + thred->idSet = PR_TRUE; + } + else + { + PR_ASSERT(pthread_equal(thred->id, id)); + } + + /* + * If the new thread is detached, tell it that PR_CreateThread() has + * accessed thred->id and thred->idSet so it's ok to delete thred. + */ + if (PR_UNJOINABLE_THREAD == state) + { + thred->okToDelete = PR_TRUE; + PR_NotifyAllCondVar(pt_book.cv); + } + PR_Unlock(pt_book.ml); + } + +done: + rv = _PT_PTHREAD_ATTR_DESTROY(&tattr); + PR_ASSERT(0 == rv); + + return thred; +} /* _PR_CreateThread */ + +PR_IMPLEMENT(PRThread*) PR_CreateThread( + PRThreadType type, void (*start)(void *arg), void *arg, + PRThreadPriority priority, PRThreadScope scope, + PRThreadState state, PRUint32 stackSize) +{ + return _PR_CreateThread( + type, start, arg, priority, scope, state, stackSize, PR_FALSE); +} /* PR_CreateThread */ + +PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble( + PRThreadType type, void (*start)(void *arg), void *arg, + PRThreadPriority priority, PRThreadScope scope, + PRThreadState state, PRUint32 stackSize) +{ + return _PR_CreateThread( + type, start, arg, priority, scope, state, stackSize, PR_TRUE); +} /* PR_CreateThreadGCAble */ + +PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred) +{ + return thred->environment; +} /* GetExecutionEnvironment */ + +PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env) +{ + thred->environment = env; +} /* SetExecutionEnvironment */ + +PR_IMPLEMENT(PRThread*) PR_AttachThread( + PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) +{ + return PR_GetCurrentThread(); +} /* PR_AttachThread */ + + +PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred) +{ + int rv = -1; + void *result = NULL; + PR_ASSERT(thred != NULL); + + if ((0xafafafaf == thred->state) + || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state)) + || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state))) + { + /* + * This might be a bad address, but if it isn't, the state should + * either be an unjoinable thread or it's already had the object + * deleted. However, the client that called join on a detached + * thread deserves all the rath I can muster.... + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + PR_LogPrint( + "PR_JoinThread: %p not joinable | already smashed\n", thred); + } + else + { + pthread_t id = thred->id; + rv = pthread_join(id, &result); + PR_ASSERT(rv == 0 && result == NULL); + if (0 == rv) + { + /* + * PR_FALSE, because the thread already called the TPD + * destructors before exiting _pt_root. + */ + _pt_thread_death_internal(thred, PR_FALSE); + } + else + { + PRErrorCode prerror; + switch (rv) + { + case EINVAL: /* not a joinable thread */ + case ESRCH: /* no thread with given ID */ + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + case EDEADLK: /* a thread joining with itself */ + prerror = PR_DEADLOCK_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prerror, rv); + } + } + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_JoinThread */ + +PR_IMPLEMENT(void) PR_DetachThread(void) +{ + void *thred; + int rv; + + _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); + if (NULL == thred) { + return; + } + _pt_thread_death(thred); + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); +} /* PR_DetachThread */ + +PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void) +{ + void *thred; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); + if (NULL == thred) { + thred = pt_AttachThread(); + } + PR_ASSERT(NULL != thred); + return (PRThread*)thred; +} /* PR_GetCurrentThread */ + +PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred) +{ + return (thred->state & PT_THREAD_BOUND) ? + PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD; +} /* PR_GetThreadScope() */ + +PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred) +{ + return (thred->state & PT_THREAD_SYSTEM) ? + PR_SYSTEM_THREAD : PR_USER_THREAD; +} + +PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred) +{ + return (thred->state & PT_THREAD_DETACHED) ? + PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; +} /* PR_GetThreadState */ + +PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred) +{ + PR_ASSERT(thred != NULL); + return thred->priority; +} /* PR_GetThreadPriority */ + +PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri) +{ + PRIntn rv; + + PR_ASSERT(NULL != thred); + + if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) { + newPri = PR_PRIORITY_FIRST; + } + else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) { + newPri = PR_PRIORITY_LAST; + } + +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + if (EPERM != pt_schedpriv) + { + int policy; + struct sched_param schedule; + + rv = pthread_getschedparam(thred->id, &policy, &schedule); + if(0 == rv) { + schedule.sched_priority = pt_PriorityMap(newPri); + rv = pthread_setschedparam(thred->id, policy, &schedule); + if (EPERM == rv) + { + pt_schedpriv = EPERM; + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_SetThreadPriority: no thread scheduling privilege")); + } + } + if (rv != 0) { + rv = -1; + } + } +#elif defined(_PR_NICE_PRIORITY_SCHEDULING) + PR_Lock(pt_book.ml); + while (thred->tid == 0) { + PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pt_book.ml); + + errno = 0; + rv = getpriority(PRIO_PROCESS, 0); + + /* Do not proceed unless we know the main thread's nice value. */ + if (errno == 0) { + rv = setpriority(PRIO_PROCESS, thred->tid, + pt_RelativePriority(rv, newPri)); + + if (rv == -1) + { + /* We don't set pt_schedpriv to EPERM in case errno == EPERM + * because adjusting the nice value might be permitted for certain + * ranges but not for others. */ + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_SetThreadPriority: setpriority failed with error %d", + errno)); + } + } +#else + (void)rv; /* rv is unused */ +#endif + + thred->priority = newPri; +} /* PR_SetThreadPriority */ + +PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred) +{ + /* + ** If the target thread indicates that it's waiting, + ** find the condition and broadcast to it. Broadcast + ** since we don't know which thread (if there are more + ** than one). This sounds risky, but clients must + ** test their invariants when resumed from a wait and + ** I don't expect very many threads to be waiting on + ** a single condition and I don't expect interrupt to + ** be used very often. + ** + ** I don't know why I thought this would work. Must have + ** been one of those weaker momements after I'd been + ** smelling the vapors. + ** + ** Even with the followng changes it is possible that + ** the pointer to the condition variable is pointing + ** at a bogus value. Will the unerlying code detect + ** that? + */ + PRCondVar *cv; + PR_ASSERT(NULL != thred); + if (NULL == thred) { + return PR_FAILURE; + } + + thred->state |= PT_THREAD_ABORTED; + + cv = thred->waiting; + if ((NULL != cv) && !thred->interrupt_blocked) + { + PRIntn rv; + (void)PR_ATOMIC_INCREMENT(&cv->notify_pending); + rv = pthread_cond_broadcast(&cv->cv); + PR_ASSERT(0 == rv); + if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) { + PR_DestroyCondVar(cv); + } + } + return PR_SUCCESS; +} /* PR_Interrupt */ + +PR_IMPLEMENT(void) PR_ClearInterrupt(void) +{ + PRThread *me = PR_GetCurrentThread(); + me->state &= ~PT_THREAD_ABORTED; +} /* PR_ClearInterrupt */ + +PR_IMPLEMENT(void) PR_BlockInterrupt(void) +{ + PRThread *me = PR_GetCurrentThread(); + _PT_THREAD_BLOCK_INTERRUPT(me); +} /* PR_BlockInterrupt */ + +PR_IMPLEMENT(void) PR_UnblockInterrupt(void) +{ + PRThread *me = PR_GetCurrentThread(); + _PT_THREAD_UNBLOCK_INTERRUPT(me); +} /* PR_UnblockInterrupt */ + +PR_IMPLEMENT(PRStatus) PR_Yield(void) +{ + static PRBool warning = PR_TRUE; + if (warning) warning = _PR_Obsolete( + "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); + return PR_Sleep(PR_INTERVAL_NO_WAIT); +} + +PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks) +{ + PRStatus rv = PR_SUCCESS; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (PR_INTERVAL_NO_WAIT == ticks) + { + _PT_PTHREAD_YIELD(); + } + else + { + PRCondVar *cv; + PRIntervalTime timein; + + timein = PR_IntervalNow(); + cv = PR_NewCondVar(_pr_sleeplock); + PR_ASSERT(cv != NULL); + PR_Lock(_pr_sleeplock); + do + { + PRIntervalTime now = PR_IntervalNow(); + PRIntervalTime delta = now - timein; + if (delta > ticks) { + break; + } + rv = PR_WaitCondVar(cv, ticks - delta); + } while (PR_SUCCESS == rv); + PR_Unlock(_pr_sleeplock); + PR_DestroyCondVar(cv); + } + return rv; +} /* PR_Sleep */ + +static void _pt_thread_death(void *arg) +{ + void *thred; + int rv; + + _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); + if (NULL == thred) + { + /* + * Have PR_GetCurrentThread return the expected value to the + * destructors. + */ + rv = pthread_setspecific(pt_book.key, arg); + PR_ASSERT(0 == rv); + } + + /* PR_TRUE for: call destructors */ + _pt_thread_death_internal(arg, PR_TRUE); + + if (NULL == thred) + { + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); + } +} + +static void _pt_thread_death_internal(void *arg, PRBool callDestructors) +{ + PRThread *thred = (PRThread*)arg; + + if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD)) + { + PR_Lock(pt_book.ml); + if (NULL == thred->prev) { + pt_book.first = thred->next; + } + else { + thred->prev->next = thred->next; + } + if (NULL == thred->next) { + pt_book.last = thred->prev; + } + else { + thred->next->prev = thred->prev; + } + PR_Unlock(pt_book.ml); + } + if (callDestructors) { + _PR_DestroyThreadPrivate(thred); + } + PR_Free(thred->privateData); + if (NULL != thred->errorString) { + PR_Free(thred->errorString); + } + if (NULL != thred->name) { + PR_Free(thred->name); + } + PR_Free(thred->stack); + if (NULL != thred->syspoll_list) { + PR_Free(thred->syspoll_list); + } +#if defined(_PR_POLL_WITH_SELECT) + if (NULL != thred->selectfd_list) { + PR_Free(thred->selectfd_list); + } +#endif +#if defined(DEBUG) + memset(thred, 0xaf, sizeof(PRThread)); +#endif /* defined(DEBUG) */ + PR_Free(thred); +} /* _pt_thread_death */ + +void _PR_InitThreads( + PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) +{ + int rv; + PRThread *thred; + + PR_ASSERT(priority == PR_PRIORITY_NORMAL); + +#ifdef _PR_NEED_PTHREAD_INIT + /* + * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily + * initialized, but pthread_self() fails to initialize + * pthreads and hence returns a null thread ID if invoked + * by the primordial thread before any other pthread call. + * So we explicitly initialize pthreads here. + */ + pthread_init(); +#endif + +#if _POSIX_THREAD_PRIORITY_SCHEDULING > 0 +#if defined(FREEBSD) + { + pthread_attr_t attr; + int policy; + /* get the min and max priorities of the default policy */ + pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_getschedpolicy(&attr, &policy); + pt_book.minPrio = sched_get_priority_min(policy); + PR_ASSERT(-1 != pt_book.minPrio); + pt_book.maxPrio = sched_get_priority_max(policy); + PR_ASSERT(-1 != pt_book.maxPrio); + pthread_attr_destroy(&attr); + } +#else + /* + ** These might be function evaluations + */ + pt_book.minPrio = PT_PRIO_MIN; + pt_book.maxPrio = PT_PRIO_MAX; +#endif +#endif + + PR_ASSERT(NULL == pt_book.ml); + pt_book.ml = PR_NewLock(); + PR_ASSERT(NULL != pt_book.ml); + pt_book.cv = PR_NewCondVar(pt_book.ml); + PR_ASSERT(NULL != pt_book.cv); + thred = PR_NEWZAP(PRThread); + PR_ASSERT(NULL != thred); + thred->arg = NULL; + thred->startFunc = NULL; + thred->priority = priority; + thred->id = pthread_self(); + thred->idSet = PR_TRUE; +#ifdef _PR_NICE_PRIORITY_SCHEDULING + thred->tid = gettid(); +#endif + + thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD); + if (PR_SYSTEM_THREAD == type) + { + thred->state |= PT_THREAD_SYSTEM; + pt_book.system += 1; + pt_book.this_many = 0; + } + else + { + pt_book.user += 1; + pt_book.this_many = 1; + } + thred->next = thred->prev = NULL; + pt_book.first = pt_book.last = thred; + + thred->stack = PR_NEWZAP(PRThreadStack); + PR_ASSERT(thred->stack != NULL); + thred->stack->stackSize = 0; + thred->stack->thr = thred; + _PR_InitializeStack(thred->stack); + + /* + * Create a key for our use to store a backpointer in the pthread + * to our PRThread object. This object gets deleted when the thread + * returns from its root in the case of a detached thread. Other + * threads delete the objects in Join. + * + * NB: The destructor logic seems to have a bug so it isn't used. + * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998. + * More info - the problem is that pthreads calls the destructor + * eagerly as the thread returns from its root, rather than lazily + * after the thread is joined. Therefore, threads that are joining + * and holding PRThread references are actually holding pointers to + * nothing. + */ + rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death); + if (0 != rv) { + PR_Assert("0 == rv", __FILE__, __LINE__); + } + pt_book.keyCreated = PR_TRUE; + rv = pthread_setspecific(pt_book.key, thred); + PR_ASSERT(0 == rv); +} /* _PR_InitThreads */ + +#ifdef __GNUC__ +/* + * GCC supports the constructor and destructor attributes as of + * version 2.5. + */ +#if defined(DARWIN) +/* + * The dynamic linker on OSX doesn't execute __attribute__((destructor)) + * destructors in the right order wrt non-__attribute((destructor)) destructors + * in other libraries. So use atexit() instead, which does. + * See https://bugzilla.mozilla.org/show_bug.cgi?id=1399746#c99 + */ +static void _PR_Fini(void); + +__attribute__ ((constructor)) +static void _register_PR_Fini() { + atexit(_PR_Fini); +} +#else +static void _PR_Fini(void) __attribute__ ((destructor)); +#endif + +#elif defined(__SUNPRO_C) +/* + * Sun Studio compiler + */ +#pragma fini(_PR_Fini) +static void _PR_Fini(void); +#elif defined(HPUX) +/* + * Current versions of HP C compiler define __HP_cc. + * HP C compiler A.11.01.20 doesn't define __HP_cc. + */ +#if defined(__ia64) || defined(_LP64) +#pragma FINI "_PR_Fini" +static void _PR_Fini(void); +#else +/* + * Only HP-UX 10.x style initializers are supported in 32-bit links. + * Need to use the +I PR_HPUX10xInit linker option. + */ +#include <dl.h> + +static void _PR_Fini(void); + +void PR_HPUX10xInit(shl_t handle, int loading) +{ + /* + * This function is called when a shared library is loaded as well + * as when the shared library is unloaded. Note that it may not + * be called when the user's program terminates. + * + * handle is the shl_load API handle for the shared library being + * initialized. + * + * loading is non-zero at startup and zero at termination. + */ + if (loading) { + /* ... do some initializations ... */ + } else { + _PR_Fini(); + } +} +#endif +#elif defined(AIX) +/* Need to use the -binitfini::_PR_Fini linker option. */ +#endif + +void _PR_Fini(void) +{ + void *thred; + int rv; + + if (!_pr_initialized) { + /* Either NSPR was never successfully initialized or + * PR_Cleanup has been called already. */ + if (pt_book.keyCreated) + { + rv = pthread_key_delete(pt_book.key); + PR_ASSERT(0 == rv); + pt_book.keyCreated = PR_FALSE; + } + return; + } + + _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred); + if (NULL != thred) + { + /* + * PR_FALSE, because it is unsafe to call back to the + * thread private data destructors at final cleanup. + */ + _pt_thread_death_internal(thred, PR_FALSE); + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); + } + rv = pthread_key_delete(pt_book.key); + PR_ASSERT(0 == rv); + pt_book.keyCreated = PR_FALSE; + /* TODO: free other resources used by NSPR */ + /* _pr_initialized = PR_FALSE; */ +} /* _PR_Fini */ + +PR_IMPLEMENT(PRStatus) PR_Cleanup(void) +{ + PRThread *me = PR_GetCurrentThread(); + int rv; + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); + PR_ASSERT(me->state & PT_THREAD_PRIMORD); + if (me->state & PT_THREAD_PRIMORD) + { + PR_Lock(pt_book.ml); + while (pt_book.user > pt_book.this_many) { + PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); + } + if (me->state & PT_THREAD_SYSTEM) { + pt_book.system -= 1; + } + else { + pt_book.user -= 1; + } + PR_Unlock(pt_book.ml); + + _PR_MD_EARLY_CLEANUP(); + + _PR_CleanupMW(); + _PR_CleanupTime(); + _PR_CleanupDtoa(); + _PR_CleanupCallOnce(); + _PR_ShutdownLinker(); + _PR_LogCleanup(); + _PR_CleanupNet(); + /* Close all the fd's before calling _PR_CleanupIO */ + _PR_CleanupIO(); + _PR_CleanupCMon(); + + _pt_thread_death(me); + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); + /* + * I am not sure if it's safe to delete the cv and lock here, + * since there may still be "system" threads around. If this + * call isn't immediately prior to exiting, then there's a + * problem. + */ + if (0 == pt_book.system) + { + PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL; + PR_DestroyLock(pt_book.ml); pt_book.ml = NULL; + } + PR_DestroyLock(_pr_sleeplock); + _pr_sleeplock = NULL; + _PR_CleanupLayerCache(); + _PR_CleanupEnv(); +#ifdef _PR_ZONE_ALLOCATOR + _PR_DestroyZones(); +#endif + _pr_initialized = PR_FALSE; + return PR_SUCCESS; + } + return PR_FAILURE; +} /* PR_Cleanup */ + +PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) +{ + _exit(status); +} + +PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred) +{ + return (PRUint32)thred->id; /* and I don't know what they will do with it */ +} + +/* + * $$$ + * The following two thread-to-processor affinity functions are not + * yet implemented for pthreads. By the way, these functions should return + * PRStatus rather than PRInt32 to indicate the success/failure status. + * $$$ + */ + +PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) +{ + return 0; /* not implemented */ +} + +PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) +{ + return 0; /* not implemented */ +} + +PR_IMPLEMENT(void) +PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg) +{ + thread->dump = dump; + thread->dumpArg = arg; +} + +/* + * Garbage collection support follows. + */ + +/* a bogus signal mask for forcing a timed wait */ +/* Not so bogus in AIX as we really do a sigwait */ +static sigset_t sigwait_set; + +static struct timespec onemillisec = {0, 1000000L}; +#ifndef PT_NO_SIGTIMEDWAIT +static struct timespec hundredmillisec = {0, 100000000L}; +#endif + +static void suspend_signal_handler(PRIntn sig); + +#ifdef PT_NO_SIGTIMEDWAIT +static void null_signal_handler(PRIntn sig); +#endif + +/* + * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which + * conflict with the use of these two signals in our GC support. + * So we don't know how to support GC on Linux pthreads. + */ +static void init_pthread_gc_support(void) +{ + PRIntn rv; + + { + struct sigaction sigact_usr2; + + sigact_usr2.sa_handler = suspend_signal_handler; + sigact_usr2.sa_flags = SA_RESTART; + sigemptyset (&sigact_usr2.sa_mask); + + rv = sigaction (SIGUSR2, &sigact_usr2, NULL); + PR_ASSERT(0 == rv); + + sigemptyset (&sigwait_set); +#if defined(PT_NO_SIGTIMEDWAIT) + sigaddset (&sigwait_set, SIGUSR1); +#else + sigaddset (&sigwait_set, SIGUSR2); +#endif /* defined(PT_NO_SIGTIMEDWAIT) */ + } +#if defined(PT_NO_SIGTIMEDWAIT) + { + struct sigaction sigact_null; + sigact_null.sa_handler = null_signal_handler; + sigact_null.sa_flags = SA_RESTART; + sigemptyset (&sigact_null.sa_mask); + rv = sigaction (SIGUSR1, &sigact_null, NULL); + PR_ASSERT(0 ==rv); + } +#endif /* defined(PT_NO_SIGTIMEDWAIT) */ +} + +PR_IMPLEMENT(void) PR_SetThreadGCAble(void) +{ + PR_Lock(pt_book.ml); + PR_GetCurrentThread()->state |= PT_THREAD_GCABLE; + PR_Unlock(pt_book.ml); +} + +PR_IMPLEMENT(void) PR_ClearThreadGCAble(void) +{ + PR_Lock(pt_book.ml); + PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE); + PR_Unlock(pt_book.ml); +} + +#if defined(DEBUG) +static PRBool suspendAllOn = PR_FALSE; +#endif + +static PRBool suspendAllSuspended = PR_FALSE; + +PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) +{ + PRIntn count = 0; + PRStatus rv = PR_SUCCESS; + PRThread* thred = pt_book.first; + +#if defined(DEBUG) || defined(FORCE_PR_ASSERT) + PRThread *me = PR_GetCurrentThread(); +#endif + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n")); + /* + * $$$ + * Need to suspend all threads other than me before doing this. + * This is really a gross and disgusting thing to do. The only + * good thing is that since all other threads are suspended, holding + * the lock during a callback seems like child's play. + * $$$ + */ + PR_ASSERT(suspendAllOn); + + while (thred != NULL) + { + /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking + * qp->next after applying the function "func". In particular, "func" + * might remove the thread from the queue and put it into another one in + * which case qp->next no longer points to the next entry in the original + * queue. + * + * To get around this problem, we save qp->next in qp_next before applying + * "func" and use that saved value as the next value after applying "func". + */ + PRThread* next = thred->next; + + if (_PT_IS_GCABLE_THREAD(thred)) + { + PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED)); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("In PR_EnumerateThreads callback thread %p thid = %X\n", + thred, thred->id)); + + rv = func(thred, count++, arg); + if (rv != PR_SUCCESS) { + return rv; + } + } + thred = next; + } + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End PR_EnumerateThreads count = %d \n", count)); + return rv; +} /* PR_EnumerateThreads */ + +/* + * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy + * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend. + * The signal handler will record the stack pointer and will block until resumed by + * the resume call. Since the signal handler is the last routine called for the + * suspended thread, the stack pointer will also serve as a place where all the + * registers have been saved on the stack for the previously executing routines. + * + * Through global variables, we also make sure that PR_Suspend and PR_Resume does not + * proceed until the thread is suspended or resumed. + */ + +/* + * In the signal handler, we can not use condition variable notify or wait. + * This does not work consistently across all pthread platforms. We also can not + * use locking since that does not seem to work reliably across platforms. + * Only thing we can do is yielding while testing for a global condition + * to change. This does work on pthread supported platforms. We may have + * to play with priortities if there are any problems detected. + */ + +/* + * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps + * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no + * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually + * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java, + * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal + * handler as all synchronization mechanisms just break down. + */ + +#if defined(PT_NO_SIGTIMEDWAIT) +static void null_signal_handler(PRIntn sig) +{ + return; +} +#endif + +static void suspend_signal_handler(PRIntn sig) +{ + PRThread *me = PR_GetCurrentThread(); + + PR_ASSERT(me != NULL); + PR_ASSERT(_PT_IS_GCABLE_THREAD(me)); + PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("Begin suspend_signal_handler thred %p thread id = %X\n", + me, me->id)); + + /* + * save stack pointer + */ + me->sp = &me; + + /* + At this point, the thread's stack pointer has been saved, + And it is going to enter a wait loop until it is resumed. + So it is _really_ suspended + */ + + me->suspend |= PT_THREAD_SUSPENDED; + + /* + * now, block current thread + */ +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_cond_signal(&me->suspendResumeCV); + while (me->suspend & PT_THREAD_SUSPENDED) + { +#if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \ + && !defined(BSDI) && !defined(UNIXWARE) \ + && !defined(DARWIN) && !defined(RISCOS) + PRIntn rv; + sigwait(&sigwait_set, &rv); +#endif + } + me->suspend |= PT_THREAD_RESUMED; + pthread_cond_signal(&me->suspendResumeCV); +#else /* defined(PT_NO_SIGTIMEDWAIT) */ + while (me->suspend & PT_THREAD_SUSPENDED) + { + PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec); + PR_ASSERT(-1 == rv); + } + me->suspend |= PT_THREAD_RESUMED; +#endif + + /* + * At this point, thread has been resumed, so set a global condition. + * The ResumeAll needs to know that this has really been resumed. + * So the signal handler sets a flag which PR_ResumeAll will reset. + * The PR_ResumeAll must reset this flag ... + */ + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id)); +} /* suspend_signal_handler */ + +static void pt_SuspendSet(PRThread *thred) +{ + PRIntn rv; + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id)); + + + /* + * Check the thread state and signal the thread to suspend + */ + + PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n", + thred, thred->id)); + rv = pthread_kill (thred->id, SIGUSR2); + PR_ASSERT(0 == rv); +} + +static void pt_SuspendTest(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id)); + + + /* + * Wait for the thread to be really suspended. This happens when the + * suspend signal handler stores the stack pointer and sets the state + * to suspended. + */ + +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_mutex_lock(&thred->suspendResumeMutex); + while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) + { + pthread_cond_timedwait( + &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec); + } + pthread_mutex_unlock(&thred->suspendResumeMutex); +#else + while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) + { + PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); + PR_ASSERT(-1 == rv); + } +#endif + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id)); +} /* pt_SuspendTest */ + +static void pt_ResumeSet(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id)); + + /* + * Clear the global state and set the thread state so that it will + * continue past yield loop in the suspend signal handler + */ + + PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED); + + + thred->suspend &= ~PT_THREAD_SUSPENDED; + +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_kill(thred->id, SIGUSR1); +#endif + +} /* pt_ResumeSet */ + +static void pt_ResumeTest(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id)); + + /* + * Wait for the threads resume state to change + * to indicate it is really resumed + */ +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_mutex_lock(&thred->suspendResumeMutex); + while ((thred->suspend & PT_THREAD_RESUMED) == 0) + { + pthread_cond_timedwait( + &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec); + } + pthread_mutex_unlock(&thred->suspendResumeMutex); +#else + while ((thred->suspend & PT_THREAD_RESUMED) == 0) { + PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); + PR_ASSERT(-1 == rv); + } +#endif + + thred->suspend &= ~PT_THREAD_RESUMED; + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ( + "End pt_ResumeTest thred %p tid %X\n", thred, thred->id)); +} /* pt_ResumeTest */ + +static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT; + +PR_IMPLEMENT(void) PR_SuspendAll(void) +{ +#ifdef DEBUG + PRIntervalTime stime, etime; +#endif + PRThread* thred = pt_book.first; + PRThread *me = PR_GetCurrentThread(); + int rv; + + rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support); + PR_ASSERT(0 == rv); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); + /* + * Stop all threads which are marked GC able. + */ + PR_Lock(pt_book.ml); +#ifdef DEBUG + suspendAllOn = PR_TRUE; + stime = PR_IntervalNow(); +#endif + while (thred != NULL) + { + if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) { + pt_SuspendSet(thred); + } + thred = thred->next; + } + + /* Wait till they are really suspended */ + thred = pt_book.first; + while (thred != NULL) + { + if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) { + pt_SuspendTest(thred); + } + thred = thred->next; + } + + suspendAllSuspended = PR_TRUE; + +#ifdef DEBUG + etime = PR_IntervalNow(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\ + ("End PR_SuspendAll (time %dms)\n", + PR_IntervalToMilliseconds(etime - stime))); +#endif +} /* PR_SuspendAll */ + +PR_IMPLEMENT(void) PR_ResumeAll(void) +{ +#ifdef DEBUG + PRIntervalTime stime, etime; +#endif + PRThread* thred = pt_book.first; + PRThread *me = PR_GetCurrentThread(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); + /* + * Resume all previously suspended GC able threads. + */ + suspendAllSuspended = PR_FALSE; +#ifdef DEBUG + stime = PR_IntervalNow(); +#endif + + while (thred != NULL) + { + if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) { + pt_ResumeSet(thred); + } + thred = thred->next; + } + + thred = pt_book.first; + while (thred != NULL) + { + if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) { + pt_ResumeTest(thred); + } + thred = thred->next; + } + + PR_Unlock(pt_book.ml); +#ifdef DEBUG + suspendAllOn = PR_FALSE; + etime = PR_IntervalNow(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End PR_ResumeAll (time %dms)\n", + PR_IntervalToMilliseconds(etime - stime))); +#endif +} /* PR_ResumeAll */ + +/* Return the stack pointer for the given thread- used by the GC */ +PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("in PR_GetSP thred %p thid = %X, sp = %p\n", + thred, thred->id, thred->sp)); + return thred->sp; +} /* PR_GetSP */ + +PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name) +{ + PRThread *thread; + size_t nameLen; + int result = 0; + + if (!name) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + thread = PR_GetCurrentThread(); + if (!thread) { + return PR_FAILURE; + } + + PR_Free(thread->name); + nameLen = strlen(name); + thread->name = (char *)PR_Malloc(nameLen + 1); + if (!thread->name) { + return PR_FAILURE; + } + memcpy(thread->name, name, nameLen + 1); + +#if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY) + pthread_set_name_np(thread->id, name); +#elif defined(ANDROID) + prctl(PR_SET_NAME, (unsigned long)(name)); +#elif defined(NETBSD) + result = pthread_setname_np(thread->id, "%s", (void *)name); +#else /* not BSD */ + /* + * On OSX, pthread_setname_np is only available in 10.6 or later, so test + * for it at runtime. It also may not be available on all linux distros. + */ +#if defined(DARWIN) + int (*dynamic_pthread_setname_np)(const char*); +#else + int (*dynamic_pthread_setname_np)(pthread_t, const char*); +#endif + + *(void**)(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + if (!dynamic_pthread_setname_np) { + return PR_SUCCESS; + } + +#if defined(DARWIN) + /* Mac OS X has a length limit of 63 characters, but there is no API + * exposing it. + */ +#define SETNAME_LENGTH_CONSTRAINT 63 +#else + /* + * The 15-character name length limit is an experimentally determined + * length of a null-terminated string that most linux distros accept + * as an argument to pthread_setname_np. Otherwise the E2BIG + * error is returned by the function. + */ +#define SETNAME_LENGTH_CONSTRAINT 15 +#endif +#define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1) +#define SETNAME_FRAGMENT2_LENGTH \ + (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1) + char name_dup[SETNAME_LENGTH_CONSTRAINT + 1]; + if (nameLen > SETNAME_LENGTH_CONSTRAINT) { + memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH); + name_dup[SETNAME_FRAGMENT1_LENGTH] = '~'; + /* Note that this also copies the null terminator. */ + memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1, + name + nameLen - SETNAME_FRAGMENT2_LENGTH, + SETNAME_FRAGMENT2_LENGTH + 1); + name = name_dup; + } + +#if defined(DARWIN) + result = dynamic_pthread_setname_np(name); +#else + result = dynamic_pthread_setname_np(thread->id, name); +#endif +#endif /* not BSD */ + + if (result) { + PR_SetError(PR_UNKNOWN_ERROR, result); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread) +{ + if (!thread) { + return NULL; + } + return thread->name; +} + +#endif /* defined(_PR_PTHREADS) */ + +/* ptthread.c */ diff --git a/nsprpub/pr/src/threads/Makefile.in b/nsprpub/pr/src/threads/Makefile.in new file mode 100644 index 0000000000..5c5c254e3a --- /dev/null +++ b/nsprpub/pr/src/threads/Makefile.in @@ -0,0 +1,62 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +ifdef USE_PTHREADS + DIRS = +else +ifdef USE_BTHREADS + DIRS = +else + DIRS = combined +endif +endif + +ifdef USE_PTHREADS +CSRCS = \ + prcmon.c \ + prrwlock.c \ + prtpd.c \ + $(NULL) +else +ifdef USE_BTHREADS +CSRCS = \ + prcmon.c \ + prrwlock.c \ + prtpd.c \ + $(NULL) +else +CSRCS = \ + prcmon.c \ + prdump.c \ + prmon.c \ + prsem.c \ + prrwlock.c \ + prcthr.c \ + prtpd.c \ + $(NULL) +endif +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/threads/combined/Makefile.in b/nsprpub/pr/src/threads/combined/Makefile.in new file mode 100644 index 0000000000..14b9eac007 --- /dev/null +++ b/nsprpub/pr/src/threads/combined/Makefile.in @@ -0,0 +1,40 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +ifdef USE_PTHREADS +CSRCS = \ + $(NULL) +else +CSRCS = \ + prucpu.c \ + prucv.c \ + prulock.c \ + pruthr.c \ + prustack.c \ + $(NULL) +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +DEFINES += -D_NSPR_BUILD_ + +include $(topsrcdir)/config/rules.mk + +export:: $(TARGETS) + diff --git a/nsprpub/pr/src/threads/combined/README b/nsprpub/pr/src/threads/combined/README new file mode 100644 index 0000000000..aa26665282 --- /dev/null +++ b/nsprpub/pr/src/threads/combined/README @@ -0,0 +1,62 @@ +NSPR 2.0 evolution +------------------ + + +Phase 1- today + +Currently (Oct 10, 1996) NSPR 2.0 has two modes. Either _PR_NTHREAD +is defined, in which case the PR_CreateThread() call always creates a +native kernel thread, or _PR_NTHREAD is not defined and PR_CreateThread() +always creates user level threads within the single, original process. This +source code is reflected in two directories, nspr20/pr/src/threads/native, and +nspr20/pr/src/threads/user. Although the PR_CreateThread() function has +a paramter to specify the "scope" of a thread, this parameter is not yet +used- except on solaris where it uses it to specify bound vs unbound threads. + +Phase 2 - next week + +The next step is to provide a combination of user and native threads. The +idea, of course, is to have some small number of native threads and each of +those threads be able to run user level threads. The number of native +threads created will most likely be proportional to the number of CPUs in +the system. For this reason, the specific set of native threads which are +used to run the user-level threads will be called "CPU" threads. + +The user level threads which will be run on the CPU threads are able to +run on any of the CPU threads available, and over the course of a user-level +thread's lifetime, it may drift from one CPU thread to another. All +user-level threads will compete for processing time via a single run queue. + +Creation of a CPU thread will be primarily controlled by NSPR itself or by +the user running a function PR_Concurrency(). The details of PR_Concurrency() +have not yet been worked out; but the idea is that the user can specify to +NSPR how many CPU threads are desired. + +In this system, user-level threads are created by using PR_CreateThread() and +specifying the PR_LOCAL_SCOPE option. LOCAL_SCOPE indicates that the thread +will be under the control of the "local" scheduler. Creating threads with +GLOBAL_SCOPE, on the other hand will create a thread which is under the +control of the system's scheduler. In otherwords, this creates a native thread +which is not a CPU thread; it runs a single thread task and never has more +than one task to run. LOCAL_SCOPE is much like creating a Solaris unbound +thread, while GLOBAL_SCOPE is similar to creating a Solaris bound thread. + +To implement this architecture, the source code will still maintain the "user" +and "native" directories which is has today. However a third directory +"combined" will also exist. To compile a version of NSPR which only creates +native threads, the user can define _PR_NTHREAD. For exclusive user-level +threads, do not define _PR_NTHREAD. To get the combined threads, define +_PR_NTHREAD and _PR_USE_CPUS. + + +Phase 3 - later than next week + +The goal is to eliminate the 3 directories. Once these three models are in +place, the remaining work will be to eliminate the native and user thread +directories for all platforms, so that the entire thread model is contained +within what is today called the "combined" model. This new and glorious +source code will attempt to make the "combined" model on any platforms which +provide the necessary underlying native threading, but will also be +capable of using exclusive user-level threads on systems which don't have +native threads. + diff --git a/nsprpub/pr/src/threads/combined/prucpu.c b/nsprpub/pr/src/threads/combined/prucpu.c new file mode 100644 index 0000000000..25ffcce862 --- /dev/null +++ b/nsprpub/pr/src/threads/combined/prucpu.c @@ -0,0 +1,424 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +_PRCPU *_pr_primordialCPU = NULL; + +PRInt32 _pr_md_idle_cpus; /* number of idle cpus */ +/* + * The idle threads in MxN models increment/decrement _pr_md_idle_cpus. + * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic + * increment/decrement routines (which are based on PR_Lock/PR_Unlock), + * because PR_Lock asserts that the calling thread is not an idle thread. + * So we use a _MDLock to protect _pr_md_idle_cpus. + */ +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifndef _PR_HAVE_ATOMIC_OPS +static _MDLock _pr_md_idle_cpus_lock; +#endif +#endif +PRUintn _pr_numCPU; +PRInt32 _pr_cpus_exit; +PRUint32 _pr_cpu_affinity_mask = 0; + +#if !defined (_PR_GLOBAL_THREADS_ONLY) + +static PRUintn _pr_cpuID; + +static void PR_CALLBACK _PR_CPU_Idle(void *); + +static _PRCPU *_PR_CreateCPU(void); +static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread); + +#if !defined(_PR_LOCAL_THREADS_ONLY) +static void _PR_RunCPU(void *arg); +#endif + +void _PR_InitCPUs() +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_native_threads_only) { + return; + } + + _pr_cpuID = 0; + _MD_NEW_LOCK( &_pr_cpuLock); +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifndef _PR_HAVE_ATOMIC_OPS + _MD_NEW_LOCK(&_pr_md_idle_cpus_lock); +#endif +#endif + +#ifdef _PR_LOCAL_THREADS_ONLY + +#ifdef HAVE_CUSTOM_USER_THREADS + _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); +#endif + + /* Now start the first CPU. */ + _pr_primordialCPU = _PR_CreateCPU(); + _pr_numCPU = 1; + _PR_StartCPU(_pr_primordialCPU, me); + + _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU); + + /* Initialize cpu for current thread (could be different from me) */ + _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU; + + _PR_MD_SET_LAST_THREAD(me); + +#else /* Combined MxN model */ + + _pr_primordialCPU = _PR_CreateCPU(); + _pr_numCPU = 1; + _PR_CreateThread(PR_SYSTEM_THREAD, + _PR_RunCPU, + _pr_primordialCPU, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, + _PR_IDLE_THREAD); + +#endif /* _PR_LOCAL_THREADS_ONLY */ + + _PR_MD_INIT_CPUS(); +} + +#ifdef WINNT +/* + * Right now this function merely stops the CPUs and does + * not do any other cleanup. + * + * It is only implemented for WINNT because bug 161998 only + * affects the WINNT version of NSPR, but it would be nice + * to implement this function for other platforms too. + */ +void _PR_CleanupCPUs(void) +{ + PRUintn i; + PRCList *qp; + _PRCPU *cpu; + + _pr_cpus_exit = 1; + for (i = 0; i < _pr_numCPU; i++) { + _PR_MD_WAKEUP_WAITER(NULL); + } + for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { + cpu = _PR_CPU_PTR(qp); + _PR_MD_JOIN_THREAD(&cpu->thread->md); + } +} +#endif + +static _PRCPUQueue *_PR_CreateCPUQueue(void) +{ + PRInt32 index; + _PRCPUQueue *cpuQueue; + cpuQueue = PR_NEWZAP(_PRCPUQueue); + + _MD_NEW_LOCK( &cpuQueue->runQLock ); + _MD_NEW_LOCK( &cpuQueue->sleepQLock ); + _MD_NEW_LOCK( &cpuQueue->miscQLock ); + + for (index = 0; index < PR_ARRAY_SIZE(cpuQueue->runQ); index++) { + PR_INIT_CLIST( &(cpuQueue->runQ[index]) ); + } + PR_INIT_CLIST( &(cpuQueue->sleepQ) ); + PR_INIT_CLIST( &(cpuQueue->pauseQ) ); + PR_INIT_CLIST( &(cpuQueue->suspendQ) ); + PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) ); + + cpuQueue->numCPUs = 1; + + return cpuQueue; +} + +/* + * Create a new CPU. + * + * This function initializes enough of the _PRCPU structure so + * that it can be accessed safely by a global thread or another + * CPU. This function does not create the native thread that + * will run the CPU nor does it initialize the parts of _PRCPU + * that must be initialized by that native thread. + * + * The reason we cannot simply have the native thread create + * and fully initialize a new CPU is that we need to be able to + * create a usable _pr_primordialCPU in _PR_InitCPUs without + * assuming that the primordial CPU thread we created can run + * during NSPR initialization. For example, on Windows while + * new threads can be created by DllMain, they won't be able + * to run during DLL initialization. If NSPR is initialized + * by DllMain, the primordial CPU thread won't run until DLL + * initialization is finished. + */ +static _PRCPU *_PR_CreateCPU(void) +{ + _PRCPU *cpu; + + cpu = PR_NEWZAP(_PRCPU); + if (cpu) { + cpu->queue = _PR_CreateCPUQueue(); + if (!cpu->queue) { + PR_DELETE(cpu); + return NULL; + } + } + return cpu; +} + +/* + * Start a new CPU. + * + * 'cpu' is a _PRCPU structure created by _PR_CreateCPU(). + * 'thread' is the native thread that will run the CPU. + * + * If this function fails, 'cpu' is destroyed. + */ +static PRStatus _PR_StartCPU(_PRCPU *cpu, PRThread *thread) +{ + /* + ** Start a new cpu. The assumption this code makes is that the + ** underlying operating system creates a stack to go with the new + ** native thread. That stack will be used by the cpu when pausing. + */ + + PR_ASSERT(!_native_threads_only); + + cpu->last_clock = PR_IntervalNow(); + + /* Before we create any threads on this CPU we have to + * set the current CPU + */ + _PR_MD_SET_CURRENT_CPU(cpu); + _PR_MD_INIT_RUNNING_CPU(cpu); + thread->cpu = cpu; + + cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD, + _PR_CPU_Idle, + (void *)cpu, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, + _PR_IDLE_THREAD); + + if (!cpu->idle_thread) { + /* didn't clean up CPU queue XXXMB */ + PR_DELETE(cpu); + return PR_FAILURE; + } + PR_ASSERT(cpu->idle_thread->cpu == cpu); + + cpu->idle_thread->no_sched = 0; + + cpu->thread = thread; + + if (_pr_cpu_affinity_mask) { + PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask); + } + + /* Created and started a new CPU */ + _PR_CPU_LIST_LOCK(); + cpu->id = _pr_cpuID++; + PR_APPEND_LINK(&cpu->links, &_PR_CPUQ()); + _PR_CPU_LIST_UNLOCK(); + + return PR_SUCCESS; +} + +#if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY) +/* +** This code is used during a cpu's initial creation. +*/ +static void _PR_RunCPU(void *arg) +{ + _PRCPU *cpu = (_PRCPU *)arg; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(NULL != me); + + /* + * _PR_StartCPU 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_StartCPU 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 started, 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 + + me->no_sched = 1; + _PR_StartCPU(cpu, me); + +#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; + + while(1) { + PRInt32 is; + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_MD_START_INTERRUPTS(); + _PR_MD_SWITCH_CONTEXT(me); + } +} +#endif + +static void PR_CALLBACK _PR_CPU_Idle(void *_cpu) +{ + _PRCPU *cpu = (_PRCPU *)_cpu; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(NULL != me); + + me->cpu = cpu; + cpu->idle_thread = me; + if (_MD_LAST_THREAD()) { + _MD_LAST_THREAD()->no_sched = 0; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_MD_SET_INTSOFF(0); + } + while(1) { + PRInt32 is; + PRIntervalTime timeout; + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + + _PR_RUNQ_LOCK(cpu); +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifdef _PR_HAVE_ATOMIC_OPS + _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus); +#else + _PR_MD_LOCK(&_pr_md_idle_cpus_lock); + _pr_md_idle_cpus++; + _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock); +#endif /* _PR_HAVE_ATOMIC_OPS */ +#endif + /* If someone on runq; do a nonblocking PAUSECPU */ + if (_PR_RUNQREADYMASK(me->cpu) != 0) { + _PR_RUNQ_UNLOCK(cpu); + timeout = PR_INTERVAL_NO_WAIT; + } else { + _PR_RUNQ_UNLOCK(cpu); + + _PR_SLEEPQ_LOCK(cpu); + if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) { + timeout = PR_INTERVAL_NO_TIMEOUT; + } else { + PRThread *wakeThread; + wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next); + timeout = wakeThread->sleep; + } + _PR_SLEEPQ_UNLOCK(cpu); + } + + /* Wait for an IO to complete */ + (void)_PR_MD_PAUSE_CPU(timeout); + +#ifdef WINNT + if (_pr_cpus_exit) { + /* _PR_CleanupCPUs tells us to exit */ + _PR_MD_END_THREAD(); + } +#endif + +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifdef _PR_HAVE_ATOMIC_OPS + _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus); +#else + _PR_MD_LOCK(&_pr_md_idle_cpus_lock); + _pr_md_idle_cpus--; + _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock); +#endif /* _PR_HAVE_ATOMIC_OPS */ +#endif + + _PR_ClockInterrupt(); + + /* Now schedule any thread that is on the runq + * INTS must be OFF when calling PR_Schedule() + */ + me->state = _PR_RUNNABLE; + _PR_MD_SWITCH_CONTEXT(me); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + } +} +#endif /* _PR_GLOBAL_THREADS_ONLY */ + +PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) +{ +#if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_LOCAL_THREADS_ONLY) + + /* do nothing */ + +#else /* combined, MxN thread model */ + + PRUintn newCPU; + _PRCPU *cpu; + PRThread *thr; + + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (_native_threads_only) { + return; + } + + _PR_CPU_LIST_LOCK(); + if (_pr_numCPU < numCPUs) { + newCPU = numCPUs - _pr_numCPU; + _pr_numCPU = numCPUs; + } else { + newCPU = 0; + } + _PR_CPU_LIST_UNLOCK(); + + for (; newCPU; newCPU--) { + cpu = _PR_CreateCPU(); + thr = _PR_CreateThread(PR_SYSTEM_THREAD, + _PR_RunCPU, + cpu, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, + _PR_IDLE_THREAD); + } +#endif +} + +PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void) +{ + if (_pr_primordialCPU) { + return _pr_primordialCPU; + } + else { + return _PR_MD_CURRENT_CPU(); + } +} diff --git a/nsprpub/pr/src/threads/combined/prucv.c b/nsprpub/pr/src/threads/combined/prucv.c new file mode 100644 index 0000000000..801d3d2fdd --- /dev/null +++ b/nsprpub/pr/src/threads/combined/prucv.c @@ -0,0 +1,679 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "primpl.h" +#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 +** Caller must hold the _PR_CVAR_LOCK(cv) +*/ +PRBool _PR_NotifyThread (PRThread *thread, PRThread *me) +{ + PRBool rv; + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + _PR_THREAD_LOCK(thread); + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + if (thread->wait.cvar != NULL) { + thread->wait.cvar = NULL; + + _PR_SLEEPQ_LOCK(thread->cpu); + /* The notify and timeout can collide; in which case both may + * attempt to delete from the sleepQ; only let one do it. + */ + if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) { + _PR_DEL_SLEEPQ(thread, PR_TRUE); + } + _PR_SLEEPQ_UNLOCK(thread->cpu); + + if (thread->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; a Resume operation + * on the thread will move it to the runQ + */ + thread->state = _PR_SUSPENDED; + _PR_MISCQ_LOCK(thread->cpu); + _PR_ADD_SUSPENDQ(thread, thread->cpu); + _PR_MISCQ_UNLOCK(thread->cpu); + _PR_THREAD_UNLOCK(thread); + } else { + /* Make thread runnable */ + thread->state = _PR_RUNNABLE; + _PR_THREAD_UNLOCK(thread); + + _PR_AddThreadToRunQ(me, thread); + _PR_MD_WAKEUP_WAITER(thread); + } + + rv = PR_TRUE; + } else { + /* Thread has already been notified */ + _PR_THREAD_UNLOCK(thread); + rv = PR_FALSE; + } + } else { /* If the thread is a native thread */ + if (thread->wait.cvar) { + thread->wait.cvar = NULL; + + if (thread->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; a Resume operation + * on the thread will enable the thread to run + */ + thread->state = _PR_SUSPENDED; + } else { + thread->state = _PR_RUNNING; + } + _PR_THREAD_UNLOCK(thread); + _PR_MD_WAKEUP_WAITER(thread); + rv = PR_TRUE; + } else { + _PR_THREAD_UNLOCK(thread); + rv = PR_FALSE; + } + } + + return rv; +} + +/* + * Notify thread waiting on cvar; called when thread is interrupted + * The thread lock is held on entry and released before return + */ +void _PR_NotifyLockedThread (PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRCondVar *cvar; + PRThreadPriority pri; + + if ( !_PR_IS_NATIVE_THREAD(me)) { + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + } + + cvar = thread->wait.cvar; + thread->wait.cvar = NULL; + _PR_THREAD_UNLOCK(thread); + + _PR_CVAR_LOCK(cvar); + _PR_THREAD_LOCK(thread); + + if (!_PR_IS_NATIVE_THREAD(thread)) { + _PR_SLEEPQ_LOCK(thread->cpu); + /* The notify and timeout can collide; in which case both may + * attempt to delete from the sleepQ; only let one do it. + */ + if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) { + _PR_DEL_SLEEPQ(thread, PR_TRUE); + } + _PR_SLEEPQ_UNLOCK(thread->cpu); + + /* Make thread runnable */ + pri = thread->priority; + thread->state = _PR_RUNNABLE; + + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + + _PR_AddThreadToRunQ(me, thread); + _PR_THREAD_UNLOCK(thread); + + _PR_MD_WAKEUP_WAITER(thread); + } else { + if (thread->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; a Resume operation + * on the thread will enable the thread to run + */ + thread->state = _PR_SUSPENDED; + } else { + thread->state = _PR_RUNNING; + } + _PR_THREAD_UNLOCK(thread); + _PR_MD_WAKEUP_WAITER(thread); + } + + _PR_CVAR_UNLOCK(cvar); + return; +} + +/* +** Make the given thread wait for the given condition variable +*/ +PRStatus _PR_WaitCondVar( + PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout) +{ + PRIntn is; + PRStatus rv = PR_SUCCESS; + + PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + +#ifdef _PR_GLOBAL_THREADS_ONLY + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + return PR_FAILURE; + } + + thread->wait.cvar = cvar; + lock->owner = NULL; + _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout); + thread->wait.cvar = NULL; + lock->owner = thread; + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + return PR_FAILURE; + } + + return PR_SUCCESS; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if ( !_PR_IS_NATIVE_THREAD(thread)) { + _PR_INTSOFF(is); + } + + _PR_CVAR_LOCK(cvar); + _PR_THREAD_LOCK(thread); + + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + _PR_CVAR_UNLOCK(cvar); + _PR_THREAD_UNLOCK(thread); + if ( !_PR_IS_NATIVE_THREAD(thread)) { + _PR_INTSON(is); + } + return PR_FAILURE; + } + + thread->state = _PR_COND_WAIT; + thread->wait.cvar = cvar; + + /* + ** Put the caller thread on the condition variable's wait Q + */ + PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ); + + /* Note- for global scope threads, we don't put them on the + * global sleepQ, so each global thread must put itself + * to sleep only for the time it wants to. + */ + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + _PR_SLEEPQ_LOCK(thread->cpu); + _PR_ADD_SLEEPQ(thread, timeout); + _PR_SLEEPQ_UNLOCK(thread->cpu); + } + _PR_CVAR_UNLOCK(cvar); + _PR_THREAD_UNLOCK(thread); + + /* + ** Release lock protecting the condition variable and thereby giving time + ** to the next thread which can potentially notify on the condition variable + */ + PR_Unlock(lock); + + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, + ("PR_Wait: cvar=%p waiting for %d", cvar, timeout)); + + rv = _PR_MD_WAIT(thread, timeout); + + _PR_CVAR_LOCK(cvar); + PR_REMOVE_LINK(&thread->waitQLinks); + _PR_CVAR_UNLOCK(cvar); + + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, + ("PR_Wait: cvar=%p done waiting", cvar)); + + if ( !_PR_IS_NATIVE_THREAD(thread)) { + _PR_INTSON(is); + } + + /* Acquire lock again that we had just relinquished */ + PR_Lock(lock); + + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + return PR_FAILURE; + } + + return rv; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me) +{ +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_NOTIFY_CV(&cvar->md, &cvar->lock->ilock); +#else /* _PR_GLOBAL_THREADS_ONLY */ + + PRCList *q; + PRIntn is; + + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + _PR_CVAR_LOCK(cvar); + q = cvar->condQ.next; + while (q != &cvar->condQ) { + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("_PR_NotifyCondVar: cvar=%p", cvar)); + if (_PR_THREAD_CONDQ_PTR(q)->wait.cvar) { + if (_PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me) == PR_TRUE) { + break; + } + } + q = q->next; + } + _PR_CVAR_UNLOCK(cvar); + + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Cndition variable debugging log info. +*/ +PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen) +{ + PRUint32 nb; + + if (cvar->lock->owner) { + nb = PR_snprintf(buf, buflen, "[%p] owner=%ld[%p]", + cvar, cvar->lock->owner->id, cvar->lock->owner); + } else { + nb = PR_snprintf(buf, buflen, "[%p]", cvar); + } + return nb; +} + +/* +** Expire condition variable waits that are ready to expire. "now" is the current +** time. +*/ +void _PR_ClockInterrupt(void) +{ + PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); + _PRCPU *cpu = me->cpu; + PRIntervalTime elapsed, now; + + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + /* Figure out how much time elapsed since the last clock tick */ + now = PR_IntervalNow(); + elapsed = now - cpu->last_clock; + cpu->last_clock = now; + + PR_LOG(_pr_clock_lm, PR_LOG_MAX, + ("ExpireWaits: elapsed=%lld usec", elapsed)); + + while(1) { + _PR_SLEEPQ_LOCK(cpu); + if (_PR_SLEEPQ(cpu).next == &_PR_SLEEPQ(cpu)) { + _PR_SLEEPQ_UNLOCK(cpu); + break; + } + + thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next); + PR_ASSERT(thread->cpu == cpu); + + if (elapsed < thread->sleep) { + thread->sleep -= elapsed; + _PR_SLEEPQMAX(thread->cpu) -= elapsed; + _PR_SLEEPQ_UNLOCK(cpu); + break; + } + _PR_SLEEPQ_UNLOCK(cpu); + + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); + + _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 + ** available for the next sleeping thread's timer. + */ + _PR_SLEEPQ_LOCK(cpu); + PR_ASSERT(!(thread->flags & _PR_ON_PAUSEQ)); + if (thread->flags & _PR_ON_SLEEPQ) { + _PR_DEL_SLEEPQ(thread, PR_FALSE); + elapsed -= thread->sleep; + _PR_SLEEPQ_UNLOCK(cpu); + } else { + /* Thread was already handled; Go get another one */ + _PR_SLEEPQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(thread); + continue; + } + + /* Notify the thread waiting on the condition variable */ + if (thread->flags & _PR_SUSPENDING) { + PR_ASSERT((thread->state == _PR_IO_WAIT) || + (thread->state == _PR_COND_WAIT)); + /* + ** Thread is suspended and its condition timeout + ** expired. Transfer thread from sleepQ to suspendQ. + */ + thread->wait.cvar = NULL; + _PR_MISCQ_LOCK(cpu); + thread->state = _PR_SUSPENDED; + _PR_ADD_SUSPENDQ(thread, cpu); + _PR_MISCQ_UNLOCK(cpu); + } else { + if (thread->wait.cvar) { + PRThreadPriority pri; + + /* Do work very similar to what _PR_NotifyThread does */ + PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) ); + + /* Make thread runnable */ + pri = thread->priority; + thread->state = _PR_RUNNABLE; + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + + PR_ASSERT(thread->cpu == cpu); + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + + if (pri > me->priority) { + _PR_SET_RESCHED_FLAG(); + } + + thread->wait.cvar = NULL; + + _PR_MD_WAKEUP_WAITER(thread); + + } else if (thread->io_pending == PR_TRUE) { + /* Need to put IO sleeper back on runq */ + int pri = thread->priority; + + thread->io_suspended = PR_TRUE; +#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); + thread->state = _PR_RUNNABLE; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + } + } + _PR_THREAD_UNLOCK(thread); + } +} + +/************************************************************************/ + +/* +** Create a new condition variable. +** "lock" is the lock to use with the condition variable. +** +** Condition variables are synchronization objects that threads can use +** to wait for some condition to occur. +** +** This may fail if memory is tight or if some operating system resource +** is low. +*/ +PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock) +{ + PRCondVar *cvar; + + cvar = PR_NEWZAP(PRCondVar); + if (cvar) { + if (_PR_InitCondVar(cvar, lock) != PR_SUCCESS) { + PR_DELETE(cvar); + return NULL; + } + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return cvar; +} + +PRStatus _PR_InitCondVar(PRCondVar *cvar, PRLock *lock) +{ + PR_ASSERT(lock != NULL); + +#ifdef _PR_GLOBAL_THREADS_ONLY + if(_PR_MD_NEW_CV(&cvar->md)) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return PR_FAILURE; + } +#endif + if (_PR_MD_NEW_LOCK(&(cvar->ilock)) != PR_SUCCESS) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return PR_FAILURE; + } + cvar->lock = lock; + PR_INIT_CLIST(&cvar->condQ); + return PR_SUCCESS; +} + +/* +** Destroy a condition variable. There must be no thread +** waiting on the condvar. The caller is responsible for guaranteeing +** that the condvar is no longer in use. +** +*/ +PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar) +{ + _PR_FreeCondVar(cvar); + PR_DELETE(cvar); +} + +void _PR_FreeCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar->condQ.next == &cvar->condQ); + +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_FREE_CV(&cvar->md); +#endif + _PR_MD_FREE_LOCK(&(cvar->ilock)); +} + +/* +** Wait for a notify on the condition variable. Sleep for "tiemout" amount +** of ticks (if "timeout" is zero then the sleep is indefinite). While +** the thread is waiting it unlocks lock. When the wait has +** finished the thread regains control of the condition variable after +** locking the associated lock. +** +** The thread waiting on the condvar will be resumed when the condvar is +** notified (assuming the thread is the next in line to receive the +** notify) or when the timeout elapses. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable or the thread has been interrupted. +*/ +extern PRThread *suspendAllThread; +PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(cvar->lock->owner == me); + PR_ASSERT(me != suspendAllThread); + if (cvar->lock->owner != me) { + return PR_FAILURE; + } + + return _PR_WaitCondVar(me, cvar, cvar->lock, timeout); +} + +/* +** Notify the highest priority thread waiting on the condition +** variable. If a thread is waiting on the condition variable (using +** PR_Wait) then it is awakened and begins waiting on the lock. +*/ +PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(cvar->lock->owner == me); + PR_ASSERT(me != suspendAllThread); + if (cvar->lock->owner != me) { + return PR_FAILURE; + } + + _PR_NotifyCondVar(cvar, me); + return PR_SUCCESS; +} + +/* +** Notify all of the threads waiting on the condition variable. All of +** threads are notified in turn. The highest priority thread will +** probably acquire the lock. +*/ +PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar) +{ + PRCList *q; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(cvar->lock->owner == me); + if (cvar->lock->owner != me) { + return PR_FAILURE; + } + +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_NOTIFYALL_CV(&cvar->md, &cvar->lock->ilock); + return PR_SUCCESS; +#else /* _PR_GLOBAL_THREADS_ONLY */ + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_CVAR_LOCK(cvar); + q = cvar->condQ.next; + while (q != &cvar->condQ) { + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar)); + _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me); + q = q->next; + } + _PR_CVAR_UNLOCK(cvar); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + + return PR_SUCCESS; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + + +/*********************************************************************/ +/*********************************************************************/ +/********************ROUTINES FOR DCE EMULATION***********************/ +/*********************************************************************/ +/*********************************************************************/ +#include "prpdce.h" + +PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void) +{ + PRCondVar *cvar = PR_NEWZAP(PRCondVar); + if (NULL != cvar) + { + if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE) + { + PR_DELETE(cvar); cvar = NULL; + } + else + { + PR_INIT_CLIST(&cvar->condQ); + cvar->lock = _PR_NAKED_CV_LOCK; + } + + } + return cvar; +} + +PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar->condQ.next == &cvar->condQ); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + + _PR_MD_FREE_LOCK(&(cvar->ilock)); + + PR_DELETE(cvar); +} + +PR_IMPLEMENT(PRStatus) PRP_NakedWait( + PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + return _PR_WaitCondVar(me, cvar, lock, timeout); +} /* PRP_NakedWait */ + +PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + + _PR_NotifyCondVar(cvar, me); + + return PR_SUCCESS; +} /* PRP_NakedNotify */ + +PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar) +{ + PRCList *q; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_MD_LOCK( &(cvar->ilock) ); + q = cvar->condQ.next; + while (q != &cvar->condQ) { + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar)); + _PR_NotifyThread(_PR_THREAD_CONDQ_PTR(q), me); + q = q->next; + } + _PR_MD_UNLOCK( &(cvar->ilock) ); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + + return PR_SUCCESS; +} /* PRP_NakedBroadcast */ + diff --git a/nsprpub/pr/src/threads/combined/prulock.c b/nsprpub/pr/src/threads/combined/prulock.c new file mode 100644 index 0000000000..69c27d149f --- /dev/null +++ b/nsprpub/pr/src/threads/combined/prulock.c @@ -0,0 +1,457 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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) +{ + _PR_MD_INIT_LOCKS(); +} + +/* +** Deal with delayed interrupts/requested reschedule during interrupt +** re-enables. +*/ +void _PR_IntsOn(_PRCPU *cpu) +{ + PRUintn missed, pri, i; + _PRInterruptTable *it; + PRThread *me; + + PR_ASSERT(cpu); /* Global threads don't have CPUs */ + PR_ASSERT(_PR_MD_GET_INTSOFF() > 0); + me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); + + /* + ** Process delayed interrupts. This logic is kinda scary because we + ** need to avoid losing an interrupt (it's ok to delay an interrupt + ** until later). + ** + ** There are two missed state words. _pr_ints.where indicates to the + ** interrupt handler which state word is currently safe for + ** modification. + ** + ** This code scans both interrupt state words, using the where flag + ** to indicate to the interrupt which state word is safe for writing. + ** If an interrupt comes in during a scan the other word will be + ** modified. This modification will be noticed during the next + ** iteration of the loop or during the next call to this routine. + */ + for (i = 0; i < 2; i++) { + cpu->where = (1 - i); + missed = cpu->u.missed[i]; + if (missed != 0) { + cpu->u.missed[i] = 0; + for (it = _pr_interruptTable; it->name; it++) { + if (missed & it->missed_bit) { + PR_LOG(_pr_sched_lm, PR_LOG_MIN, + ("IntsOn[0]: %s intr", it->name)); + (*it->handler)(); + } + } + } + } + + if (cpu->u.missed[3] != 0) { + _PRCPU *cpu; + + _PR_THREAD_LOCK(me); + me->state = _PR_RUNNABLE; + pri = me->priority; + + cpu = me->cpu; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(me, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(me); + _PR_MD_SWITCH_CONTEXT(me); + } +} + +/* +** Unblock the first runnable waiting thread. Skip over +** threads that are trying to be suspended +** Note: Caller must hold _PR_LOCK_LOCK() +*/ +void _PR_UnblockLockWaiter(PRLock *lock) +{ + PRThread *t = NULL; + PRThread *me; + PRCList *q; + + q = lock->waitQ.next; + PR_ASSERT(q != &lock->waitQ); + while (q != &lock->waitQ) { + /* Unblock first waiter */ + t = _PR_THREAD_CONDQ_PTR(q); + + /* + ** We are about to change the thread's state to runnable and for local + ** threads, we are going to assign a cpu to it. So, protect thread's + ** data structure. + */ + _PR_THREAD_LOCK(t); + + if (t->flags & _PR_SUSPENDING) { + q = q->next; + _PR_THREAD_UNLOCK(t); + continue; + } + + /* Found a runnable thread */ + PR_ASSERT(t->state == _PR_LOCK_WAIT); + PR_ASSERT(t->wait.lock == lock); + t->wait.lock = 0; + PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */ + + /* + ** If this is a native thread, nothing else to do except to wake it + ** up by calling the machine dependent wakeup routine. + ** + ** If this is a local thread, we need to assign it a cpu and + ** put the thread on that cpu's run queue. There are two cases to + ** take care of. If the currently running thread is also a local + ** thread, we just assign our own cpu to that thread and put it on + ** the cpu's run queue. If the the currently running thread is a + ** native thread, we assign the primordial cpu to it (on NT, + ** MD_WAKEUP handles the cpu assignment). + */ + + if ( !_PR_IS_NATIVE_THREAD(t) ) { + + t->state = _PR_RUNNABLE; + + me = _PR_MD_CURRENT_THREAD(); + + _PR_AddThreadToRunQ(me, t); + _PR_THREAD_UNLOCK(t); + } else { + t->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(t); + } + _PR_MD_WAKEUP_WAITER(t); + break; + } + return; +} + +/************************************************************************/ + + +PR_IMPLEMENT(PRLock*) PR_NewLock(void) +{ + PRLock *lock; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + lock = PR_NEWZAP(PRLock); + if (lock) { + if (_PR_InitLock(lock) != PR_SUCCESS) { + PR_DELETE(lock); + return NULL; + } + } + return lock; +} + +PRStatus _PR_InitLock(PRLock *lock) +{ + if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) { + return PR_FAILURE; + } + PR_INIT_CLIST(&lock->links); + PR_INIT_CLIST(&lock->waitQ); + return PR_SUCCESS; +} + +/* +** Destroy the given lock "lock". There is no point in making this race +** free because if some other thread has the pointer to this lock all +** bets are off. +*/ +PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) +{ + _PR_FreeLock(lock); + PR_DELETE(lock); +} + +void _PR_FreeLock(PRLock *lock) +{ + PR_ASSERT(lock->owner == 0); + _PR_MD_FREE_LOCK(&lock->ilock); +} + +extern PRThread *suspendAllThread; +/* +** Lock the lock. +*/ +PR_IMPLEMENT(void) PR_Lock(PRLock *lock) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntn is; + PRThread *t; + PRCList *q; + + PR_ASSERT(me != suspendAllThread); + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); + PR_ASSERT(lock != NULL); +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_LOCK(&lock->ilock); + PR_ASSERT(lock->owner == 0); + lock->owner = me; + return; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if (_native_threads_only) { + _PR_MD_LOCK(&lock->ilock); + PR_ASSERT(lock->owner == 0); + lock->owner = me; + return; + } + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + +retry: + _PR_LOCK_LOCK(lock); + if (lock->owner == 0) { + /* Just got the lock */ + lock->owner = me; + lock->priority = me->priority; + /* Add the granted lock to this owning thread's lock list */ + PR_APPEND_LINK(&lock->links, &me->lockList); + _PR_LOCK_UNLOCK(lock); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_FAST_INTSON(is); + } + return; + } + + /* If this thread already owns this lock, then it is a deadlock */ + PR_ASSERT(lock->owner != me); + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + +#if 0 + if (me->priority > lock->owner->priority) { + /* + ** Give the lock owner a priority boost until we get the + ** lock. Record the priority we boosted it to. + */ + lock->boostPriority = me->priority; + _PR_SetThreadPriority(lock->owner, me->priority); + } +#endif + + /* + Add this thread to the asked for lock's list of waiting threads. We + add this thread thread in the right priority order so when the unlock + occurs, the thread with the higher priority will get the lock. + */ + q = lock->waitQ.next; + if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority == + _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) { + /* + * If all the threads in the lock waitQ have the same priority, + * then avoid scanning the list: insert the element at the end. + */ + q = &lock->waitQ; + } else { + /* Sort thread into lock's waitQ at appropriate point */ + /* Now scan the list for where to insert this entry */ + while (q != &lock->waitQ) { + t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); + if (me->priority > t->priority) { + /* Found a lower priority thread to insert in front of */ + break; + } + q = q->next; + } + } + PR_INSERT_BEFORE(&me->waitQLinks, q); + + /* + Now grab the threadLock since we are about to change the state. We have + to do this since a PR_Suspend or PR_SetThreadPriority type call that takes + a PRThread* as an argument could be changing the state of this thread from + a thread running on a different cpu. + */ + + _PR_THREAD_LOCK(me); + me->state = _PR_LOCK_WAIT; + me->wait.lock = lock; + _PR_THREAD_UNLOCK(me); + + _PR_LOCK_UNLOCK(lock); + + _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); + goto retry; + +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Unlock the lock. +*/ +PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) +{ + PRCList *q; + PRThreadPriority pri, boost; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(lock != NULL); + PR_ASSERT(lock->owner == me); + PR_ASSERT(me != suspendAllThread); + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); + if (lock->owner != me) { + return PR_FAILURE; + } + +#ifdef _PR_GLOBAL_THREADS_ONLY + lock->owner = 0; + _PR_MD_UNLOCK(&lock->ilock); + return PR_SUCCESS; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if (_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); + + /* Remove the lock from the owning thread's lock list */ + PR_REMOVE_LINK(&lock->links); + pri = lock->priority; + boost = lock->boostPriority; + if (boost > pri) { + /* + ** We received a priority boost during the time we held the lock. + ** We need to figure out what priority to move to by scanning + ** down our list of lock's that we are still holding and using + ** the highest boosted priority found. + */ + q = me->lockList.next; + while (q != &me->lockList) { + PRLock *ll = _PR_LOCK_PTR(q); + if (ll->boostPriority > pri) { + pri = ll->boostPriority; + } + q = q->next; + } + if (pri != me->priority) { + _PR_SetThreadPriority(me, pri); + } + } + + /* Unblock the first waiting thread */ + q = lock->waitQ.next; + if (q != &lock->waitQ) { + _PR_UnblockLockWaiter(lock); + } + lock->boostPriority = PR_PRIORITY_LOW; + lock->owner = 0; + _PR_LOCK_UNLOCK(lock); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + return PR_SUCCESS; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** If the current thread owns |lock|, this assertion is guaranteed to +** succeed. Otherwise, the behavior of this function is undefined. +*/ +PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(lock->owner == me); +} + +/* +** Test and then lock the lock if it's not already locked by some other +** thread. Return PR_FALSE if some other thread owned the lock at the +** time of the call. +*/ +PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRBool rv = PR_FALSE; + PRIntn is; + +#ifdef _PR_GLOBAL_THREADS_ONLY + is = _PR_MD_TEST_AND_LOCK(&lock->ilock); + if (is == 0) { + lock->owner = me; + return PR_TRUE; + } + return PR_FALSE; +#else /* _PR_GLOBAL_THREADS_ONLY */ + +#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); + } + + _PR_LOCK_LOCK(lock); + if (lock->owner == 0) { + /* Just got the lock */ + lock->owner = me; + lock->priority = me->priority; + /* Add the granted lock to this owning thread's lock list */ + PR_APPEND_LINK(&lock->links, &me->lockList); + rv = PR_TRUE; + } + _PR_LOCK_UNLOCK(lock); + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + return rv; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/************************************************************************/ +/************************************************************************/ +/***********************ROUTINES FOR DCE EMULATION***********************/ +/************************************************************************/ +/************************************************************************/ +PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) +{ + return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; +} diff --git a/nsprpub/pr/src/threads/combined/prustack.c b/nsprpub/pr/src/threads/combined/prustack.c new file mode 100644 index 0000000000..3f5452c48c --- /dev/null +++ b/nsprpub/pr/src/threads/combined/prustack.c @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/* List of free stack virtual memory chunks */ +PRLock *_pr_stackLock; +PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks); +PRIntn _pr_numFreeStacks; +PRIntn _pr_maxFreeStacks = 4; + +#ifdef DEBUG +/* +** A variable that can be set via the debugger... +*/ +PRBool _pr_debugStacks = PR_FALSE; +#endif + +/* How much space to leave between the stacks, at each end */ +#define REDZONE (2 << _pr_pageShift) + +#define _PR_THREAD_STACK_PTR(_qp) \ + ((PRThreadStack*) ((char*) (_qp) - offsetof(PRThreadStack,links))) + +void _PR_InitStacks(void) +{ + _pr_stackLock = PR_NewLock(); +} + +void _PR_CleanupStacks(void) +{ + if (_pr_stackLock) { + PR_DestroyLock(_pr_stackLock); + _pr_stackLock = NULL; + } +} + +/* +** Allocate a stack for a thread. +*/ +PRThreadStack *_PR_NewStack(PRUint32 stackSize) +{ + PRCList *qp; + PRThreadStack *ts; + PRThread *thr; + + /* + ** Trim the list of free stacks. Trim it backwards, tossing out the + ** oldest stack found first (this way more recent stacks have a + ** chance of being present in the data cache). + */ + PR_Lock(_pr_stackLock); + qp = _pr_freeStacks.prev; + while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) { + ts = _PR_THREAD_STACK_PTR(qp); + thr = _PR_THREAD_STACK_TO_PTR(ts); + qp = qp->prev; + /* + * skip stacks which are still being used + */ + if (thr->no_sched) { + continue; + } + PR_REMOVE_LINK(&ts->links); + + /* Give platform OS to clear out the stack for debugging */ + _PR_MD_CLEAR_STACK(ts); + + _pr_numFreeStacks--; + _PR_DestroySegment(ts->seg); + PR_DELETE(ts); + } + + /* + ** Find a free thread stack. This searches the list of free'd up + ** virtually mapped thread stacks. + */ + qp = _pr_freeStacks.next; + ts = 0; + while (qp != &_pr_freeStacks) { + ts = _PR_THREAD_STACK_PTR(qp); + thr = _PR_THREAD_STACK_TO_PTR(ts); + qp = qp->next; + /* + * skip stacks which are still being used + */ + if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) { + /* + ** Found a stack that is not in use and is big enough. Change + ** stackSize to fit it. + */ + stackSize = ts->allocSize - 2*REDZONE; + PR_REMOVE_LINK(&ts->links); + _pr_numFreeStacks--; + ts->links.next = 0; + ts->links.prev = 0; + PR_Unlock(_pr_stackLock); + goto done; + } + ts = 0; + } + PR_Unlock(_pr_stackLock); + + if (!ts) { + /* Make a new thread stack object. */ + ts = PR_NEWZAP(PRThreadStack); + if (!ts) { + return NULL; + } + + /* + ** Assign some of the virtual space to the new stack object. We + ** may not get that piece of VM, but if nothing else we will + ** advance the pointer so we don't collide (unless the OS screws + ** up). + */ + ts->allocSize = stackSize + 2*REDZONE; + ts->seg = _PR_NewSegment(ts->allocSize, 0); + if (!ts->seg) { + PR_DELETE(ts); + return NULL; + } + } + +done: + ts->allocBase = (char*)ts->seg->vaddr; + ts->flags = _PR_STACK_MAPPED; + ts->stackSize = stackSize; + +#ifdef HAVE_STACK_GROWING_UP + ts->stackTop = ts->allocBase + REDZONE; + ts->stackBottom = ts->stackTop + stackSize; +#else + ts->stackBottom = ts->allocBase + REDZONE; + ts->stackTop = ts->stackBottom + stackSize; +#endif + + PR_LOG(_pr_thread_lm, PR_LOG_NOTICE, + ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n", + ts->allocBase, ts->allocBase + ts->allocSize - 1, + ts->allocBase + REDZONE, + ts->allocBase + REDZONE + stackSize - 1)); + + _PR_MD_INIT_STACK(ts,REDZONE); + + return ts; +} + +/* +** Free the stack for the current thread +*/ +void _PR_FreeStack(PRThreadStack *ts) +{ + if (!ts) { + return; + } + if (ts->flags & _PR_STACK_PRIMORDIAL) { + PR_DELETE(ts); + return; + } + + /* + ** Put the stack on the free list. This is done because we are still + ** using the stack. Next time a thread is created we will trim the + ** list down; it's safe to do it then because we will have had to + ** context switch to a live stack before another thread can be + ** created. + */ + PR_Lock(_pr_stackLock); + PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev); + _pr_numFreeStacks++; + PR_Unlock(_pr_stackLock); +} diff --git a/nsprpub/pr/src/threads/combined/pruthr.c b/nsprpub/pr/src/threads/combined/pruthr.c new file mode 100644 index 0000000000..44a0820072 --- /dev/null +++ b/nsprpub/pr/src/threads/combined/pruthr.c @@ -0,0 +1,1942 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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; +PRInt32 _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread + * waits until all other user (non-system) + * threads have terminated before it exits. + * So whenever we decrement _pr_userActive, + * it is compared with + * _pr_primordialExitCount. + * If the primordial thread is a system + * thread, then _pr_primordialExitCount + * is 0. If the primordial thread is + * itself a user thread, then + * _pr_primordialThread is 1. + */ +PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to + * _pr_primordialExitCount, this condition + * variable is notified. + */ + +PRLock *_pr_deadQLock; +PRUint32 _pr_numNativeDead; +PRUint32 _pr_numUserDead; +PRCList _pr_deadNativeQ; +PRCList _pr_deadUserQ; + +PRUint32 _pr_join_counter; + +PRUint32 _pr_local_threads; +PRUint32 _pr_global_threads; + +PRBool suspendAllOn = PR_FALSE; +PRThread *suspendAllThread = NULL; + +extern PRCList _pr_active_global_threadQ; +extern PRCList _pr_active_local_threadQ; + +static void _PR_DecrActiveThreadCount(PRThread *thread); +static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *); +static void _PR_InitializeNativeStack(PRThreadStack *ts); +static void _PR_InitializeRecycledThread(PRThread *thread); +static void _PR_UserRunThread(void); + +void _PR_InitThreads(PRThreadType type, PRThreadPriority priority, + PRUintn maxPTDs) +{ + PRThread *thread; + PRThreadStack *stack; + + PR_ASSERT(priority == PR_PRIORITY_NORMAL); + + _pr_terminationCVLock = PR_NewLock(); + _pr_activeLock = PR_NewLock(); + +#ifndef HAVE_CUSTOM_USER_THREADS + stack = PR_NEWZAP(PRThreadStack); +#ifdef HAVE_STACK_GROWING_UP + stack->stackTop = (char*) ((((PRWord)&type) >> _pr_pageShift) + << _pr_pageShift); +#else +#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS) + stack->stackTop = (char*) &thread; +#else + stack->stackTop = (char*) ((((PRWord)&type + _pr_pageSize - 1) + >> _pr_pageShift) << _pr_pageShift); +#endif +#endif +#else + /* If stack is NULL, we're using custom user threads like NT fibers. */ + stack = PR_NEWZAP(PRThreadStack); + if (stack) { + stack->stackSize = 0; + _PR_InitializeNativeStack(stack); + } +#endif /* HAVE_CUSTOM_USER_THREADS */ + + thread = _PR_AttachThread(type, priority, stack); + if (thread) { + _PR_MD_SET_CURRENT_THREAD(thread); + + if (type == PR_SYSTEM_THREAD) { + thread->flags = _PR_SYSTEM; + _pr_systemActive++; + _pr_primordialExitCount = 0; + } else { + _pr_userActive++; + _pr_primordialExitCount = 1; + } + thread->no_sched = 1; + _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock); + } + + if (!thread) { + PR_Abort(); + } +#ifdef _PR_LOCAL_THREADS_ONLY + thread->flags |= _PR_PRIMORDIAL; +#else + thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE; +#endif + + /* + * Needs _PR_PRIMORDIAL flag set before calling + * _PR_MD_INIT_THREAD() + */ + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { + /* + * XXX do what? + */ + } + + if (_PR_IS_NATIVE_THREAD(thread)) { + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); + _pr_global_threads++; + } else { + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); + _pr_local_threads++; + } + + _pr_recycleThreads = 0; + _pr_deadQLock = PR_NewLock(); + _pr_numNativeDead = 0; + _pr_numUserDead = 0; + PR_INIT_CLIST(&_pr_deadNativeQ); + PR_INIT_CLIST(&_pr_deadUserQ); +} + +void _PR_CleanupThreads(void) +{ + if (_pr_terminationCVLock) { + PR_DestroyLock(_pr_terminationCVLock); + _pr_terminationCVLock = NULL; + } + if (_pr_activeLock) { + PR_DestroyLock(_pr_activeLock); + _pr_activeLock = NULL; + } + if (_pr_primordialExitCVar) { + PR_DestroyCondVar(_pr_primordialExitCVar); + _pr_primordialExitCVar = NULL; + } + /* TODO _pr_dead{Native,User}Q need to be deleted */ + if (_pr_deadQLock) { + PR_DestroyLock(_pr_deadQLock); + _pr_deadQLock = NULL; + } +} + +/* +** Initialize a stack for a native thread +*/ +static void _PR_InitializeNativeStack(PRThreadStack *ts) +{ + if( ts && (ts->stackTop == 0) ) { + ts->allocSize = ts->stackSize; + + /* + ** Setup stackTop and stackBottom values. + */ +#ifdef HAVE_STACK_GROWING_UP + ts->allocBase = (char*) ((((PRWord)&ts) >> _pr_pageShift) + << _pr_pageShift); + ts->stackBottom = ts->allocBase + ts->stackSize; + ts->stackTop = ts->allocBase; +#else + ts->allocBase = (char*) ((((PRWord)&ts + _pr_pageSize - 1) + >> _pr_pageShift) << _pr_pageShift); + ts->stackTop = ts->allocBase; + ts->stackBottom = ts->allocBase - ts->stackSize; +#endif + } +} + +void _PR_NotifyJoinWaiters(PRThread *thread) +{ + /* + ** Handle joinable threads. Change the state to waiting for join. + ** Remove from our run Q and put it on global waiting to join Q. + ** Notify on our "termination" condition variable so that joining + ** thread will know about our termination. Switch our context and + ** come back later on to continue the cleanup. + */ + PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); + if (thread->term != NULL) { + PR_Lock(_pr_terminationCVLock); + _PR_THREAD_LOCK(thread); + thread->state = _PR_JOIN_WAIT; + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + _PR_MISCQ_LOCK(thread->cpu); + _PR_ADD_JOINQ(thread, thread->cpu); + _PR_MISCQ_UNLOCK(thread->cpu); + } + _PR_THREAD_UNLOCK(thread); + PR_NotifyCondVar(thread->term); + PR_Unlock(_pr_terminationCVLock); + _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(thread->state != _PR_JOIN_WAIT); + } + +} + +/* + * Zero some of the data members of a recycled thread. + * + * Note that we can do this either when a dead thread is added to + * the dead thread queue or when it is reused. Here, we are doing + * this lazily, when the thread is reused in _PR_CreateThread(). + */ +static void _PR_InitializeRecycledThread(PRThread *thread) +{ + /* + * Assert that the following data members are already zeroed + * by _PR_CleanupThread(). + */ +#ifdef DEBUG + if (thread->privateData) { + unsigned int i; + for (i = 0; i < thread->tpdLength; i++) { + PR_ASSERT(thread->privateData[i] == NULL); + } + } +#endif + PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0); + PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0); + PR_ASSERT(thread->errorStringLength == 0); + PR_ASSERT(thread->name == 0); + + /* Reset data members in thread structure */ + thread->errorCode = thread->osErrorCode = 0; + thread->io_pending = thread->io_suspended = PR_FALSE; + thread->environment = 0; + PR_INIT_CLIST(&thread->lockList); +} + +PRStatus _PR_RecycleThread(PRThread *thread) +{ + if ( _PR_IS_NATIVE_THREAD(thread) && + _PR_NUM_DEADNATIVE < _pr_recycleThreads) { + _PR_DEADQ_LOCK; + PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ); + _PR_INC_DEADNATIVE; + _PR_DEADQ_UNLOCK; + return (PR_SUCCESS); + } else if ( !_PR_IS_NATIVE_THREAD(thread) && + _PR_NUM_DEADUSER < _pr_recycleThreads) { + _PR_DEADQ_LOCK; + PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ); + _PR_INC_DEADUSER; + _PR_DEADQ_UNLOCK; + return (PR_SUCCESS); + } + return (PR_FAILURE); +} + +/* + * Decrement the active thread count, either _pr_systemActive or + * _pr_userActive, depending on whether the thread is a system thread + * or a user thread. If all the user threads, except possibly + * the primordial thread, have terminated, we notify the primordial + * thread of this condition. + * + * Since this function will lock _pr_activeLock, do not call this + * function while holding the _pr_activeLock lock, as this will result + * in a deadlock. + */ + +static void +_PR_DecrActiveThreadCount(PRThread *thread) +{ + PR_Lock(_pr_activeLock); + if (thread->flags & _PR_SYSTEM) { + _pr_systemActive--; + } else { + _pr_userActive--; + if (_pr_userActive == _pr_primordialExitCount) { + PR_NotifyCondVar(_pr_primordialExitCVar); + } + } + PR_Unlock(_pr_activeLock); +} + +/* +** Detach thread structure +*/ +static void +_PR_DestroyThread(PRThread *thread) +{ + _PR_MD_FREE_LOCK(&thread->threadLock); + PR_DELETE(thread); +} + +void +_PR_NativeDestroyThread(PRThread *thread) +{ + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + if (NULL != thread->privateData) { + PR_ASSERT(0 != thread->tpdLength); + PR_DELETE(thread->privateData); + thread->tpdLength = 0; + } + PR_DELETE(thread->stack); + _PR_DestroyThread(thread); +} + +void +_PR_UserDestroyThread(PRThread *thread) +{ + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + if (NULL != thread->privateData) { + PR_ASSERT(0 != thread->tpdLength); + PR_DELETE(thread->privateData); + thread->tpdLength = 0; + } + _PR_MD_FREE_LOCK(&thread->threadLock); + if (thread->threadAllocatedOnStack == 1) { + _PR_MD_CLEAN_THREAD(thread); + /* + * Because the no_sched field is set, this thread/stack will + * will not be re-used until the flag is cleared by the thread + * we will context switch to. + */ + _PR_FreeStack(thread->stack); + } else { +#ifdef WINNT + _PR_MD_CLEAN_THREAD(thread); +#else + /* + * This assertion does not apply to NT. On NT, every fiber + * has its threadAllocatedOnStack equal to 0. Elsewhere, + * only the primordial thread has its threadAllocatedOnStack + * equal to 0. + */ + PR_ASSERT(thread->flags & _PR_PRIMORDIAL); +#endif + } +} + + +/* +** Run a thread's start function. When the start function returns the +** thread is done executing and no longer needs the CPU. If there are no +** more user threads running then we can exit the program. +*/ +void _PR_NativeRunThread(void *arg) +{ + PRThread *thread = (PRThread *)arg; + + _PR_MD_SET_CURRENT_THREAD(thread); + + _PR_MD_SET_CURRENT_CPU(NULL); + + /* Set up the thread stack information */ + _PR_InitializeNativeStack(thread->stack); + + /* Set up the thread md information */ + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { + /* + * thread failed to initialize itself, possibly due to + * failure to allocate per-thread resources + */ + return; + } + + while(1) { + thread->state = _PR_RUNNING; + + /* + * Add to list of active threads + */ + PR_Lock(_pr_activeLock); + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); + _pr_global_threads++; + PR_Unlock(_pr_activeLock); + + (*thread->startFunc)(thread->arg); + + /* + * The following two assertions are meant for NT asynch io. + * + * The thread should have no asynch io in progress when it + * exits, otherwise the overlapped buffer, which is part of + * the thread structure, would become invalid. + */ + PR_ASSERT(thread->io_pending == PR_FALSE); + /* + * This assertion enforces the programming guideline that + * if an io function times out or is interrupted, the thread + * should close the fd to force the asynch io to abort + * before it exits. Right now, closing the fd is the only + * way to clear the io_suspended flag. + */ + PR_ASSERT(thread->io_suspended == PR_FALSE); + + /* + * remove thread from list of active threads + */ + PR_Lock(_pr_activeLock); + PR_REMOVE_LINK(&thread->active); + _pr_global_threads--; + PR_Unlock(_pr_activeLock); + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); + + /* All done, time to go away */ + _PR_CleanupThread(thread); + + _PR_NotifyJoinWaiters(thread); + + _PR_DecrActiveThreadCount(thread); + + thread->state = _PR_DEAD_STATE; + + if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == + PR_FAILURE)) { + /* + * thread not recycled + * platform-specific thread exit processing + * - for stuff like releasing native-thread resources, etc. + */ + _PR_MD_EXIT_THREAD(thread); + /* + * Free memory allocated for the thread + */ + _PR_NativeDestroyThread(thread); + /* + * thread gone, cannot de-reference thread now + */ + return; + } + + /* Now wait for someone to activate us again... */ + _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); + } +} + +static void _PR_UserRunThread(void) +{ + PRThread *thread = _PR_MD_CURRENT_THREAD(); + PRIntn is; + + if (_MD_LAST_THREAD()) { + _MD_LAST_THREAD()->no_sched = 0; + } + +#ifdef HAVE_CUSTOM_USER_THREADS + if (thread->stack == NULL) { + thread->stack = PR_NEWZAP(PRThreadStack); + _PR_InitializeNativeStack(thread->stack); + } +#endif /* HAVE_CUSTOM_USER_THREADS */ + + while(1) { + /* Run thread main */ + if ( !_PR_IS_NATIVE_THREAD(thread)) { + _PR_MD_SET_INTSOFF(0); + } + + /* + * Add to list of active threads + */ + if (!(thread->flags & _PR_IDLE_THREAD)) { + PR_Lock(_pr_activeLock); + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); + _pr_local_threads++; + PR_Unlock(_pr_activeLock); + } + + (*thread->startFunc)(thread->arg); + + /* + * The following two assertions are meant for NT asynch io. + * + * The thread should have no asynch io in progress when it + * exits, otherwise the overlapped buffer, which is part of + * the thread structure, would become invalid. + */ + PR_ASSERT(thread->io_pending == PR_FALSE); + /* + * This assertion enforces the programming guideline that + * if an io function times out or is interrupted, the thread + * should close the fd to force the asynch io to abort + * before it exits. Right now, closing the fd is the only + * way to clear the io_suspended flag. + */ + PR_ASSERT(thread->io_suspended == PR_FALSE); + + PR_Lock(_pr_activeLock); + /* + * remove thread from list of active threads + */ + if (!(thread->flags & _PR_IDLE_THREAD)) { + PR_REMOVE_LINK(&thread->active); + _pr_local_threads--; + } + PR_Unlock(_pr_activeLock); + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); + + /* All done, time to go away */ + _PR_CleanupThread(thread); + + _PR_INTSOFF(is); + + _PR_NotifyJoinWaiters(thread); + + _PR_DecrActiveThreadCount(thread); + + thread->state = _PR_DEAD_STATE; + + if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == + PR_FAILURE)) { + /* + ** Destroy the thread resources + */ + _PR_UserDestroyThread(thread); + } + + /* + ** Find another user thread to run. This cpu has finished the + ** previous threads main and is now ready to run another thread. + */ + { + PRInt32 is; + _PR_INTSOFF(is); + _PR_MD_SWITCH_CONTEXT(thread); + } + + /* Will land here when we get scheduled again if we are recycling... */ + } +} + +void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntn is; + + if ( _PR_IS_NATIVE_THREAD(thread) ) { + _PR_MD_SET_PRIORITY(&(thread->md), newPri); + return; + } + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_THREAD_LOCK(thread); + if (newPri != thread->priority) { + _PRCPU *cpu = thread->cpu; + + switch (thread->state) { + case _PR_RUNNING: + /* Change my priority */ + + _PR_RUNQ_LOCK(cpu); + thread->priority = newPri; + if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) { + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_SET_RESCHED_FLAG(); + } + } + _PR_RUNQ_UNLOCK(cpu); + break; + + case _PR_RUNNABLE: + + _PR_RUNQ_LOCK(cpu); + /* Move to different runQ */ + _PR_DEL_RUNQ(thread); + thread->priority = newPri; + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + _PR_ADD_RUNQ(thread, cpu, newPri); + _PR_RUNQ_UNLOCK(cpu); + + if (newPri > me->priority) { + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_SET_RESCHED_FLAG(); + } + } + + break; + + case _PR_LOCK_WAIT: + case _PR_COND_WAIT: + case _PR_IO_WAIT: + case _PR_SUSPENDED: + + thread->priority = newPri; + break; + } + } + _PR_THREAD_UNLOCK(thread); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } +} + +/* +** Suspend the named thread and copy its gc registers into regBuf +*/ +static void _PR_Suspend(PRThread *thread) +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(thread != me); + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu)); + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_THREAD_LOCK(thread); + switch (thread->state) { + case _PR_RUNNABLE: + if (!_PR_IS_NATIVE_THREAD(thread)) { + _PR_RUNQ_LOCK(thread->cpu); + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(thread->cpu); + + _PR_MISCQ_LOCK(thread->cpu); + _PR_ADD_SUSPENDQ(thread, thread->cpu); + _PR_MISCQ_UNLOCK(thread->cpu); + } else { + /* + * Only LOCAL threads are suspended by _PR_Suspend + */ + PR_ASSERT(0); + } + thread->state = _PR_SUSPENDED; + break; + + case _PR_RUNNING: + /* + * The thread being suspended should be a LOCAL thread with + * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state + */ + PR_ASSERT(0); + break; + + case _PR_LOCK_WAIT: + case _PR_IO_WAIT: + case _PR_COND_WAIT: + if (_PR_IS_NATIVE_THREAD(thread)) { + _PR_MD_SUSPEND_THREAD(thread); + } + thread->flags |= _PR_SUSPENDING; + break; + + default: + PR_Abort(); + } + _PR_THREAD_UNLOCK(thread); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } +} + +static void _PR_Resume(PRThread *thread) +{ + PRThreadPriority pri; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_THREAD_LOCK(thread); + switch (thread->state) { + case _PR_SUSPENDED: + thread->state = _PR_RUNNABLE; + thread->flags &= ~_PR_SUSPENDING; + if (!_PR_IS_NATIVE_THREAD(thread)) { + _PR_MISCQ_LOCK(thread->cpu); + _PR_DEL_SUSPENDQ(thread); + _PR_MISCQ_UNLOCK(thread->cpu); + + pri = thread->priority; + + _PR_RUNQ_LOCK(thread->cpu); + _PR_ADD_RUNQ(thread, thread->cpu, pri); + _PR_RUNQ_UNLOCK(thread->cpu); + + if (pri > _PR_MD_CURRENT_THREAD()->priority) { + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_SET_RESCHED_FLAG(); + } + } + } else { + PR_ASSERT(0); + } + break; + + case _PR_IO_WAIT: + case _PR_COND_WAIT: + thread->flags &= ~_PR_SUSPENDING; + /* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */ + break; + + case _PR_LOCK_WAIT: + { + PRLock *wLock = thread->wait.lock; + + thread->flags &= ~_PR_SUSPENDING; + + _PR_LOCK_LOCK(wLock); + if (thread->wait.lock->owner == 0) { + _PR_UnblockLockWaiter(thread->wait.lock); + } + _PR_LOCK_UNLOCK(wLock); + break; + } + case _PR_RUNNABLE: + break; + case _PR_RUNNING: + /* + * The thread being suspended should be a LOCAL thread with + * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state + */ + PR_ASSERT(0); + break; + + default: + /* + * thread should have been in one of the above-listed blocked states + * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE) + */ + PR_Abort(); + } + _PR_THREAD_UNLOCK(thread); + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + +} + +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) +static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus) +{ + PRThread *thread; + PRIntn pri; + PRUint32 r; + PRCList *qp; + PRIntn priMin, priMax; + + _PR_RUNQ_LOCK(cpu); + r = _PR_RUNQREADYMASK(cpu); + if (r==0) { + priMin = priMax = PR_PRIORITY_FIRST; + } else if (r == (1<<PR_PRIORITY_NORMAL) ) { + priMin = priMax = PR_PRIORITY_NORMAL; + } else { + priMin = PR_PRIORITY_FIRST; + priMax = PR_PRIORITY_LAST; + } + thread = NULL; + for (pri = priMax; pri >= priMin ; pri-- ) { + if (r & (1 << pri)) { + for (qp = _PR_RUNQ(cpu)[pri].next; + qp != &_PR_RUNQ(cpu)[pri]; + qp = qp->next) { + thread = _PR_THREAD_PTR(qp); + /* + * skip non-schedulable threads + */ + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + if (thread->no_sched) { + thread = NULL; + /* + * Need to wakeup cpus to avoid missing a + * runnable thread + * Waking up all CPU's need happen only once. + */ + + *wakeup_cpus = PR_TRUE; + continue; + } else if (thread->flags & _PR_BOUND_THREAD) { + /* + * Thread bound to cpu 0 + */ + + thread = NULL; + continue; + } else if (thread->io_pending == PR_TRUE) { + /* + * A thread that is blocked for I/O needs to run + * on the same cpu on which it was blocked. This is because + * the cpu's ioq is accessed without lock protection and scheduling + * the thread on a different cpu would preclude this optimization. + */ + thread = NULL; + continue; + } else { + /* Pull thread off of its run queue */ + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(cpu); + return(thread); + } + } + } + thread = NULL; + } + _PR_RUNQ_UNLOCK(cpu); + return(thread); +} +#endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */ + +/* +** Schedule this native thread by finding the highest priority nspr +** thread that is ready to run. +** +** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls +** PR_Schedule() rather than calling PR_Schedule. Otherwise if there +** is initialization required for switching from SWITCH_CONTEXT, +** it will not get done! +*/ +void _PR_Schedule(void) +{ + PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + PRIntn pri; + PRUint32 r; + PRCList *qp; + PRIntn priMin, priMax; +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) + PRBool wakeup_cpus; +#endif + + /* Interrupts must be disabled */ + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + /* Since we are rescheduling, we no longer want to */ + _PR_CLEAR_RESCHED_FLAG(); + + /* + ** Find highest priority thread to run. Bigger priority numbers are + ** higher priority threads + */ + _PR_RUNQ_LOCK(cpu); + /* + * if we are in SuspendAll mode, can schedule only the thread + * that called PR_SuspendAll + * + * The thread may be ready to run now, after completing an I/O + * operation, for example + */ + if ((thread = suspendAllThread) != 0) { + if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) { + /* Pull thread off of its run queue */ + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(cpu); + goto found_thread; + } else { + thread = NULL; + _PR_RUNQ_UNLOCK(cpu); + goto idle_thread; + } + } + r = _PR_RUNQREADYMASK(cpu); + if (r==0) { + priMin = priMax = PR_PRIORITY_FIRST; + } else if (r == (1<<PR_PRIORITY_NORMAL) ) { + priMin = priMax = PR_PRIORITY_NORMAL; + } else { + priMin = PR_PRIORITY_FIRST; + priMax = PR_PRIORITY_LAST; + } + thread = NULL; + for (pri = priMax; pri >= priMin ; pri-- ) { + if (r & (1 << pri)) { + for (qp = _PR_RUNQ(cpu)[pri].next; + qp != &_PR_RUNQ(cpu)[pri]; + qp = qp->next) { + thread = _PR_THREAD_PTR(qp); + /* + * skip non-schedulable threads + */ + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + if ((thread->no_sched) && (me != thread)) { + thread = NULL; + continue; + } else { + /* Pull thread off of its run queue */ + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(cpu); + goto found_thread; + } + } + } + thread = NULL; + } + _PR_RUNQ_UNLOCK(cpu); + +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) + + wakeup_cpus = PR_FALSE; + _PR_CPU_LIST_LOCK(); + for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { + if (cpu != _PR_CPU_PTR(qp)) { + if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus)) + != NULL) { + thread->cpu = cpu; + _PR_CPU_LIST_UNLOCK(); + if (wakeup_cpus == PR_TRUE) { + _PR_MD_WAKEUP_CPUS(); + } + goto found_thread; + } + } + } + _PR_CPU_LIST_UNLOCK(); + if (wakeup_cpus == PR_TRUE) { + _PR_MD_WAKEUP_CPUS(); + } + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +idle_thread: + /* + ** There are no threads to run. Switch to the idle thread + */ + PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing")); + thread = _PR_MD_CURRENT_CPU()->idle_thread; + +found_thread: + PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) && + (!(thread->no_sched)))); + + /* Resume the thread */ + PR_LOG(_pr_sched_lm, PR_LOG_MAX, + ("switching to %d[%p]", thread->id, thread)); + PR_ASSERT(thread->state != _PR_RUNNING); + thread->state = _PR_RUNNING; + + /* If we are on the runq, it just means that we went to sleep on some + * resource, and by the time we got here another real native thread had + * already given us the resource and put us back on the runqueue + */ + PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU()); + if (thread != me) { + _PR_MD_RESTORE_CONTEXT(thread); + } +#if 0 + /* XXXMB; with setjmp/longjmp it is impossible to land here, but + * it is not with fibers... Is this a bad thing? I believe it is + * still safe. + */ + PR_NOT_REACHED("impossible return from schedule"); +#endif +} + +/* +** Attaches a thread. +** Does not set the _PR_MD_CURRENT_THREAD. +** Does not specify the scope of the thread. +*/ +static PRThread * +_PR_AttachThread(PRThreadType type, PRThreadPriority priority, + PRThreadStack *stack) +{ + PRThread *thread; + char *mem; + + if (priority > PR_PRIORITY_LAST) { + priority = PR_PRIORITY_LAST; + } else if (priority < PR_PRIORITY_FIRST) { + priority = PR_PRIORITY_FIRST; + } + + mem = (char*) PR_CALLOC(sizeof(PRThread)); + if (mem) { + thread = (PRThread*) mem; + thread->priority = priority; + thread->stack = stack; + thread->state = _PR_RUNNING; + PR_INIT_CLIST(&thread->lockList); + if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { + PR_DELETE(thread); + return 0; + } + + return thread; + } + return 0; +} + + + +PR_IMPLEMENT(PRThread*) +_PR_NativeCreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, + PRUint32 flags) +{ + PRThread *thread; + + thread = _PR_AttachThread(type, priority, NULL); + + if (thread) { + PR_Lock(_pr_activeLock); + thread->flags = (flags | _PR_GLOBAL_SCOPE); + thread->id = ++_pr_utid; + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + PR_Unlock(_pr_activeLock); + + thread->stack = PR_NEWZAP(PRThreadStack); + if (!thread->stack) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto done; + } + thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE; + thread->stack->thr = thread; + thread->startFunc = start; + thread->arg = arg; + + /* + Set thread flags related to scope and joinable state. If joinable + thread, allocate a "termination" conidition variable. + */ + if (state == PR_JOINABLE_THREAD) { + thread->term = PR_NewCondVar(_pr_terminationCVLock); + if (thread->term == NULL) { + PR_DELETE(thread->stack); + goto done; + } + } + + thread->state = _PR_RUNNING; + if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority, + scope,state,stackSize) == PR_SUCCESS) { + return thread; + } + if (thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = NULL; + } + PR_DELETE(thread->stack); + } + +done: + if (thread) { + _PR_DecrActiveThreadCount(thread); + _PR_DestroyThread(thread); + } + return NULL; +} + +/************************************************************************/ + +PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, + PRUint32 flags) +{ + PRThread *me; + PRThread *thread = NULL; + PRThreadStack *stack; + char *top; + PRIntn is; + PRIntn native = 0; + PRIntn useRecycled = 0; + PRBool status; + + /* + First, pin down the priority. Not all compilers catch passing out of + range enum here. If we let bad values thru, priority queues won't work. + */ + if (priority > PR_PRIORITY_LAST) { + priority = PR_PRIORITY_LAST; + } else if (priority < PR_PRIORITY_FIRST) { + priority = PR_PRIORITY_FIRST; + } + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (! (flags & _PR_IDLE_THREAD)) { + me = _PR_MD_CURRENT_THREAD(); + } + +#if defined(_PR_GLOBAL_THREADS_ONLY) + /* + * can create global threads only + */ + if (scope == PR_LOCAL_THREAD) { + 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()); + + _PR_ADJUST_STACKSIZE(stackSize); + + if (native) { + /* + * clear the IDLE_THREAD flag which applies to LOCAL + * threads only + */ + flags &= ~_PR_IDLE_THREAD; + flags |= _PR_GLOBAL_SCOPE; + if (_PR_NUM_DEADNATIVE > 0) { + _PR_DEADQ_LOCK; + + if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */ + _PR_DEADQ_UNLOCK; + } else { + thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next); + PR_REMOVE_LINK(&thread->links); + _PR_DEC_DEADNATIVE; + _PR_DEADQ_UNLOCK; + + _PR_InitializeRecycledThread(thread); + thread->startFunc = start; + thread->arg = arg; + thread->flags = (flags | _PR_GLOBAL_SCOPE); + if (type == PR_SYSTEM_THREAD) + { + thread->flags |= _PR_SYSTEM; + PR_ATOMIC_INCREMENT(&_pr_systemActive); + } + else { + PR_ATOMIC_INCREMENT(&_pr_userActive); + } + + if (state == PR_JOINABLE_THREAD) { + if (!thread->term) { + thread->term = PR_NewCondVar(_pr_terminationCVLock); + } + } + else { + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + } + + thread->priority = priority; + _PR_MD_SET_PRIORITY(&(thread->md), priority); + /* XXX what about stackSize? */ + thread->state = _PR_RUNNING; + _PR_MD_WAKEUP_WAITER(thread); + return thread; + } + } + thread = _PR_NativeCreateThread(type, start, arg, priority, + scope, state, stackSize, flags); + } else { + if (_PR_NUM_DEADUSER > 0) { + _PR_DEADQ_LOCK; + + if (_PR_NUM_DEADUSER == 0) { /* thread safe check */ + _PR_DEADQ_UNLOCK; + } else { + PRCList *ptr; + + /* Go down list checking for a recycled thread with a + * large enough stack. XXXMB - this has a bad degenerate case. + */ + ptr = _PR_DEADUSERQ.next; + while( ptr != &_PR_DEADUSERQ ) { + thread = _PR_THREAD_PTR(ptr); + if ((thread->stack->stackSize >= stackSize) && + (!thread->no_sched)) { + PR_REMOVE_LINK(&thread->links); + _PR_DEC_DEADUSER; + break; + } else { + ptr = ptr->next; + thread = NULL; + } + } + + _PR_DEADQ_UNLOCK; + + if (thread) { + _PR_InitializeRecycledThread(thread); + thread->startFunc = start; + thread->arg = arg; + thread->priority = priority; + if (state == PR_JOINABLE_THREAD) { + if (!thread->term) { + thread->term = PR_NewCondVar(_pr_terminationCVLock); + } + } else { + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + } + useRecycled++; + } + } + } + if (thread == NULL) { +#ifndef HAVE_CUSTOM_USER_THREADS + stack = _PR_NewStack(stackSize); + if (!stack) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + /* Allocate thread object and per-thread data off the top of the stack*/ + top = stack->stackTop; +#ifdef HAVE_STACK_GROWING_UP + thread = (PRThread*) top; + top = top + sizeof(PRThread); + /* + * Make stack 64-byte aligned + */ + if ((PRUptrdiff)top & 0x3f) { + top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f); + } +#else + top = top - sizeof(PRThread); + thread = (PRThread*) top; + /* + * Make stack 64-byte aligned + */ + if ((PRUptrdiff)top & 0x3f) { + top = (char*)((PRUptrdiff)top & ~0x3f); + } +#endif + stack->thr = thread; + memset(thread, 0, sizeof(PRThread)); + thread->threadAllocatedOnStack = 1; +#else + thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg); + if (!thread) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + thread->threadAllocatedOnStack = 0; + stack = NULL; + top = NULL; +#endif + + /* Initialize thread */ + thread->tpdLength = 0; + thread->privateData = NULL; + thread->stack = stack; + thread->priority = priority; + thread->startFunc = start; + thread->arg = arg; + PR_INIT_CLIST(&thread->lockList); + + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { + if (thread->threadAllocatedOnStack == 1) { + _PR_FreeStack(thread->stack); + } + else { + PR_DELETE(thread); + } + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return NULL; + } + + if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { + if (thread->threadAllocatedOnStack == 1) { + _PR_FreeStack(thread->stack); + } + else { + PR_DELETE(thread->privateData); + PR_DELETE(thread); + } + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return NULL; + } + + _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status); + + if (status == PR_FALSE) { + _PR_MD_FREE_LOCK(&thread->threadLock); + if (thread->threadAllocatedOnStack == 1) { + _PR_FreeStack(thread->stack); + } + else { + PR_DELETE(thread->privateData); + PR_DELETE(thread); + } + return NULL; + } + + /* + Set thread flags related to scope and joinable state. If joinable + thread, allocate a "termination" condition variable. + */ + if (state == PR_JOINABLE_THREAD) { + thread->term = PR_NewCondVar(_pr_terminationCVLock); + if (thread->term == NULL) { + _PR_MD_FREE_LOCK(&thread->threadLock); + if (thread->threadAllocatedOnStack == 1) { + _PR_FreeStack(thread->stack); + } + else { + PR_DELETE(thread->privateData); + PR_DELETE(thread); + } + return NULL; + } + } + + } + + /* Update thread type counter */ + PR_Lock(_pr_activeLock); + thread->flags = flags; + thread->id = ++_pr_utid; + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + + /* Make thread runnable */ + thread->state = _PR_RUNNABLE; + /* + * Add to list of active threads + */ + PR_Unlock(_pr_activeLock); + + if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) ) { + thread->cpu = _PR_GetPrimordialCPU(); + } + else { + thread->cpu = _PR_MD_CURRENT_CPU(); + } + + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); + + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + _PR_RUNQ_LOCK(thread->cpu); + _PR_ADD_RUNQ(thread, thread->cpu, priority); + _PR_RUNQ_UNLOCK(thread->cpu); + } + + if (thread->flags & _PR_IDLE_THREAD) { + /* + ** If the creating thread is a kernel thread, we need to + ** awaken the user thread idle thread somehow; potentially + ** it could be sleeping in its idle loop, and we need to poke + ** it. To do so, wake the idle thread... + */ + _PR_MD_WAKEUP_WAITER(NULL); + } else if (_PR_IS_NATIVE_THREAD(me)) { + _PR_MD_WAKEUP_WAITER(thread); + } + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) ) { + _PR_INTSON(is); + } + } + + return thread; +} + +PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + return _PR_CreateThread(type, start, arg, priority, scope, state, + stackSize, 0); +} + +/* +** Associate a thread object with an existing native thread. +** "type" is the type of thread object to attach +** "priority" is the priority to assign to the thread +** "stack" defines the shape of the threads stack +** +** This can return NULL if some kind of error occurs, or if memory is +** tight. +** +** This call is not normally needed unless you create your own native +** thread. PR_Init does this automatically for the primordial thread. +*/ +PRThread* _PRI_AttachThread(PRThreadType type, + PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags) +{ + PRThread *thread; + + if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) { + return thread; + } + _PR_MD_SET_CURRENT_THREAD(NULL); + + /* Clear out any state if this thread was attached before */ + _PR_MD_SET_CURRENT_CPU(NULL); + + thread = _PR_AttachThread(type, priority, stack); + if (thread) { + PRIntn is; + + _PR_MD_SET_CURRENT_THREAD(thread); + + thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED; + + if (!stack) { + thread->stack = PR_NEWZAP(PRThreadStack); + if (!thread->stack) { + _PR_DestroyThread(thread); + return NULL; + } + thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE; + } + PR_INIT_CLIST(&thread->links); + + if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) { + PR_DELETE(thread->stack); + _PR_DestroyThread(thread); + return NULL; + } + + _PR_MD_SET_CURRENT_CPU(NULL); + + if (_PR_MD_CURRENT_CPU()) { + _PR_INTSOFF(is); + PR_Lock(_pr_activeLock); + } + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + if (_PR_MD_CURRENT_CPU()) { + PR_Unlock(_pr_activeLock); + _PR_INTSON(is); + } + } + return thread; +} + +PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type, + PRThreadPriority priority, PRThreadStack *stack) +{ + return PR_GetCurrentThread(); +} + +PR_IMPLEMENT(void) PR_DetachThread(void) +{ + /* + * On Solaris, and Windows, foreign threads are detached when + * they terminate. + */ +#if !defined(WIN32) \ + && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)) + PRThread *me; + if (_pr_initialized) { + me = _PR_MD_GET_ATTACHED_THREAD(); + if ((me != NULL) && (me->flags & _PR_ATTACHED)) { + _PRI_DetachThread(); + } + } +#endif +} + +void _PRI_DetachThread(void) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me->flags & _PR_PRIMORDIAL) { + /* + * ignore, if primordial thread + */ + return; + } + PR_ASSERT(me->flags & _PR_ATTACHED); + PR_ASSERT(_PR_IS_NATIVE_THREAD(me)); + _PR_CleanupThread(me); + PR_DELETE(me->privateData); + + _PR_DecrActiveThreadCount(me); + + _PR_MD_CLEAN_THREAD(me); + _PR_MD_SET_CURRENT_THREAD(NULL); + if (!me->threadAllocatedOnStack) { + PR_DELETE(me->stack); + } + _PR_MD_FREE_LOCK(&me->threadLock); + PR_DELETE(me); +} + +/* +** Wait for thread termination: +** "thread" is the target thread +** +** This can return PR_FAILURE if no joinable thread could be found +** corresponding to the specified target thread. +** +** The calling thread is suspended until the target thread completes. +** Several threads cannot wait for the same thread to complete; one thread +** will complete successfully and others will terminate with an error PR_FAILURE. +** The calling thread will not be blocked if the target thread has already +** terminated. +*/ +PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread) +{ + PRIntn is; + PRCondVar *term; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + term = thread->term; + /* can't join a non-joinable thread */ + if (term == NULL) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + goto ErrorExit; + } + + /* multiple threads can't wait on the same joinable thread */ + if (term->condQ.next != &term->condQ) { + goto ErrorExit; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + + /* wait for the target thread's termination cv invariant */ + PR_Lock (_pr_terminationCVLock); + while (thread->state != _PR_JOIN_WAIT) { + (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT); + } + (void) PR_Unlock (_pr_terminationCVLock); + + /* + Remove target thread from global waiting to join Q; make it runnable + again and put it back on its run Q. When it gets scheduled later in + _PR_RunThread code, it will clean up its stack. + */ + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + thread->state = _PR_RUNNABLE; + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + _PR_THREAD_LOCK(thread); + + _PR_MISCQ_LOCK(thread->cpu); + _PR_DEL_JOINQ(thread); + _PR_MISCQ_UNLOCK(thread->cpu); + + _PR_AddThreadToRunQ(me, thread); + _PR_THREAD_UNLOCK(thread); + } + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + + _PR_MD_WAKEUP_WAITER(thread); + + return PR_SUCCESS; + +ErrorExit: + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + return PR_FAILURE; +} + +PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread, + PRThreadPriority newPri) +{ + + /* + First, pin down the priority. Not all compilers catch passing out of + range enum here. If we let bad values thru, priority queues won't work. + */ + if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) { + newPri = PR_PRIORITY_LAST; + } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) { + newPri = PR_PRIORITY_FIRST; + } + + if ( _PR_IS_NATIVE_THREAD(thread) ) { + thread->priority = newPri; + _PR_MD_SET_PRIORITY(&(thread->md), newPri); + } else { + _PR_SetThreadPriority(thread, newPri); + } +} + +PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name) +{ + PRThread *thread; + size_t nameLen; + + if (!name) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + thread = PR_GetCurrentThread(); + if (!thread) { + return PR_FAILURE; + } + + PR_Free(thread->name); + nameLen = strlen(name); + thread->name = (char *)PR_Malloc(nameLen + 1); + if (!thread->name) { + return PR_FAILURE; + } + memcpy(thread->name, name, nameLen + 1); + _PR_MD_SET_CURRENT_THREAD_NAME(thread->name); + return PR_SUCCESS; +} + +PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread) +{ + if (!thread) { + return NULL; + } + return thread->name; +} + + +/* +** This routine prevents all other threads from running. This call is needed by +** the garbage collector. +*/ +PR_IMPLEMENT(void) PR_SuspendAll(void) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRCList *qp; + + /* + * Stop all user and native threads which are marked GC able. + */ + PR_Lock(_pr_activeLock); + suspendAllOn = PR_TRUE; + suspendAllThread = _PR_MD_CURRENT_THREAD(); + _PR_MD_BEGIN_SUSPEND_ALL(); + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) { + _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); + PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING); + } + } + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) + /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */ + { + _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); + } + } + _PR_MD_END_SUSPEND_ALL(); +} + +/* +** This routine unblocks all other threads that were suspended from running by +** PR_SuspendAll(). This call is needed by the garbage collector. +*/ +PR_IMPLEMENT(void) PR_ResumeAll(void) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRCList *qp; + + /* + * Resume all user and native threads which are marked GC able. + */ + _PR_MD_BEGIN_RESUME_ALL(); + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) { + _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp)); + } + } + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) { + _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); + } + } + _PR_MD_END_RESUME_ALL(); + suspendAllThread = NULL; + suspendAllOn = PR_FALSE; + PR_Unlock(_pr_activeLock); +} + +PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) +{ + PRCList *qp, *qp_next; + PRIntn i = 0; + PRStatus rv = PR_SUCCESS; + PRThread* t; + + /* + ** Currently Enumerate threads happen only with suspension and + ** pr_activeLock held + */ + PR_ASSERT(suspendAllOn); + + /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking + * qp->next after applying the function "func". In particular, "func" + * might remove the thread from the queue and put it into another one in + * which case qp->next no longer points to the next entry in the original + * queue. + * + * To get around this problem, we save qp->next in qp_next before applying + * "func" and use that saved value as the next value after applying "func". + */ + + /* + * Traverse the list of local and global threads + */ + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next) + { + qp_next = qp->next; + t = _PR_ACTIVE_THREAD_PTR(qp); + if (_PR_IS_GCABLE_THREAD(t)) + { + rv = (*func)(t, i, arg); + if (rv != PR_SUCCESS) { + return rv; + } + i++; + } + } + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next) + { + qp_next = qp->next; + t = _PR_ACTIVE_THREAD_PTR(qp); + if (_PR_IS_GCABLE_THREAD(t)) + { + rv = (*func)(t, i, arg); + if (rv != PR_SUCCESS) { + return rv; + } + i++; + } + } + return rv; +} + +/* FUNCTION: _PR_AddSleepQ +** DESCRIPTION: +** Adds a thread to the sleep/pauseQ. +** RESTRICTIONS: +** Caller must have the RUNQ lock. +** Caller must be a user level thread +*/ +PR_IMPLEMENT(void) +_PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout) +{ + _PRCPU *cpu = thread->cpu; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + /* append the thread to the global pause Q */ + PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu)); + thread->flags |= _PR_ON_PAUSEQ; + } else { + PRIntervalTime sleep; + PRCList *q; + PRThread *t; + + /* sort onto global sleepQ */ + sleep = timeout; + + /* Check if we are longest timeout */ + if (timeout >= _PR_SLEEPQMAX(cpu)) { + PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu)); + thread->sleep = timeout - _PR_SLEEPQMAX(cpu); + _PR_SLEEPQMAX(cpu) = timeout; + } else { + /* Sort thread into global sleepQ at appropriate point */ + q = _PR_SLEEPQ(cpu).next; + + /* Now scan the list for where to insert this entry */ + while (q != &_PR_SLEEPQ(cpu)) { + t = _PR_THREAD_PTR(q); + if (sleep < t->sleep) { + /* Found sleeper to insert in front of */ + break; + } + sleep -= t->sleep; + q = q->next; + } + thread->sleep = sleep; + PR_INSERT_BEFORE(&thread->links, q); + + /* + ** Subtract our sleep time from the sleeper that follows us (there + ** must be one) so that they remain relative to us. + */ + PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu)); + + t = _PR_THREAD_PTR(thread->links.next); + PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread); + t->sleep -= sleep; + } + + thread->flags |= _PR_ON_SLEEPQ; + } +} + +/* FUNCTION: _PR_DelSleepQ +** DESCRIPTION: +** Removes a thread from the sleep/pauseQ. +** INPUTS: +** If propogate_time is true, then the thread following the deleted +** thread will be get the time from the deleted thread. This is used +** when deleting a sleeper that has not timed out. +** RESTRICTIONS: +** Caller must have the RUNQ lock. +** Caller must be a user level thread +*/ +PR_IMPLEMENT(void) +_PR_DelSleepQ(PRThread *thread, PRBool propogate_time) +{ + _PRCPU *cpu = thread->cpu; + + /* Remove from pauseQ/sleepQ */ + if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { + if (thread->flags & _PR_ON_SLEEPQ) { + PRCList *q = thread->links.next; + if (q != &_PR_SLEEPQ(cpu)) { + if (propogate_time == PR_TRUE) { + PRThread *after = _PR_THREAD_PTR(q); + after->sleep += thread->sleep; + } else { + _PR_SLEEPQMAX(cpu) -= thread->sleep; + } + } else { + /* Check if prev is the beggining of the list; if so, + * we are the only element on the list. + */ + if (thread->links.prev != &_PR_SLEEPQ(cpu)) { + _PR_SLEEPQMAX(cpu) -= thread->sleep; + } + else { + _PR_SLEEPQMAX(cpu) = 0; + } + } + thread->flags &= ~_PR_ON_SLEEPQ; + } else { + thread->flags &= ~_PR_ON_PAUSEQ; + } + PR_REMOVE_LINK(&thread->links); + } else { + PR_ASSERT(0); + } +} + +void +_PR_AddThreadToRunQ( + PRThread *me, /* the current thread */ + PRThread *thread) /* the local thread to be added to a run queue */ +{ + PRThreadPriority pri = thread->priority; + _PRCPU *cpu = thread->cpu; + + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); + +#if defined(WINNT) + /* + * On NT, we can only reliably know that the current CPU + * is not idle. We add the awakened thread to the run + * queue of its CPU if its CPU is the current CPU. + * For any other CPU, we don't really know whether it + * is busy or idle. So in all other cases, we just + * "post" the awakened thread to the IO completion port + * for the next idle CPU to execute (this is done in + * _PR_MD_WAKEUP_WAITER). + * Threads with a suspended I/O operation remain bound to + * the same cpu until I/O is cancelled + * + * NOTE: the boolean expression below must be the exact + * opposite of the corresponding boolean expression in + * _PR_MD_WAKEUP_WAITER. + */ + if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) || + (thread->md.thr_bound_cpu)) { + PR_ASSERT(!thread->md.thr_bound_cpu || + (thread->md.thr_bound_cpu == cpu)); + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + } +#else + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) { + if (pri > me->priority) { + _PR_SET_RESCHED_FLAG(); + } + } +#endif +} diff --git a/nsprpub/pr/src/threads/prcmon.c b/nsprpub/pr/src/threads/prcmon.c new file mode 100644 index 0000000000..c1d354ced5 --- /dev/null +++ b/nsprpub/pr/src/threads/prcmon.c @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <stdlib.h> +#include <stddef.h> + +/* Lock used to lock the monitor cache */ +#ifdef _PR_NO_PREEMPT +#define _PR_NEW_LOCK_MCACHE() +#define _PR_DESTROY_LOCK_MCACHE() +#define _PR_LOCK_MCACHE() +#define _PR_UNLOCK_MCACHE() +#else +#ifdef _PR_LOCAL_THREADS_ONLY +#define _PR_NEW_LOCK_MCACHE() +#define _PR_DESTROY_LOCK_MCACHE() +#define _PR_LOCK_MCACHE() { PRIntn _is; _PR_INTSOFF(_is) +#define _PR_UNLOCK_MCACHE() _PR_INTSON(_is); } +#else +PRLock *_pr_mcacheLock; +#define _PR_NEW_LOCK_MCACHE() (_pr_mcacheLock = PR_NewLock()) +#define _PR_DESTROY_LOCK_MCACHE() \ + PR_BEGIN_MACRO \ + if (_pr_mcacheLock) { \ + PR_DestroyLock(_pr_mcacheLock); \ + _pr_mcacheLock = NULL; \ + } \ + PR_END_MACRO +#define _PR_LOCK_MCACHE() PR_Lock(_pr_mcacheLock) +#define _PR_UNLOCK_MCACHE() PR_Unlock(_pr_mcacheLock) +#endif +#endif + +/************************************************************************/ + +typedef struct MonitorCacheEntryStr MonitorCacheEntry; + +struct MonitorCacheEntryStr { + MonitorCacheEntry* next; + void* address; + PRMonitor* mon; + long cacheEntryCount; +}; + +/* +** An array of MonitorCacheEntry's, plus a pointer to link these +** arrays together. +*/ + +typedef struct MonitorCacheEntryBlockStr MonitorCacheEntryBlock; + +struct MonitorCacheEntryBlockStr { + MonitorCacheEntryBlock* next; + MonitorCacheEntry entries[1]; +}; + +static PRUint32 hash_mask; +static PRUintn num_hash_buckets; +static PRUintn num_hash_buckets_log2; +static MonitorCacheEntry **hash_buckets; +static MonitorCacheEntry *free_entries; +static PRUintn num_free_entries; +static PRBool expanding; +static MonitorCacheEntryBlock *mcache_blocks; + +static void (*OnMonitorRecycle)(void *address); + +#define HASH(address) \ + ((PRUint32) ( ((PRUptrdiff)(address) >> 2) ^ \ + ((PRUptrdiff)(address) >> 10) ) \ + & hash_mask) + +/* +** Expand the monitor cache. This grows the hash buckets and allocates a +** new chunk of cache entries and throws them on the free list. We keep +** as many hash buckets as there are entries. +** +** Because we call malloc and malloc may need the monitor cache, we must +** ensure that there are several free monitor cache entries available for +** malloc to get. FREE_THRESHOLD is used to prevent monitor cache +** starvation during monitor cache expansion. +*/ + +#define FREE_THRESHOLD 5 + +static PRStatus ExpandMonitorCache(PRUintn new_size_log2) +{ + MonitorCacheEntry **old_hash_buckets, *p; + PRUintn i, entries, old_num_hash_buckets, added; + MonitorCacheEntry **new_hash_buckets; + MonitorCacheEntryBlock *new_block; + + entries = 1L << new_size_log2; + + /* + ** Expand the monitor-cache-entry free list + */ + new_block = (MonitorCacheEntryBlock*) + PR_CALLOC(sizeof(MonitorCacheEntryBlock) + + (entries - 1) * sizeof(MonitorCacheEntry)); + if (NULL == new_block) { + return PR_FAILURE; + } + + /* + ** Allocate system monitors for the new monitor cache entries. If we + ** run out of system monitors, break out of the loop. + */ + for (i = 0, p = new_block->entries; i < entries; i++, p++) { + p->mon = PR_NewMonitor(); + if (!p->mon) { + break; + } + } + added = i; + if (added != entries) { + MonitorCacheEntryBlock *realloc_block; + + if (added == 0) { + /* Totally out of system monitors. Lossage abounds */ + PR_DELETE(new_block); + return PR_FAILURE; + } + + /* + ** We were able to allocate some of the system monitors. Use + ** realloc to shrink down the new_block memory. If that fails, + ** carry on with the too-large new_block. + */ + realloc_block = (MonitorCacheEntryBlock*) + PR_REALLOC(new_block, sizeof(MonitorCacheEntryBlock) + + (added - 1) * sizeof(MonitorCacheEntry)); + if (realloc_block) { + new_block = realloc_block; + } + } + + /* + ** Now that we have allocated all of the system monitors, build up + ** the new free list. We can just update the free_list because we own + ** the mcache-lock and we aren't calling anyone who might want to use + ** it. + */ + for (i = 0, p = new_block->entries; i < added - 1; i++, p++) { + p->next = p + 1; + } + p->next = free_entries; + free_entries = new_block->entries; + num_free_entries += added; + new_block->next = mcache_blocks; + mcache_blocks = new_block; + + /* Try to expand the hash table */ + new_hash_buckets = (MonitorCacheEntry**) + PR_CALLOC(entries * sizeof(MonitorCacheEntry*)); + if (NULL == new_hash_buckets) { + /* + ** Partial lossage. In this situation we don't get any more hash + ** buckets, which just means that the table lookups will take + ** longer. This is bad, but not fatal + */ + PR_LOG(_pr_cmon_lm, PR_LOG_WARNING, + ("unable to grow monitor cache hash buckets")); + return PR_SUCCESS; + } + + /* + ** Compute new hash mask value. This value is used to mask an address + ** until it's bits are in the right spot for indexing into the hash + ** table. + */ + hash_mask = entries - 1; + + /* + ** Expand the hash table. We have to rehash everything in the old + ** table into the new table. + */ + old_hash_buckets = hash_buckets; + old_num_hash_buckets = num_hash_buckets; + for (i = 0; i < old_num_hash_buckets; i++) { + p = old_hash_buckets[i]; + while (p) { + MonitorCacheEntry *next = p->next; + + /* Hash based on new table size, and then put p in the new table */ + PRUintn hash = HASH(p->address); + p->next = new_hash_buckets[hash]; + new_hash_buckets[hash] = p; + + p = next; + } + } + + /* + ** Switch over to new hash table and THEN call free of the old + ** table. Since free might re-enter _pr_mcache_lock, things would + ** break terribly if it used the old hash table. + */ + hash_buckets = new_hash_buckets; + num_hash_buckets = entries; + num_hash_buckets_log2 = new_size_log2; + PR_DELETE(old_hash_buckets); + + PR_LOG(_pr_cmon_lm, PR_LOG_NOTICE, + ("expanded monitor cache to %d (buckets %d)", + num_free_entries, entries)); + + return PR_SUCCESS; +} /* ExpandMonitorCache */ + +/* +** Lookup a monitor cache entry by address. Return a pointer to the +** pointer to the monitor cache entry on success, null on failure. +*/ +static MonitorCacheEntry **LookupMonitorCacheEntry(void *address) +{ + PRUintn hash; + MonitorCacheEntry **pp, *p; + + hash = HASH(address); + pp = hash_buckets + hash; + while ((p = *pp) != 0) { + if (p->address == address) { + if (p->cacheEntryCount > 0) { + return pp; + } + return NULL; + } + pp = &p->next; + } + return NULL; +} + +/* +** Try to create a new cached monitor. If it's already in the cache, +** great - return it. Otherwise get a new free cache entry and set it +** up. If the cache free space is getting low, expand the cache. +*/ +static PRMonitor *CreateMonitor(void *address) +{ + PRUintn hash; + MonitorCacheEntry **pp, *p; + + hash = HASH(address); + pp = hash_buckets + hash; + while ((p = *pp) != 0) { + if (p->address == address) { + goto gotit; + } + + pp = &p->next; + } + + /* Expand the monitor cache if we have run out of free slots in the table */ + if (num_free_entries < FREE_THRESHOLD) { + /* Expand monitor cache */ + + /* + ** This function is called with the lock held. So what's the 'expanding' + ** boolean all about? Seems a bit redundant. + */ + if (!expanding) { + PRStatus rv; + + expanding = PR_TRUE; + rv = ExpandMonitorCache(num_hash_buckets_log2 + 1); + expanding = PR_FALSE; + if (PR_FAILURE == rv) { + return NULL; + } + + /* redo the hash because it'll be different now */ + hash = HASH(address); + } else { + /* + ** We are in process of expanding and we need a cache + ** monitor. Make sure we have enough! + */ + PR_ASSERT(num_free_entries > 0); + } + } + + /* Make a new monitor */ + p = free_entries; + free_entries = p->next; + num_free_entries--; + if (OnMonitorRecycle && p->address) { + OnMonitorRecycle(p->address); + } + p->address = address; + p->next = hash_buckets[hash]; + hash_buckets[hash] = p; + PR_ASSERT(p->cacheEntryCount == 0); + +gotit: + p->cacheEntryCount++; + return p->mon; +} + +/* +** Initialize the monitor cache +*/ +void _PR_InitCMon(void) +{ + _PR_NEW_LOCK_MCACHE(); + ExpandMonitorCache(3); +} + +/* +** Destroy the monitor cache +*/ +void _PR_CleanupCMon(void) +{ + _PR_DESTROY_LOCK_MCACHE(); + + while (free_entries) { + PR_DestroyMonitor(free_entries->mon); + free_entries = free_entries->next; + } + num_free_entries = 0; + + while (mcache_blocks) { + MonitorCacheEntryBlock *block; + + block = mcache_blocks; + mcache_blocks = block->next; + PR_DELETE(block); + } + + PR_DELETE(hash_buckets); + hash_mask = 0; + num_hash_buckets = 0; + num_hash_buckets_log2 = 0; + + expanding = PR_FALSE; + OnMonitorRecycle = NULL; +} + +/* +** Create monitor for address. Don't enter the monitor while we have the +** mcache locked because we might block! +*/ +PR_IMPLEMENT(PRMonitor*) PR_CEnterMonitor(void *address) +{ + PRMonitor *mon; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + _PR_LOCK_MCACHE(); + mon = CreateMonitor(address); + _PR_UNLOCK_MCACHE(); + + if (!mon) { + return NULL; + } + + PR_EnterMonitor(mon); + return mon; +} + +PR_IMPLEMENT(PRStatus) PR_CExitMonitor(void *address) +{ + MonitorCacheEntry **pp, *p; + PRStatus status = PR_SUCCESS; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + if (pp != NULL) { + p = *pp; + if (--p->cacheEntryCount == 0) { + /* + ** Nobody is using the system monitor. Put it on the cached free + ** list. We are safe from somebody trying to use it because we + ** have the mcache locked. + */ + p->address = 0; /* defensive move */ + *pp = p->next; /* unlink from hash_buckets */ + p->next = free_entries; /* link into free list */ + free_entries = p; + num_free_entries++; /* count it as free */ + } + status = PR_ExitMonitor(p->mon); + } else { + status = PR_FAILURE; + } + _PR_UNLOCK_MCACHE(); + + return status; +} + +PR_IMPLEMENT(PRStatus) PR_CWait(void *address, PRIntervalTime ticks) +{ + MonitorCacheEntry **pp; + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + mon = pp ? ((*pp)->mon) : NULL; + _PR_UNLOCK_MCACHE(); + + if (mon == NULL) { + return PR_FAILURE; + } + return PR_Wait(mon, ticks); +} + +PR_IMPLEMENT(PRStatus) PR_CNotify(void *address) +{ + MonitorCacheEntry **pp; + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + mon = pp ? ((*pp)->mon) : NULL; + _PR_UNLOCK_MCACHE(); + + if (mon == NULL) { + return PR_FAILURE; + } + return PR_Notify(mon); +} + +PR_IMPLEMENT(PRStatus) PR_CNotifyAll(void *address) +{ + MonitorCacheEntry **pp; + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + mon = pp ? ((*pp)->mon) : NULL; + _PR_UNLOCK_MCACHE(); + + if (mon == NULL) { + return PR_FAILURE; + } + return PR_NotifyAll(mon); +} + +PR_IMPLEMENT(void) +PR_CSetOnMonitorRecycle(void (*callback)(void *address)) +{ + OnMonitorRecycle = callback; +} diff --git a/nsprpub/pr/src/threads/prcthr.c b/nsprpub/pr/src/threads/prcthr.c new file mode 100644 index 0000000000..e7044ed531 --- /dev/null +++ b/nsprpub/pr/src/threads/prcthr.c @@ -0,0 +1,428 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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. +** +** +** Clean up a thread object, releasing all of the attached data. Do not +** free the object itself (it may not have been malloc'd) +*/ +void _PR_CleanupThread(PRThread *thread) +{ + /* Free up per-thread-data */ + _PR_DestroyThreadPrivate(thread); + + /* Free any thread dump procs */ + if (thread->dumpArg) { + PR_DELETE(thread->dumpArg); + } + thread->dump = 0; + + PR_DELETE(thread->name); + PR_DELETE(thread->errorString); + thread->errorStringSize = 0; + thread->errorStringLength = 0; + thread->environment = NULL; +} + +PR_IMPLEMENT(PRStatus) PR_Yield() +{ + static PRBool warning = PR_TRUE; + if (warning) warning = _PR_Obsolete( + "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); + return (PR_Sleep(PR_INTERVAL_NO_WAIT)); +} + +/* +** Make the current thread sleep until "timeout" ticks amount of time +** has expired. If "timeout" is PR_INTERVAL_NO_WAIT then the call is +** equivalent to a yield. Waiting for an infinite amount of time is +** allowed in the expectation that another thread will interrupt(). +** +** A single lock is used for all threads calling sleep. Each caller +** does get its own condition variable since each is expected to have +** a unique 'timeout'. +*/ +PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime timeout) +{ + PRStatus rv = PR_SUCCESS; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (PR_INTERVAL_NO_WAIT == timeout) + { + /* + ** This is a simple yield, nothing more, nothing less. + */ + PRIntn is; + PRThread *me = PR_GetCurrentThread(); + PRUintn pri = me->priority; + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + + if ( _PR_IS_NATIVE_THREAD(me) ) { + _PR_MD_YIELD(); + } + else + { + _PR_INTSOFF(is); + _PR_RUNQ_LOCK(cpu); + if (_PR_RUNQREADYMASK(cpu) >> pri) { + me->cpu = cpu; + me->state = _PR_RUNNABLE; + _PR_ADD_RUNQ(me, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: yielding")); + _PR_MD_SWITCH_CONTEXT(me); + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: done")); + + _PR_FAST_INTSON(is); + } + else + { + _PR_RUNQ_UNLOCK(cpu); + _PR_INTSON(is); + } + } + } + else + { + /* + ** This is waiting for some finite period of time. + ** A thread in this state is interruptible (PR_Interrupt()), + ** but the lock and cvar used are local to the implementation + ** and not visible to the caller, therefore not notifiable. + */ + PRCondVar *cv; + PRIntervalTime timein; + + timein = PR_IntervalNow(); + cv = PR_NewCondVar(_pr_sleeplock); + PR_ASSERT(cv != NULL); + PR_Lock(_pr_sleeplock); + do + { + PRIntervalTime delta = PR_IntervalNow() - timein; + if (delta > timeout) { + break; + } + rv = PR_WaitCondVar(cv, timeout - delta); + } while (rv == PR_SUCCESS); + PR_Unlock(_pr_sleeplock); + PR_DestroyCondVar(cv); + } + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thread) +{ + return thread->id; +} + +PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread) +{ + return (PRThreadPriority) thread->priority; +} + +PR_IMPLEMENT(PRThread *) PR_GetCurrentThread() +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + return _PR_MD_CURRENT_THREAD(); +} + +/* +** Set the interrupt flag for a thread. The thread will be unable to +** block in i/o functions when this happens. Also, any PR_Wait's in +** progress will be undone. The interrupt remains in force until +** PR_ClearInterrupt is called. +*/ +PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thread) +{ +#ifdef _PR_GLOBAL_THREADS_ONLY + PRCondVar *victim; + + _PR_THREAD_LOCK(thread); + thread->flags |= _PR_INTERRUPT; + victim = thread->wait.cvar; + _PR_THREAD_UNLOCK(thread); + if ((NULL != victim) && (!(thread->flags & _PR_INTERRUPT_BLOCKED))) { + int haveLock = (victim->lock->owner == _PR_MD_CURRENT_THREAD()); + + if (!haveLock) { + PR_Lock(victim->lock); + } + PR_NotifyAllCondVar(victim); + if (!haveLock) { + PR_Unlock(victim->lock); + } + } + return PR_SUCCESS; +#else /* ! _PR_GLOBAL_THREADS_ONLY */ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + + _PR_THREAD_LOCK(thread); + thread->flags |= _PR_INTERRUPT; + switch (thread->state) { + case _PR_COND_WAIT: + /* + * call is made with thread locked; + * on return lock is released + */ + if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) { + _PR_NotifyLockedThread(thread); + } + break; + case _PR_IO_WAIT: + /* + * Need to hold the thread lock when calling + * _PR_Unblock_IO_Wait(). On return lock is + * released. + */ +#if defined(XP_UNIX) || defined(WINNT) || defined(WIN16) + if (!(thread->flags & _PR_INTERRUPT_BLOCKED)) { + _PR_Unblock_IO_Wait(thread); + } +#else + _PR_THREAD_UNLOCK(thread); +#endif + break; + case _PR_RUNNING: + case _PR_RUNNABLE: + case _PR_LOCK_WAIT: + default: + _PR_THREAD_UNLOCK(thread); + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } + return PR_SUCCESS; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Clear the interrupt flag for self. +*/ +PR_IMPLEMENT(void) PR_ClearInterrupt() +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_THREAD_LOCK(me); + me->flags &= ~_PR_INTERRUPT; + _PR_THREAD_UNLOCK(me); + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } +} + +PR_IMPLEMENT(void) PR_BlockInterrupt() +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_THREAD_LOCK(me); + _PR_THREAD_BLOCK_INTERRUPT(me); + _PR_THREAD_UNLOCK(me); + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } +} /* PR_BlockInterrupt */ + +PR_IMPLEMENT(void) PR_UnblockInterrupt() +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSOFF(is); + } + _PR_THREAD_LOCK(me); + _PR_THREAD_UNBLOCK_INTERRUPT(me); + _PR_THREAD_UNLOCK(me); + if ( !_PR_IS_NATIVE_THREAD(me)) { + _PR_INTSON(is); + } +} /* PR_UnblockInterrupt */ + +/* +** Return the thread stack pointer of the given thread. +*/ +PR_IMPLEMENT(void *) PR_GetSP(PRThread *thread) +{ + return (void *)_PR_MD_GET_SP(thread); +} + +PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thread) +{ + return thread->environment; +} + +PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *env) +{ + thread->environment = env; +} + + +PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) +{ +#ifdef HAVE_THREAD_AFFINITY + return _PR_MD_GETTHREADAFFINITYMASK(thread, mask); +#else + return 0; +#endif +} + +PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) +{ +#ifdef HAVE_THREAD_AFFINITY + return _PR_MD_SETTHREADAFFINITYMASK(thread, mask); +#else + return 0; +#endif +} + +/* This call is thread unsafe if another thread is calling SetConcurrency() + */ +PR_IMPLEMENT(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask) +{ +#ifdef HAVE_THREAD_AFFINITY + PRCList *qp; + extern PRUint32 _pr_cpu_affinity_mask; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + _pr_cpu_affinity_mask = mask; + + qp = _PR_CPUQ().next; + while(qp != &_PR_CPUQ()) { + _PRCPU *cpu; + + cpu = _PR_CPU_PTR(qp); + PR_SetThreadAffinityMask(cpu->thread, mask); + + qp = qp->next; + } +#endif + + return 0; +} + +PRUint32 _pr_recycleThreads = 0; +PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 count) +{ + _pr_recycleThreads = count; +} + +PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + return _PR_CreateThread(type, start, arg, priority, scope, state, + stackSize, _PR_GCABLE_THREAD); +} + +#ifdef SOLARIS +PR_IMPLEMENT(PRThread*) PR_CreateThreadBound(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRUintn priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + return _PR_CreateThread(type, start, arg, priority, scope, state, + stackSize, _PR_BOUND_THREAD); +} +#endif + + +PR_IMPLEMENT(PRThread*) PR_AttachThreadGCAble( + PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) +{ + /* $$$$ not sure how to finese this one */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PR_IMPLEMENT(void) PR_SetThreadGCAble() +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_Lock(_pr_activeLock); + _PR_MD_CURRENT_THREAD()->flags |= _PR_GCABLE_THREAD; + PR_Unlock(_pr_activeLock); +} + +PR_IMPLEMENT(void) PR_ClearThreadGCAble() +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + PR_Lock(_pr_activeLock); + _PR_MD_CURRENT_THREAD()->flags &= (~_PR_GCABLE_THREAD); + PR_Unlock(_pr_activeLock); +} + +PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thread) +{ + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + if (_PR_IS_NATIVE_THREAD(thread)) { + return (thread->flags & _PR_BOUND_THREAD) ? PR_GLOBAL_BOUND_THREAD : + PR_GLOBAL_THREAD; + } else { + return PR_LOCAL_THREAD; + } +} + +PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thread) +{ + return (thread->flags & _PR_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD; +} + +PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thread) +{ + return (NULL == thread->term) ? PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; +} /* PR_GetThreadState */ diff --git a/nsprpub/pr/src/threads/prdump.c b/nsprpub/pr/src/threads/prdump.c new file mode 100644 index 0000000000..04d842047c --- /dev/null +++ b/nsprpub/pr/src/threads/prdump.c @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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; + +PRUint32 _PR_DumpPrintf(PRFileDesc *fd, const char *fmt, ...) +{ + char buf[100]; + PRUint32 nb; + va_list ap; + + va_start(ap, fmt); + nb = PR_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + PR_Write(fd, buf, nb); + + return nb; +} + +void _PR_DumpThread(PRFileDesc *fd, PRThread *thread) +{ + +#ifndef _PR_GLOBAL_THREADS_ONLY + _PR_DumpPrintf(fd, "%05d[%08p] pri=%2d flags=0x%02x", + thread->id, thread, thread->priority, thread->flags); + switch (thread->state) { + case _PR_RUNNABLE: + case _PR_RUNNING: + break; + case _PR_LOCK_WAIT: + _PR_DumpPrintf(fd, " lock=%p", thread->wait.lock); + break; + case _PR_COND_WAIT: + _PR_DumpPrintf(fd, " condvar=%p sleep=%lldms", + thread->wait.cvar, thread->sleep); + break; + case _PR_SUSPENDED: + _PR_DumpPrintf(fd, " suspended"); + break; + } + PR_Write(fd, "\n", 1); +#endif + + /* Now call dump routine */ + if (thread->dump) { + thread->dump(fd, thread, thread->dumpArg); + } +} + +static void DumpThreadQueue(PRFileDesc *fd, PRCList *list) +{ +#ifndef _PR_GLOBAL_THREADS_ONLY + PRCList *q; + + q = list->next; + while (q != list) { + PRThread *t = _PR_THREAD_PTR(q); + _PR_DumpThread(fd, t); + q = q->next; + } +#endif +} + +void _PR_DumpThreads(PRFileDesc *fd) +{ + PRThread *t; + PRIntn i; + + _PR_DumpPrintf(fd, "Current Thread:\n"); + t = _PR_MD_CURRENT_THREAD(); + _PR_DumpThread(fd, t); + + _PR_DumpPrintf(fd, "Runnable Threads:\n"); + for (i = 0; i < PR_ARRAY_SIZE(_PR_RUNQ(t->cpu)); i++) { + DumpThreadQueue(fd, &_PR_RUNQ(t->cpu)[i]); + } + + _PR_DumpPrintf(fd, "CondVar timed wait Threads:\n"); + DumpThreadQueue(fd, &_PR_SLEEPQ(t->cpu)); + + _PR_DumpPrintf(fd, "CondVar wait Threads:\n"); + DumpThreadQueue(fd, &_PR_PAUSEQ(t->cpu)); + + _PR_DumpPrintf(fd, "Suspended Threads:\n"); + DumpThreadQueue(fd, &_PR_SUSPENDQ(t->cpu)); +} + +PR_IMPLEMENT(void) PR_ShowStatus(void) +{ + PRIntn is; + + if ( _PR_MD_CURRENT_THREAD() + && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { + _PR_INTSOFF(is); + } + _pr_dumpOut = _pr_stderr; + _PR_DumpThreads(_pr_dumpOut); + if ( _PR_MD_CURRENT_THREAD() + && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { + _PR_FAST_INTSON(is); + } +} + +PR_IMPLEMENT(void) +PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg) +{ + thread->dump = dump; + thread->dumpArg = arg; +} diff --git a/nsprpub/pr/src/threads/prmon.c b/nsprpub/pr/src/threads/prmon.c new file mode 100644 index 0000000000..b7c526eea7 --- /dev/null +++ b/nsprpub/pr/src/threads/prmon.c @@ -0,0 +1,357 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +/************************************************************************/ + +/* + * Notifies just get posted to the monitor. The actual notification is done + * when the monitor is fully exited so that MP systems don't contend for a + * monitor that they can't enter. + */ +static void _PR_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast) +{ + PR_ASSERT(mon != NULL); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon); + + /* mon->notifyTimes is protected by the monitor, so we don't need to + * acquire mon->lock. + */ + if (broadcast) { + mon->notifyTimes = -1; + } + else if (mon->notifyTimes != -1) { + mon->notifyTimes += 1; + } +} + +static void _PR_PostNotifiesFromMonitor(PRCondVar *cv, PRIntn times) +{ + PRStatus rv; + + /* + * Time to actually notify any waits that were affected while the monitor + * was entered. + */ + PR_ASSERT(cv != NULL); + PR_ASSERT(times != 0); + if (times == -1) { + rv = PR_NotifyAllCondVar(cv); + PR_ASSERT(rv == PR_SUCCESS); + } else { + while (times-- > 0) { + rv = PR_NotifyCondVar(cv); + PR_ASSERT(rv == PR_SUCCESS); + } + } +} + +/* +** Create a new monitor. +*/ +PR_IMPLEMENT(PRMonitor*) PR_NewMonitor() +{ + PRMonitor *mon; + PRStatus rv; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + mon = PR_NEWZAP(PRMonitor); + if (mon == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + rv = _PR_InitLock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + if (rv != PR_SUCCESS) { + goto error1; + } + + mon->owner = NULL; + + rv = _PR_InitCondVar(&mon->entryCV, &mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + if (rv != PR_SUCCESS) { + goto error2; + } + + rv = _PR_InitCondVar(&mon->waitCV, &mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + if (rv != PR_SUCCESS) { + goto error3; + } + + mon->notifyTimes = 0; + mon->entryCount = 0; + mon->name = NULL; + return mon; + +error3: + _PR_FreeCondVar(&mon->entryCV); +error2: + _PR_FreeLock(&mon->lock); +error1: + PR_Free(mon); + return NULL; +} + +PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) +{ + PRMonitor* mon = PR_NewMonitor(); + if (mon) { + mon->name = name; + } + return mon; +} + +/* +** Destroy a monitor. There must be no thread waiting on the monitor's +** condition variable. The caller is responsible for guaranteeing that the +** monitor is no longer in use. +*/ +PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) +{ + PR_ASSERT(mon != NULL); + _PR_FreeCondVar(&mon->waitCV); + _PR_FreeCondVar(&mon->entryCV); + _PR_FreeLock(&mon->lock); +#if defined(DEBUG) + memset(mon, 0xaf, sizeof(PRMonitor)); +#endif + PR_Free(mon); +} + +/* +** Enter the lock associated with the monitor. +*/ +PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRStatus rv; + + PR_ASSERT(mon != NULL); + PR_Lock(&mon->lock); + if (mon->entryCount != 0) { + if (mon->owner == me) { + goto done; + } + while (mon->entryCount != 0) { + rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(rv == PR_SUCCESS); + } + } + /* and now I have the monitor */ + PR_ASSERT(mon->notifyTimes == 0); + PR_ASSERT(mon->owner == NULL); + mon->owner = me; + +done: + mon->entryCount += 1; + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); +} + +/* +** Test and then enter the lock associated with the monitor if it's not +** already entered by some other thread. Return PR_FALSE if some other +** thread owned the lock at the time of the call. +*/ +PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRStatus rv; + + PR_ASSERT(mon != NULL); + PR_Lock(&mon->lock); + if (mon->entryCount != 0) { + if (mon->owner == me) { + goto done; + } + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + return PR_FALSE; + } + /* and now I have the monitor */ + PR_ASSERT(mon->notifyTimes == 0); + PR_ASSERT(mon->owner == NULL); + mon->owner = me; + +done: + mon->entryCount += 1; + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + return PR_TRUE; +} + +/* +** Exit the lock associated with the monitor once. +*/ +PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRStatus rv; + + PR_ASSERT(mon != NULL); + PR_Lock(&mon->lock); + /* the entries should be > 0 and we'd better be the owner */ + PR_ASSERT(mon->entryCount > 0); + PR_ASSERT(mon->owner == me); + if (mon->entryCount == 0 || mon->owner != me) + { + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + return PR_FAILURE; + } + + mon->entryCount -= 1; /* reduce by one */ + if (mon->entryCount == 0) + { + /* and if it transitioned to zero - notify an entry waiter */ + /* make the owner unknown */ + mon->owner = NULL; + if (mon->notifyTimes != 0) { + _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); + mon->notifyTimes = 0; + } + rv = PR_NotifyCondVar(&mon->entryCV); + PR_ASSERT(rv == PR_SUCCESS); + } + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + return PR_SUCCESS; +} + +/* +** Return the number of times that the current thread has entered the +** lock. Returns zero if the current thread has not entered the lock. +*/ +PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRStatus rv; + PRIntn count = 0; + + PR_Lock(&mon->lock); + if (mon->owner == me) { + count = mon->entryCount; + } + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + return count; +} + +PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon) +{ +#if defined(DEBUG) || defined(FORCE_PR_ASSERT) + PRStatus rv; + + PR_Lock(&mon->lock); + PR_ASSERT(mon->entryCount != 0 && + mon->owner == _PR_MD_CURRENT_THREAD()); + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); +#endif +} + +/* +** Wait for a notify on the condition variable. Sleep for "ticks" amount +** of time (if "tick" is 0 then the sleep is indefinite). While +** the thread is waiting it exits the monitors lock (as if it called +** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When +** the wait has finished the thread regains control of the monitors lock +** with the same entry count as before the wait began. +** +** The thread waiting on the monitor will be resumed when the monitor is +** notified (assuming the thread is the next in line to receive the +** notify) or when the "ticks" elapses. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable. +** This routine can return PR_PENDING_INTERRUPT_ERROR if the waiting thread +** has been interrupted. +*/ +PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks) +{ + PRStatus rv; + PRUint32 saved_entries; + PRThread *saved_owner; + + PR_ASSERT(mon != NULL); + PR_Lock(&mon->lock); + /* the entries better be positive */ + PR_ASSERT(mon->entryCount > 0); + /* and it better be owned by us */ + PR_ASSERT(mon->owner == _PR_MD_CURRENT_THREAD()); /* XXX return failure */ + + /* tuck these away 'till later */ + saved_entries = mon->entryCount; + mon->entryCount = 0; + saved_owner = mon->owner; + mon->owner = NULL; + /* If we have pending notifies, post them now. */ + if (mon->notifyTimes != 0) { + _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); + mon->notifyTimes = 0; + } + rv = PR_NotifyCondVar(&mon->entryCV); + PR_ASSERT(rv == PR_SUCCESS); + + rv = PR_WaitCondVar(&mon->waitCV, ticks); + PR_ASSERT(rv == PR_SUCCESS); + + while (mon->entryCount != 0) { + rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(rv == PR_SUCCESS); + } + PR_ASSERT(mon->notifyTimes == 0); + /* reinstate the interesting information */ + mon->entryCount = saved_entries; + mon->owner = saved_owner; + + rv = PR_Unlock(&mon->lock); + PR_ASSERT(rv == PR_SUCCESS); + return rv; +} + +/* +** Notify the highest priority thread waiting on the condition +** variable. If a thread is waiting on the condition variable (using +** PR_Wait) then it is awakened and begins waiting on the monitor's lock. +*/ +PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) +{ + _PR_PostNotifyToMonitor(mon, PR_FALSE); + return PR_SUCCESS; +} + +/* +** Notify all of the threads waiting on the condition variable. All of +** threads are notified in turn. The highest priority thread will +** probably acquire the monitor first when the monitor is exited. +*/ +PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) +{ + _PR_PostNotifyToMonitor(mon, PR_TRUE); + return PR_SUCCESS; +} + +/************************************************************************/ + +PRUint32 _PR_MonitorToString(PRMonitor *mon, char *buf, PRUint32 buflen) +{ + PRUint32 nb; + + if (mon->owner) { + nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld", + mon, mon->owner->id, mon->owner, mon->entryCount); + } else { + nb = PR_snprintf(buf, buflen, "[%p]", mon); + } + return nb; +} diff --git a/nsprpub/pr/src/threads/prrwlock.c b/nsprpub/pr/src/threads/prrwlock.c new file mode 100644 index 0000000000..0cb51e5bdf --- /dev/null +++ b/nsprpub/pr/src/threads/prrwlock.c @@ -0,0 +1,498 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" + +#include <string.h> + +#if defined(HPUX) && defined(_PR_PTHREADS) + +#include <pthread.h> +#define HAVE_UNIX98_RWLOCK +#define RWLOCK_T pthread_rwlock_t +#define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL) +#define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock) +#define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock) +#define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock) +#define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock) + +#elif defined(SOLARIS) && (defined(_PR_PTHREADS) \ + || defined(_PR_GLOBAL_THREADS_ONLY)) + +#include <synch.h> +#define HAVE_UI_RWLOCK +#define RWLOCK_T rwlock_t +#define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL) +#define RWLOCK_DESTROY(lock) rwlock_destroy(lock) +#define RWLOCK_RDLOCK(lock) rw_rdlock(lock) +#define RWLOCK_WRLOCK(lock) rw_wrlock(lock) +#define RWLOCK_UNLOCK(lock) rw_unlock(lock) + +#endif + +/* + * Reader-writer lock + */ +struct PRRWLock { + char *rw_name; /* lock name */ + PRUint32 rw_rank; /* rank of the lock */ + +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + RWLOCK_T rw_lock; +#else + PRLock *rw_lock; + PRInt32 rw_lock_cnt; /* == 0, if unlocked */ + /* == -1, if write-locked */ + /* > 0 , # of read locks */ + PRUint32 rw_reader_cnt; /* number of waiting readers */ + PRUint32 rw_writer_cnt; /* number of waiting writers */ + PRCondVar *rw_reader_waitq; /* cvar for readers */ + PRCondVar *rw_writer_waitq; /* cvar for writers */ +#ifdef DEBUG + PRThread *rw_owner; /* lock owner for write-lock */ +#endif +#endif +}; + +#ifdef DEBUG +#define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using + rank-order for locks + */ +#endif + +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG + +static PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */ +static PRUintn pr_thread_rwlock_alloc_failed; + +#define _PR_RWLOCK_RANK_ORDER_LIMIT 10 + +typedef struct thread_rwlock_stack { + PRInt32 trs_index; /* top of stack */ + PRRWLock *trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock + pointers */ + +} thread_rwlock_stack; + +static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock); +static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void); +static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock); +static void _PR_RELEASE_LOCK_STACK(void *lock_stack); + +#endif + +/* + * Reader/Writer Locks + */ + +/* + * PR_NewRWLock + * Create a reader-writer lock, with the given lock rank and lock name + * + */ + +PR_IMPLEMENT(PRRWLock *) +PR_NewRWLock(PRUint32 lock_rank, const char *lock_name) +{ + PRRWLock *rwlock; +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + int err; +#endif + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + rwlock = PR_NEWZAP(PRRWLock); + if (rwlock == NULL) { + return NULL; + } + + rwlock->rw_rank = lock_rank; + if (lock_name != NULL) { + rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1); + if (rwlock->rw_name == NULL) { + PR_DELETE(rwlock); + return(NULL); + } + strcpy(rwlock->rw_name, lock_name); + } else { + rwlock->rw_name = NULL; + } + +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + err = RWLOCK_INIT(&rwlock->rw_lock); + if (err != 0) { + PR_SetError(PR_UNKNOWN_ERROR, err); + PR_Free(rwlock->rw_name); + PR_DELETE(rwlock); + return NULL; + } + return rwlock; +#else + rwlock->rw_lock = PR_NewLock(); + if (rwlock->rw_lock == NULL) { + goto failed; + } + rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock); + if (rwlock->rw_reader_waitq == NULL) { + goto failed; + } + rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock); + if (rwlock->rw_writer_waitq == NULL) { + goto failed; + } + rwlock->rw_reader_cnt = 0; + rwlock->rw_writer_cnt = 0; + rwlock->rw_lock_cnt = 0; + return rwlock; + +failed: + if (rwlock->rw_reader_waitq != NULL) { + PR_DestroyCondVar(rwlock->rw_reader_waitq); + } + if (rwlock->rw_lock != NULL) { + PR_DestroyLock(rwlock->rw_lock); + } + PR_Free(rwlock->rw_name); + PR_DELETE(rwlock); + return NULL; +#endif +} + +/* +** Destroy the given RWLock "lock". +*/ +PR_IMPLEMENT(void) +PR_DestroyRWLock(PRRWLock *rwlock) +{ +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + int err; + err = RWLOCK_DESTROY(&rwlock->rw_lock); + PR_ASSERT(err == 0); +#else + PR_ASSERT(rwlock->rw_reader_cnt == 0); + PR_DestroyCondVar(rwlock->rw_reader_waitq); + PR_DestroyCondVar(rwlock->rw_writer_waitq); + PR_DestroyLock(rwlock->rw_lock); +#endif + if (rwlock->rw_name != NULL) { + PR_Free(rwlock->rw_name); + } + PR_DELETE(rwlock); +} + +/* +** Read-lock the RWLock. +*/ +PR_IMPLEMENT(void) +PR_RWLock_Rlock(PRRWLock *rwlock) +{ +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + int err; +#endif + +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG + /* + * assert that rank ordering is not violated; the rank of 'rwlock' should + * be equal to or greater than the highest rank of all the locks held by + * the thread. + */ + PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || + (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); +#endif + +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + err = RWLOCK_RDLOCK(&rwlock->rw_lock); + PR_ASSERT(err == 0); +#else + PR_Lock(rwlock->rw_lock); + /* + * wait if write-locked or if a writer is waiting; preference for writers + */ + while ((rwlock->rw_lock_cnt < 0) || + (rwlock->rw_writer_cnt > 0)) { + rwlock->rw_reader_cnt++; + PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT); + rwlock->rw_reader_cnt--; + } + /* + * Increment read-lock count + */ + rwlock->rw_lock_cnt++; + + PR_Unlock(rwlock->rw_lock); +#endif + +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG + /* + * update thread's lock rank + */ + if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) { + _PR_SET_THREAD_RWLOCK_RANK(rwlock); + } +#endif +} + +/* +** Write-lock the RWLock. +*/ +PR_IMPLEMENT(void) +PR_RWLock_Wlock(PRRWLock *rwlock) +{ +#if defined(DEBUG) + PRThread *me = PR_GetCurrentThread(); +#endif +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + int err; +#endif + +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG + /* + * assert that rank ordering is not violated; the rank of 'rwlock' should + * be equal to or greater than the highest rank of all the locks held by + * the thread. + */ + PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || + (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK())); +#endif + +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + err = RWLOCK_WRLOCK(&rwlock->rw_lock); + PR_ASSERT(err == 0); +#else + PR_Lock(rwlock->rw_lock); + /* + * wait if read locked + */ + while (rwlock->rw_lock_cnt != 0) { + rwlock->rw_writer_cnt++; + PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT); + rwlock->rw_writer_cnt--; + } + /* + * apply write lock + */ + rwlock->rw_lock_cnt--; + PR_ASSERT(rwlock->rw_lock_cnt == -1); +#ifdef DEBUG + PR_ASSERT(me != NULL); + rwlock->rw_owner = me; +#endif + PR_Unlock(rwlock->rw_lock); +#endif + +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG + /* + * update thread's lock rank + */ + if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) { + _PR_SET_THREAD_RWLOCK_RANK(rwlock); + } +#endif +} + +/* +** Unlock the RW lock. +*/ +PR_IMPLEMENT(void) +PR_RWLock_Unlock(PRRWLock *rwlock) +{ +#if defined(DEBUG) + PRThread *me = PR_GetCurrentThread(); +#endif +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + int err; +#endif + +#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) + err = RWLOCK_UNLOCK(&rwlock->rw_lock); + PR_ASSERT(err == 0); +#else + PR_Lock(rwlock->rw_lock); + /* + * lock must be read or write-locked + */ + PR_ASSERT(rwlock->rw_lock_cnt != 0); + if (rwlock->rw_lock_cnt > 0) { + + /* + * decrement read-lock count + */ + rwlock->rw_lock_cnt--; + if (rwlock->rw_lock_cnt == 0) { + /* + * lock is not read-locked anymore; wakeup a waiting writer + */ + if (rwlock->rw_writer_cnt > 0) { + PR_NotifyCondVar(rwlock->rw_writer_waitq); + } + } + } else { + PR_ASSERT(rwlock->rw_lock_cnt == -1); + + rwlock->rw_lock_cnt = 0; +#ifdef DEBUG + PR_ASSERT(rwlock->rw_owner == me); + rwlock->rw_owner = NULL; +#endif + /* + * wakeup a writer, if present; preference for writers + */ + if (rwlock->rw_writer_cnt > 0) { + PR_NotifyCondVar(rwlock->rw_writer_waitq); + } + /* + * else, wakeup all readers, if any + */ + else if (rwlock->rw_reader_cnt > 0) { + PR_NotifyAllCondVar(rwlock->rw_reader_waitq); + } + } + PR_Unlock(rwlock->rw_lock); +#endif + +#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG + /* + * update thread's lock rank + */ + if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) { + _PR_UNSET_THREAD_RWLOCK_RANK(rwlock); + } +#endif + return; +} + +#ifndef _PR_RWLOCK_RANK_ORDER_DEBUG + +void _PR_InitRWLocks(void) { } + +#else + +void _PR_InitRWLocks(void) +{ + /* + * allocated thread-private-data index for rwlock list + */ + if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key, + _PR_RELEASE_LOCK_STACK) == PR_FAILURE) { + pr_thread_rwlock_alloc_failed = 1; + return; + } +} + +/* + * _PR_SET_THREAD_RWLOCK_RANK + * Set a thread's lock rank, which is the highest of the ranks of all + * the locks held by the thread. Pointers to the locks are added to a + * per-thread list, which is anchored off a thread-private data key. + */ + +static void +_PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock) +{ + thread_rwlock_stack *lock_stack; + PRStatus rv; + + /* + * allocate a lock stack + */ + if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) { + lock_stack = (thread_rwlock_stack *) + PR_CALLOC(1 * sizeof(thread_rwlock_stack)); + if (lock_stack) { + rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack); + if (rv == PR_FAILURE) { + PR_DELETE(lock_stack); + pr_thread_rwlock_alloc_failed = 1; + return; + } + } else { + pr_thread_rwlock_alloc_failed = 1; + return; + } + } + /* + * add rwlock to lock stack, if limit is not exceeded + */ + if (lock_stack) { + if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) { + lock_stack->trs_stack[lock_stack->trs_index++] = rwlock; + } + } +} + +static void +_PR_RELEASE_LOCK_STACK(void *lock_stack) +{ + PR_ASSERT(lock_stack); + PR_DELETE(lock_stack); +} + +/* + * _PR_GET_THREAD_RWLOCK_RANK + * + * return thread's lock rank. If thread-private-data for the lock + * stack is not allocated, return PR_RWLOCK_RANK_NONE. + */ + +static PRUint32 +_PR_GET_THREAD_RWLOCK_RANK(void) +{ + thread_rwlock_stack *lock_stack; + + lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); + if (lock_stack == NULL || lock_stack->trs_index == 0) { + return (PR_RWLOCK_RANK_NONE); + } + else { + return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank); + } +} + +/* + * _PR_UNSET_THREAD_RWLOCK_RANK + * + * remove the rwlock from the lock stack. Since locks may not be + * unlocked in a FIFO order, the entire lock stack is searched. + */ + +static void +_PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock) +{ + thread_rwlock_stack *lock_stack; + int new_index = 0, index, done = 0; + + lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); + + PR_ASSERT(lock_stack != NULL); + + for (index = lock_stack->trs_index - 1; index >= 0; index--) { + if (!done && (lock_stack->trs_stack[index] == rwlock)) { + /* + * reset the slot for rwlock + */ + lock_stack->trs_stack[index] = NULL; + done = 1; + } + /* + * search for the lowest-numbered empty slot, above which there are + * no non-empty slots + */ + if (!new_index && (lock_stack->trs_stack[index] != NULL)) { + new_index = index + 1; + } + if (done && new_index) { + break; + } + } + /* + * set top of stack to highest numbered empty slot + */ + lock_stack->trs_index = new_index; + +} + +#endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */ diff --git a/nsprpub/pr/src/threads/prsem.c b/nsprpub/pr/src/threads/prsem.c new file mode 100644 index 0000000000..120075b0cb --- /dev/null +++ b/nsprpub/pr/src/threads/prsem.c @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "primpl.h" +#include "obsolete/prsem.h" + +/************************************************************************/ + +/* +** Create a new semaphore. +*/ +PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) +{ + PRSemaphore *sem; + PRCondVar *cvar; + PRLock *lock; + + sem = PR_NEWZAP(PRSemaphore); + if (sem) { +#ifdef HAVE_CVAR_BUILT_ON_SEM + _PR_MD_NEW_SEM(&sem->md, value); +#else + lock = PR_NewLock(); + if (!lock) { + PR_DELETE(sem); + return NULL; + } + + cvar = PR_NewCondVar(lock); + if (!cvar) { + PR_DestroyLock(lock); + PR_DELETE(sem); + return NULL; + } + sem->cvar = cvar; + sem->count = value; +#endif + } + return sem; +} + +/* +** Destroy a semaphore. There must be no thread waiting on the semaphore. +** The caller is responsible for guaranteeing that the semaphore is +** no longer in use. +*/ +PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *sem) +{ +#ifdef HAVE_CVAR_BUILT_ON_SEM + _PR_MD_DESTROY_SEM(&sem->md); +#else + PR_ASSERT(sem->waiters == 0); + + PR_DestroyLock(sem->cvar->lock); + PR_DestroyCondVar(sem->cvar); +#endif + PR_DELETE(sem); +} + +/* +** Wait on a Semaphore. +** +** This routine allows a calling thread to wait or proceed depending upon the +** state of the semahore sem. The thread can proceed only if the counter value +** of the semaphore sem is currently greater than 0. If the value of semaphore +** sem is positive, it is decremented by one and the routine returns immediately +** allowing the calling thread to continue. If the value of semaphore sem is 0, +** the calling thread blocks awaiting the semaphore to be released by another +** thread. +** +** This routine can return PR_PENDING_INTERRUPT if the waiting thread +** has been interrupted. +*/ +PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *sem) +{ + PRStatus status = PR_SUCCESS; + +#ifdef HAVE_CVAR_BUILT_ON_SEM + return _PR_MD_WAIT_SEM(&sem->md); +#else + PR_Lock(sem->cvar->lock); + while (sem->count == 0) { + sem->waiters++; + status = PR_WaitCondVar(sem->cvar, PR_INTERVAL_NO_TIMEOUT); + sem->waiters--; + if (status != PR_SUCCESS) { + break; + } + } + if (status == PR_SUCCESS) { + sem->count--; + } + PR_Unlock(sem->cvar->lock); +#endif + + return (status); +} + +/* +** This routine increments the counter value of the semaphore. If other threads +** are blocked for the semaphore, then the scheduler will determine which ONE +** thread will be unblocked. +*/ +PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *sem) +{ +#ifdef HAVE_CVAR_BUILT_ON_SEM + _PR_MD_POST_SEM(&sem->md); +#else + PR_Lock(sem->cvar->lock); + if (sem->waiters) { + PR_NotifyCondVar(sem->cvar); + } + sem->count++; + PR_Unlock(sem->cvar->lock); +#endif +} + +#if DEBUG +/* +** Returns the value of the semaphore referenced by sem without affecting +** the state of the semaphore. The value represents the semaphore vaule +** at the time of the call, but may not be the actual value when the +** caller inspects it. (FOR DEBUGGING ONLY) +*/ +PR_IMPLEMENT(PRUintn) PR_GetValueSem(PRSemaphore *sem) +{ + PRUintn rv; + +#ifdef HAVE_CVAR_BUILT_ON_SEM + rv = _PR_MD_GET_VALUE_SEM(&sem->md); +#else + PR_Lock(sem->cvar->lock); + rv = sem->count; + PR_Unlock(sem->cvar->lock); +#endif + + return rv; +} +#endif diff --git a/nsprpub/pr/src/threads/prtpd.c b/nsprpub/pr/src/threads/prtpd.c new file mode 100644 index 0000000000..e6385cf700 --- /dev/null +++ b/nsprpub/pr/src/threads/prtpd.c @@ -0,0 +1,252 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Thread Private Data +** +** There is an aribitrary limit on the number of keys that will be allocated +** by the runtime. It's largish, so it is intended to be a sanity check, not +** an impediment. +** +** There is a counter, initialized to zero and incremented every time a +** client asks for a new key, that holds the high water mark for keys. All +** threads logically have the same high water mark and are permitted to +** ask for TPD up to that key value. +** +** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is +** called. The size of the vector will be some value greater than or equal +** to the current high water mark. Each thread has its own TPD length and +** vector. +** +** Threads that get private data for keys they have not set (or perhaps +** don't even exist for that thread) get a NULL return. If the key is +** beyond the high water mark, an error will be returned. +*/ + +/* +** As of this time, BeOS has its own TPD implementation. Integrating +** this standard one is a TODO for anyone with a bit of spare time on +** their hand. For now, we just #ifdef out this whole file and use +** the routines in pr/src/btthreads/ +*/ + + +#include "primpl.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 + +#define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */ +static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */ +static PRInt32 _pr_tpd_highwater = 0; /* next TPD key to be assigned */ +static PRThreadPrivateDTOR *_pr_tpd_destructors = NULL; +/* the destructors are associated with + the keys, therefore asserting that + the TPD key depicts the data's 'type' */ + +/* +** Initialize the thread private data manipulation +*/ +void _PR_InitTPD(void) +{ + _pr_tpd_destructors = (PRThreadPrivateDTOR*) + PR_CALLOC(_PR_TPD_LIMIT * sizeof(PRThreadPrivateDTOR*)); + PR_ASSERT(NULL != _pr_tpd_destructors); + _pr_tpd_length = _PR_TPD_LIMIT; +} + +/* +** Clean up the thread private data manipulation +*/ +void _PR_CleanupTPD(void) +{ +} /* _PR_CleanupTPD */ + +/* +** This routine returns a new index for per-thread-private data table. +** The index is visible to all threads within a process. This index can +** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines +** to save and retrieve data associated with the index for a thread. +** +** The index independently maintains specific values for each binding thread. +** A thread can only get access to its own thread-specific-data. +** +** Upon a new index return the value associated with the index for all threads +** is NULL, and upon thread creation the value associated with all indices for +** that thread is NULL. +** +** "dtor" is the destructor function to invoke when the private +** data is set or destroyed +** +** Returns PR_FAILURE if the total number of indices will exceed the maximun +** allowed. +*/ + +PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex( + PRUintn *newIndex, PRThreadPrivateDTOR dtor) +{ + PRStatus rv; + PRInt32 index; + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + + PR_ASSERT(NULL != newIndex); + PR_ASSERT(NULL != _pr_tpd_destructors); + + index = PR_ATOMIC_INCREMENT(&_pr_tpd_highwater) - 1; /* allocate index */ + if (_PR_TPD_LIMIT <= index) + { + PR_SetError(PR_TPD_RANGE_ERROR, 0); + rv = PR_FAILURE; /* that's just wrong */ + } + else + { + _pr_tpd_destructors[index] = dtor; /* record destructor @index */ + *newIndex = (PRUintn)index; /* copy into client's location */ + rv = PR_SUCCESS; /* that's okay */ + } + + return rv; +} + +/* +** Define some per-thread-private data. +** "index" is an index into the per-thread private data table +** "priv" is the per-thread-private data +** +** If the per-thread private data table has a previously registered +** destructor function and a non-NULL per-thread-private data value, +** the destructor function is invoked. +** +** This can return PR_FAILURE if index is invalid (ie., beyond the limit +** on the TPD slots) or memory is insufficient to allocate an expanded +** vector. +*/ + +PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv) +{ + PRThread *self = PR_GetCurrentThread(); + + /* + ** To improve performance, we don't check if the index has been + ** allocated. + */ + if (index >= _PR_TPD_LIMIT) + { + PR_SetError(PR_TPD_RANGE_ERROR, 0); + return PR_FAILURE; + } + + PR_ASSERT(((NULL == self->privateData) && (0 == self->tpdLength)) + || ((NULL != self->privateData) && (0 != self->tpdLength))); + + /* + ** If this thread does not have a sufficient vector for the index + ** being set, go ahead and extend this vector now. + */ + if ((NULL == self->privateData) || (self->tpdLength <= index)) + { + void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); + if (NULL == extension) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_FAILURE; + } + if (self->privateData) { + (void)memcpy( + extension, self->privateData, + self->tpdLength * sizeof(void*)); + PR_DELETE(self->privateData); + } + self->tpdLength = _pr_tpd_length; + self->privateData = (void**)extension; + } + /* + ** There wasn't much chance of having to call the destructor + ** unless the slot already existed. + */ + else if (self->privateData[index] && _pr_tpd_destructors[index]) + { + void *data = self->privateData[index]; + self->privateData[index] = NULL; + (*_pr_tpd_destructors[index])(data); + } + + PR_ASSERT(index < self->tpdLength); + self->privateData[index] = priv; + + return PR_SUCCESS; +} + +/* +** Recover the per-thread-private data for the current thread. "index" is +** the index into the per-thread private data table. +** +** The returned value may be NULL which is indistinguishable from an error +** condition. +** +*/ + +PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) +{ + PRThread *self = PR_GetCurrentThread(); + void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ? + NULL : self->privateData[index]; + + return tpd; +} + +/* +** Destroy the thread's private data, if any exists. This is called at +** thread termination time only. There should be no threading issues +** since this is being called by the thread itself. +*/ +void _PR_DestroyThreadPrivate(PRThread* self) +{ +#define _PR_TPD_DESTRUCTOR_ITERATIONS 4 + + if (NULL != self->privateData) /* we have some */ + { + PRBool clean; + PRUint32 index; + PRInt32 passes = _PR_TPD_DESTRUCTOR_ITERATIONS; + PR_ASSERT(0 != self->tpdLength); + do + { + clean = PR_TRUE; + for (index = 0; index < self->tpdLength; ++index) + { + void *priv = self->privateData[index]; /* extract */ + if (NULL != priv) /* we have data at this index */ + { + if (NULL != _pr_tpd_destructors[index]) + { + self->privateData[index] = NULL; /* precondition */ + (*_pr_tpd_destructors[index])(priv); /* destroy */ + clean = PR_FALSE; /* unknown side effects */ + } + } + } + } while ((--passes > 0) && !clean); /* limit # of passes */ + /* + ** We give up after a fixed number of passes. Any non-NULL + ** thread-private data value with a registered destructor + ** function is not destroyed. + */ + memset(self->privateData, 0, self->tpdLength * sizeof(void*)); + } +} /* _PR_DestroyThreadPrivate */ + diff --git a/nsprpub/pr/tests/Makefile.in b/nsprpub/pr/tests/Makefile.in new file mode 100644 index 0000000000..a58a846927 --- /dev/null +++ b/nsprpub/pr/tests/Makefile.in @@ -0,0 +1,446 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#! gmake + +MOD_DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +DIRS = dll + +CSRCS = \ + abstract.c \ + accept.c \ + acceptread.c \ + acceptreademu.c \ + addrstr.c \ + affinity.c \ + alarm.c \ + anonfm.c \ + append.c \ + atomic.c \ + attach.c \ + bigfile.c \ + bigfile2.c \ + bigfile3.c \ + cleanup.c \ + cltsrv.c \ + concur.c \ + cvar.c \ + cvar2.c \ + dceemu.c \ + dlltest.c \ + dtoa.c \ + env.c \ + errcodes.c \ + errset.c \ + exit.c \ + fdcach.c \ + fileio.c \ + foreign.c \ + forktest.c \ + formattm.c \ + fsync.c \ + getai.c \ + gethost.c \ + getproto.c \ + i2l.c \ + initclk.c \ + inrval.c \ + instrumt.c \ + intrio.c \ + intrupt.c \ + io_timeout.c \ + ioconthr.c \ + ipv6.c \ + join.c \ + joinkk.c \ + joinku.c \ + joinuk.c \ + joinuu.c \ + layer.c \ + lazyinit.c \ + libfilename.c \ + lltest.c \ + lock.c \ + lockfile.c \ + logfile.c \ + logger.c \ + makedir.c \ + mbcs.c \ + multiacc.c \ + multiwait.c \ + many_cv.c \ + monref.c \ + nameshm1.c \ + nbconn.c \ + nblayer.c \ + nonblock.c \ + ntioto.c \ + ntoh.c \ + obsints.c \ + op_2long.c \ + op_excl.c \ + op_filnf.c \ + op_filok.c \ + op_noacc.c \ + op_nofil.c \ + openfile.c \ + parent.c \ + parsetm.c \ + peek.c \ + perf.c \ + pipeping.c \ + pipeping2.c \ + pipepong.c \ + pipepong2.c \ + pipeself.c \ + poll_er.c \ + poll_nm.c \ + poll_to.c \ + pollable.c \ + prfdbl.c \ + prftest.c \ + prftest1.c \ + prftest2.c \ + prfz.c \ + primblok.c \ + priotest.c \ + provider.c \ + prpoll.c \ + prpollml.c \ + pushtop.c \ + ranfile.c \ + randseed.c \ + reinit.c \ + rmdir.c \ + rwlockrank.c \ + rwlocktest.c \ + sel_spd.c \ + selct_er.c \ + selct_nm.c \ + selct_to.c \ + select2.c \ + selintr.c \ + sem.c \ + sema.c \ + semaerr.c \ + semaerr1.c \ + semaping.c \ + semapong.c \ + sendzlf.c \ + server_test.c \ + servr_kk.c \ + servr_ku.c \ + servr_uk.c \ + servr_uu.c \ + short_thread.c \ + sigpipe.c \ + socket.c \ + sockopt.c \ + sockping.c \ + sockpong.c \ + sprintf.c \ + stack.c \ + stdio.c \ + str2addr.c \ + strod.c \ + suspend.c \ + switch.c \ + system.c \ + testbit.c \ + testfile.c \ + thrpool_server.c \ + thrpool_client.c \ + threads.c \ + thruput.c \ + timemac.c \ + timetest.c \ + tmoacc.c \ + tmocon.c \ + tpd.c \ + vercheck.c \ + version.c \ + udpsrv.c \ + writev.c \ + xnotify.c \ + y2k.c \ + y2ktmo.c \ + zerolen.c \ + $(NULL) + +ifeq ($(OS_ARCH),WINCE) +CFLAGS += -FImozce_shunt.h -Zi -UDEBUG -DNDEBUG +LDOPTS += -link $(DIST)/lib/mozce_shunt.lib ws2.lib -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO -PDB:$(@:.exe=.pdb) +endif + +ifeq ($(OS_TARGET),OS2) +CSRCS += \ + sleep.c \ + stat.c \ + yield.c \ + $(NULL) +endif + +ifeq (,$(filter-out WINCE WINNT OS2,$(OS_ARCH))) +PROG_SUFFIX = .exe +DLL_SUFFIX = .dll +else +PROG_SUFFIX = +DLL_SUFFIX = +endif + +PROGS = $(addprefix $(OBJDIR)/, $(CSRCS:.c=$(PROG_SUFFIX))) + +TARGETS = $(PROGS) + +INCLUDES = -I$(dist_includedir) -I$(topsrcdir)/pr/include -I$(topsrcdir)/pr/include/private + +ifeq ($(OS_ARCH), WINNT) +ifdef NS_USE_GCC + EXTRA_LIBS += -lws2_32 +else + EXTRA_LIBS += ws2_32.lib + LDOPTS = -NOLOGO -DEBUG -DEBUGTYPE:CV -INCREMENTAL:NO + CFLAGS += -Fd$(@:.$(OBJ_SUFFIX)=.pdb) + ifdef PROFILE + LDOPTS += -PROFILE -MAP + endif # profile +endif # NS_USE_GCC +endif + +ifeq ($(OS_ARCH),OS2) +EXTRA_LIBS = $(OS_LIBS) +LDOPTS = -Zomf -Zlinker /PM:VIO -Zlinker /ST:0x64000 +endif + +ifneq ($(OS_ARCH), WINNT) +# Use an absolute pathname as the runtime library path (for the -R +# or -rpath linker option or the LD_RUN_PATH environment variable). +ifeq (,$(patsubst /%,,$(DIST))) +# $(DIST) is already an absolute pathname. +ABSOLUTE_LIB_DIR = $(dist_libdir) +else +# $(DIST) is a relative pathname: prepend the current directory. +PWD = $(shell pwd) +ABSOLUTE_LIB_DIR = $(PWD)/$(dist_libdir) +endif +endif + +ifeq ($(OS_ARCH), HP-UX) + LDOPTS += -z -Wl,+s,+b,$(ABSOLUTE_LIB_DIR) + ifeq ($(USE_64),1) + LDOPTS += +DD64 + endif + ifeq ($(USE_PTHREADS),1) + EXTRA_LIBS = $(LIBPTHREAD) + endif +endif + +# AIX +ifeq ($(OS_ARCH),AIX) + LDOPTS += -blibpath:$(ABSOLUTE_LIB_DIR):/usr/lib:/lib + ifneq ($(OS_ARCH)$(OS_RELEASE),AIX4.1) + LDOPTS += -brtl + EXTRA_LIBS = -ldl + endif +endif + +# Solaris +ifeq ($(OS_ARCH), SunOS) + ifdef NS_USE_GCC + LDOPTS += -Xlinker -R -Xlinker $(ABSOLUTE_LIB_DIR) + else + ifeq ($(USE_CPLUS), 1) + CC = CC + endif + LDOPTS += -R $(ABSOLUTE_LIB_DIR) + endif + + ifdef USE_PTHREADS + EXTRA_LIBS = -lpthread + endif +endif # SunOS + +ifeq (,$(filter-out Linux GNU GNU_%,$(OS_ARCH))) + LDOPTS += -Xlinker -rpath $(ABSOLUTE_LIB_DIR) + ifeq ($(USE_PTHREADS),1) + EXTRA_LIBS = -lpthread + endif +endif + +ifeq ($(OS_ARCH), SCOOS) +# SCO Unix needs to link against -lsocket again even though we +# already linked with these system libraries when we built libnspr.so. +EXTRA_LIBS = -lsocket +# This hardcodes in the executable programs the directory to find +# libnspr.so etc. at program startup. Equivalent to the -R or -rpath +# option for ld on other platforms. +export LD_RUN_PATH = $(ABSOLUTE_LIB_DIR) +endif + +ifeq ($(OS_ARCH),OpenUNIX) +export LD_RUN_PATH = $(ABSOLUTE_LIB_DIR) +ifeq ($(USE_PTHREADS),1) +LDOPTS += -pthread +endif +endif + +ifeq ($(OS_ARCH), UNIXWARE) +export LD_RUN_PATH = $(ABSOLUTE_LIB_DIR) +endif + +ifeq ($(OS_ARCH),FreeBSD) +ifeq ($(USE_PTHREADS),1) +LDOPTS += -pthread +endif +LDOPTS += -Xlinker -R $(ABSOLUTE_LIB_DIR) +endif + +ifeq ($(OS_ARCH),OpenBSD) +ifeq ($(USE_PTHREADS),1) +LDOPTS += -pthread +endif +endif + +ifeq ($(OS_ARCH),BSD_OS) +ifneq ($(OS_RELEASE),1.1) +EXTRA_LIBS = -ldl +endif +endif + +ifeq ($(OS_ARCH),RISCOS) +EXTRA_LIBS = -ldl +endif + +ifeq ($(USE_PTHREADS),1) +LIBPTHREAD = -lpthread +ifeq ($(OS_ARCH),AIX) +LIBPTHREAD = -lpthreads +endif +ifeq (,$(filter-out FreeBSD OpenBSD BSD_OS QNX Darwin OpenUNIX,$(OS_ARCH))) +LIBPTHREAD = +endif +endif + +ifeq ($(OS_TARGET),Android) +LIBPTHREAD = +XCFLAGS = $(OS_CFLAGS) +endif + +##################################################### +# +# The rules +# +##################################################### + +include $(topsrcdir)/config/rules.mk + +AIX_PRE_4_2 = 0 +ifeq ($(OS_ARCH),AIX) +ifeq ($(OS_RELEASE),4.1) +ifneq ($(USE_PTHREADS), 1) +#AIX_PRE_4_2 = 1 +endif +endif +endif + +ifeq ($(AIX_PRE_4_2),1) + +# AIX releases prior to 4.2 need a special two-step linking hack +# in order to both override the system select() and be able to +# get at the original system select(). +# +# We use a pattern rule in ns/nspr20/config/rules.mk to generate +# the .$(OBJ_SUFFIX) file from the .c source file, then do the +# two-step linking hack below. + +$(OBJDIR)/%: $(OBJDIR)/%.$(OBJ_SUFFIX) + @$(MAKE_OBJDIR) + rm -f $@ $(AIX_TMP) + $(CC) $(AIX_LINK_OPTS) -o $(AIX_TMP) $< $(dist_libdir)/libnspr$(MOD_MAJOR_VERSION).a + $(CC) -o $@ $(AIX_TMP) $(AIX_WRAP) + rm -f $(AIX_TMP) + +else + +# All platforms that are not AIX pre-4.2. + +$(OBJDIR)/%$(PROG_SUFFIX): $(OBJDIR)/%.$(OBJ_SUFFIX) + @$(MAKE_OBJDIR) +ifeq ($(NS_USE_GCC)_$(OS_ARCH),_WINNT) + link $(LDOPTS) $(EXTRA_LDOPTS) $< $(LIBPLC) $(LIBNSPR) $(EXTRA_LIBS) -out:$@ +ifdef MT + @if test -f $@.manifest; then \ + $(MT) -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ + rm -f $@.manifest; \ + fi +endif +else + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(EXTRA_LIBS) -o $@ +endif # WINNT +endif # AIX_PRE_4_2 + +export:: $(TARGETS) +clean:: + rm -f $(TARGETS) + +# The following tests call BSD socket functions, so they need to link +# with -lsocket on some platforms. +ifeq ($(OS_ARCH),SunOS) +ifeq ($(USE_IPV6),1) +$(OBJDIR)/gethost: $(OBJDIR)/gethost.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) -lsocket $(EXTRA_LIBS) -o $@ +endif +$(OBJDIR)/prpoll: $(OBJDIR)/prpoll.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) -lsocket $(EXTRA_LIBS) -o $@ +endif + +ifeq ($(USE_PTHREADS), 1) +$(OBJDIR)/attach: $(OBJDIR)/attach.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@ +$(OBJDIR)/foreign: $(OBJDIR)/foreign.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@ +$(OBJDIR)/provider: $(OBJDIR)/provider.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@ +$(OBJDIR)/socket: $(OBJDIR)/socket.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@ +$(OBJDIR)/testfile: $(OBJDIR)/testfile.o + $(PURE) $(CC) $(XCFLAGS) $< $(LDOPTS) $(LIBPLC) $(LIBNSPR) $(LIBPTHREAD) $(EXTRA_LIBS) -o $@ +endif + +# +# Run the test programs with no arguments +# +# Test output goes to the file pointed to by the environment variable +# NSPR_TEST_LOGFILE, if set, else to /dev/null +# +ECHO = echo +PROGRAMS = $(notdir $(PROGS)) +ifdef NSPR_TEST_LOGFILE +LOGFILE = $(NSPR_TEST_LOGFILE) +else +ifeq (,$(filter-out WINNT OS2,$(OS_ARCH))) +LOGFILE = nul +else +LOGFILE = /dev/null +endif +endif + +ifeq ($(OS_TARGET),Linux) +ECHO = /bin/echo +endif + +ALWAYS: + +runtests:: $(PROGS) ALWAYS + $(topsrcdir)/pr/tests/runtests.sh $(DIST) diff --git a/nsprpub/pr/tests/README.TXT b/nsprpub/pr/tests/README.TXT new file mode 100644 index 0000000000..ebae6cdceb --- /dev/null +++ b/nsprpub/pr/tests/README.TXT @@ -0,0 +1,404 @@ +File: pr/tests/readme + +This document describes the test cases in the NSPR directory +pr/tests. + +===================================================================== +There is a sub-directory here: + +dll + sources for the .dll(.so) used by test dlltest.c + +===================================================================== +The individual files are described here. + +The script 'runtests.ksh' enumerates and runs test cases that are +expected to run on all platforms. + + +accept.c + Tests PR_Accept() and related socket functions. + +acceptread.c + Tests PR_AcceptRead() + +alarm.c + Tests alarm functions declared in obsolete/pralarm.h. + The alarm functions are obsolete, so is this test. + +atomic.c + Tests Atomic operations defined in pratom.h + +attach.c + Test PR_AttachThread() + Note: This is an NSPR private function. + +bigfile.c + Test 64bit file offset functions declared in prio.h + +bug1test.c + Demonstrates a bug on NT. + +cleanup.c + Tests PR_Cleanup() declared in prinit.h + +cltsrv.c + Tests many socket functions. + +concur.c + Tests threading functions and concurrent operations. + +cvar.c + Tests condition variables. + +cvar2.c + Tests condition variables. A rather abusive test. + +dbmalloc.c + Obsolete. Originally for testing debug builds of NSPR's malloc. + +dbmalloc1.c + Obsolete. Originally for testing debug builds of NSPR's malloc. + +dceemu.c + Tests special functions for DCE emulation. + +depend.c + Obsoltet. Tests early spec for library dependency. + +dlltest.c + Tests dynamic library functions. Used with dll/my.c + +dtoa.c + Tests conversions of double to string. + +exit.c + Tests PR_ProcessExit() declared in prinit.h + +fileio.c + Tests NSPR semaphores a bit of file i/o and threading + functions. + +foreign.c + Test auto-attach of a thread created by something other than + NSPR. + +forktest.c + Limited use. Tests unix fork() and related functions. + +fsync.c + Tests use of PR_Sync() declared in prio.h + +getproto.c + Tests socket functions PR_GetProtoByName(), etc. + +i2l.c + Tests LongLong functions for converting 32bit integer to 64bit + integer. + +initclk.c + Tests timing on minimal use of condition variable + +inrval.c + Tests interval timing functions. + +instrumt.c + Tests instrumentation functions. prcountr.h prtrace.h + +intrupt.c + Tests PR_Interrupt() + +ioconthr.c + Tests i/o continuation mechanism in pthreads. + +io_timeout.c + Test socket i/o timeouts. + +io_timeoutk.c + Obsolete. Subsumed in io_timeout.c + +io_timeoutu.c + Obsolete. Subsumed in io_timeout.c + +ipv6.c + Tests IPv6. IPv6 is not used by NSPR clients. + +join.c + Tests PR_JoinThread() + +joinkk.c + Tests PR_JoinThread() + +joinku.c + Tests PR_JoinThread() + +joinuk.c + Tests PR_JoinThread() + +joinuu.c + Tests PR_JoinThread() + +layer.c + Tests layered I/O. + +lazyinit.c + Tests implicit initialization. + +lltest.c + Tests LongLong (64bit integer) arithmentic and conversions. + +lock.c + Tests PR_Lock() in heavily threaded environment. + +lockfile.c + Test PR_Lockfile(). + +logger.c + Tests PR_LOG() + +makefile + The makefile that builds all the tests + +many_cv.c + Tests aquiring a large number of condition variables. + +multiwait.c + ??? + +nbconn.c + Test non-blocking connect. + +nblayer.c + Tests NSPR's layered I/O capability. + +nonblock.c + Tests operations on non-blocking socket. + +op_2long.c + Tests PR_Open() where filename is too long. + +op_filnf.c + Tests PR_Open() where filename is NotFound. + +op_filok.c + Tests PR_Open() where filename is accessable. + +op_noacc.c + Tests PR_Open() where file permissions are wrong. + Limited use. Windows has no concept of Unix style file permissions. + +op_nofil.c + Tests PR_Open() where filename does not exist. + +parent.c + Test parent/child process capability + +perf.c + Tests and measures context switch times for various thread + syncronization functions. + +pipeping.c + Tests inter-process pipes. Run with pipepong.c + +pipepong.c + Tests inter-process pipes. Run with pipeping.c + +pipeself.c + Tests inter-thread pipes. + +pollable.c + Tests pollable events. prio.h + +poll_er.c + Tests PR_Poll() where an error is expected. + +poll_nm.c + Tests PR_Poll() where normal operation is expected. + +poll_to.c + Tests PR_Poll() where timeout is expected. + +prftest.c + Tests printf-like formatting. + +prftest1.c + Obsolete. Subsumed in prftest.c + +prftest2.c + Obsolete. Subsumed in prftest.c + +prfz.c + Tests printf handling of (s)size_t formats + +priotest.c + Limited use. Tests NSPR thread dispatching priority. + +provider.c + +prpoll.c + Tests PR_Poll(). + +prselect.c + Obsolete. PR_Select() is obsolete. + +prttools.h + Unused file. + +ranfile.c + Tests random file access. + +readme + This file. + +runtests.ksh + A korn shell script that runs a set of tests that should run + on any of the NSPR supported platforms. + +runtests.pl + A perl script to run the test cases. This srcipt runs tests + common to all platforms and runs tests applicable to specific + platforms. Uses file runtests.txt to control execution. + +runtests.txt + Control file for perl script: runtests.pl + +rwlocktest.c + Tests Reader/Writer lock + +selct_er.c + Obsolete. PR_Select() is obsolete. + +selct_nm.c + Obsolete. PR_Select() is obsolete. + +selct_to.c + Obsolete. PR_Select() is obsolete. + +select2.c + Obsolete. PR_Select() is obsolete. + +sel_spd.c + Obsolete. PR_Select() is obsolete. + +sem.c + Obsolete. Semaphores are not supported. + +server_test.c + Tests sockets by simulating a server in loopback mode. + Makes its own client threads. + +servr_kk.c + Tests client/server sockets, threads using system threads. + +servr_ku.c + Tests client/server sockets, threads using system and user threads. + +servr_uk.c + Tests client/server sockets, threads using system and user threads. + +servr_uu.c + Tests client/server sockets, threads user threads. + +short_thread.c + Tests short-running threads. Useful for testing for race conditions. + +sigpipe.c + Tests NSPR's SIGPIPE handler. Unix only. + +sleep.c + Limited use. Tests sleep capability of platform. + +socket.c + Tests many socket functions. + +sockopt.c + Tests setting and getting socket options. + +sprintf.c + Tests sprintf. + +stack.c + Test atomic stack operations. + +stat.c + Tests performance of getfileinfo() vs. stat() + +stdio.c + Tests NSPR's handling of stdin, stdout, stderr. + +strod.c + Tests formatting of double precision floating point. + +suspend.c + Private interfaces PR_SuspendAll(), PR_ResumeAll(), etc. + +switch.c + Tests thread switching + +system.c + Tests PR_GetSystemInfo() + +testbit.c + Tests bit arrays. + +testfile.c + Tests many file I/O functions. + +threads.c + Tests thread caching. + +thruput.c + Tests socket thruput. Must be run by hand as client/server. + Does not self terminate. + +time.c + Incomplete. Limited use. + +timemac.c + Test time and date functions. Originally for Mac. + +timetest.c + Tests time conversion over a wide range of dates. + +tmoacc.c + Server to tmocon.c and writev.c + Do not run it by itself. + +tmocon.c + Client thread to tmoacc.c + +tpd.c + Tests thread private data. + +udpsrv.c + Tests UDP socket functions. + +ut_ttools.h + unused file. + +version.c + Extract and print library version data. + +vercheck.c + Test PR_VersionCheck(). + +writev.c + Tests gather-write on a socket. Requires tmoacc.c + +xnotify.c + Tests cached monitors. + +yield.c + Limited use + +y2k.c + Test to verify NSPR's date functions as Y2K compliant. + +dll\Makefile + makefile for mygetval.c, mysetval.c + +dll\mygetval.c + Dynamic library test. See also dlltest.c + +dll\mysetval.c + Dynamic library test. See also dlltest.c diff --git a/nsprpub/pr/tests/abstract.c b/nsprpub/pr/tests/abstract.c new file mode 100644 index 0000000000..3d255c1c2b --- /dev/null +++ b/nsprpub/pr/tests/abstract.c @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdio.h> + +#if defined(LINUX) + +#include <string.h> +#include "nspr.h" + +static const char abstractSocketName[] = "\0testsocket"; + +static void +ClientThread(void* aArg) +{ + PRFileDesc* socket; + PRNetAddr addr; + PRUint8 buf[1024]; + PRInt32 len; + PRInt32 total; + + addr.local.family = PR_AF_LOCAL; + memcpy(addr.local.path, abstractSocketName, sizeof(abstractSocketName)); + + socket = PR_OpenTCPSocket(addr.raw.family); + if (!socket) { + fprintf(stderr, "PR_OpenTCPSokcet failed\n"); + exit(1); + } + + if (PR_Connect(socket, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + fprintf(stderr, "PR_Connect failed\n"); + exit(1); + } + + total = 0; + while (total < sizeof(buf)) { + len = PR_Recv(socket, buf + total, sizeof(buf) - total, 0, + PR_INTERVAL_NO_TIMEOUT); + if (len < 1) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + total += len; + } + + total = 0; + while (total < sizeof(buf)) { + len = PR_Send(socket, buf + total, sizeof(buf) - total, 0, + PR_INTERVAL_NO_TIMEOUT); + if (len < 1) { + fprintf(stderr, "PR_Send failed\n"); + exit(1); + } + total += len; + } + + if (PR_Close(socket) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +int +main() +{ + PRFileDesc* socket; + PRFileDesc* acceptSocket; + PRThread* thread; + PRNetAddr addr; + PRUint8 buf[1024]; + PRInt32 len; + PRInt32 total; + + addr.local.family = PR_AF_LOCAL; + memcpy(addr.local.path, abstractSocketName, sizeof(abstractSocketName)); + + socket = PR_OpenTCPSocket(addr.raw.family); + if (!socket) { + fprintf(stderr, "PR_OpenTCPSocket failed\n"); + exit(1); + } + if (PR_Bind(socket, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + + if (PR_Listen(socket, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + + thread = PR_CreateThread(PR_USER_THREAD, ClientThread, 0, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (!thread) { + fprintf(stderr, "PR_CreateThread failed"); + exit(1); + } + + acceptSocket = PR_Accept(socket, NULL, PR_INTERVAL_NO_TIMEOUT); + if (!acceptSocket) { + fprintf(stderr, "PR_Accept failed\n"); + exit(1); + } + + memset(buf, 'A', sizeof(buf)); + + total = 0; + while (total < sizeof(buf)) { + len = PR_Send(acceptSocket, buf + total, sizeof(buf) - total, 0, + PR_INTERVAL_NO_TIMEOUT); + if (len < 1) { + fprintf(stderr, "PR_Send failed\n"); + exit(1); + } + total += len; + } + + total = 0; + while (total < sizeof(buf)) { + len = PR_Recv(acceptSocket, buf + total, sizeof(buf) - total, 0, + PR_INTERVAL_NO_TIMEOUT); + if (len < 1) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + total += len; + } + + if (PR_Close(acceptSocket) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + if (PR_JoinThread(thread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + + if (PR_Close(socket) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + printf("PASS\n"); + return 0; +} + +#else +int +main() +{ + printf("PASS\n"); + return 0; +} +#endif diff --git a/nsprpub/pr/tests/accept.c b/nsprpub/pr/tests/accept.c new file mode 100644 index 0000000000..3e3850b698 --- /dev/null +++ b/nsprpub/pr/tests/accept.c @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1996 - Netscape Communications Corporation +** +** Name: accept.c +** +** Description: Run accept() sucessful connection tests. +** +** Modification History: +** 04-Jun-97 AGarcia - Reconvert test file to return a 0 for PASS and a 1 for FAIL +** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 Revert to return code 0 and 1. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ + +#include "nspr.h" +#include "prpriv.h" + +#include <stdlib.h> +#include <string.h> + +#include "plgetopt.h" +#include "plerror.h" + +#define BASE_PORT 10000 + +#define CLIENT_DATA 128 + +#define ACCEPT_NORMAL 0x1 +#define ACCEPT_FAST 0x2 +#define ACCEPT_READ 0x3 +#define ACCEPT_READ_FAST 0x4 +#define ACCEPT_READ_FAST_CB 0x5 + +#define CLIENT_NORMAL 0x1 +#define CLIENT_TIMEOUT_ACCEPT 0x2 +#define CLIENT_TIMEOUT_SEND 0x3 + +#define SERVER_MAX_BIND_COUNT 100 + +#if defined(XP_OS2) +#define TIMEOUTSECS 10 +#else +#define TIMEOUTSECS 2 +#endif +PRIntervalTime timeoutTime; + +static PRInt32 count = 1; +static PRFileDesc *output; +static PRNetAddr serverAddr; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; +static PRInt32 clientCommand; +static PRInt32 iterations; +static PRStatus rv; +static PRFileDesc *listenSock; +static PRFileDesc *clientSock = NULL; +static PRNetAddr listenAddr; +static PRNetAddr clientAddr; +static PRThread *clientThread; +static PRNetAddr *raddr; +static char buf[4096 + 2*sizeof(PRNetAddr) + 32]; +static PRInt32 status; +static PRInt32 bytesRead; + +PRIntn failed_already=0; +PRIntn debug_mode; + +void Test_Assert(const char *msg, const char *file, PRIntn line) +{ + failed_already=1; + if (debug_mode) { + PR_fprintf(output, "@%s:%d ", file, line); + PR_fprintf(output, msg); + } +} /* Test_Assert */ + +#define TEST_ASSERT(expr) \ + if (!(expr)) Test_Assert(#expr, __FILE__, __LINE__) + +#ifdef WINNT +#define CALLBACK_MAGIC 0x12345678 + +void timeout_callback(void *magic) +{ + TEST_ASSERT(magic == (void *)CALLBACK_MAGIC); + if (debug_mode) { + PR_fprintf(output, "timeout callback called okay\n"); + } +} +#endif + + +static void PR_CALLBACK +ClientThread(void *_action) +{ + PRInt32 action = * (PRInt32 *) _action; + PRInt32 iterations = count; + PRFileDesc *sock = NULL; + + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = listenAddr.inet.port; + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + + for (; iterations--;) { + PRInt32 rv; + char buf[CLIENT_DATA]; + + memset(buf, 0xaf, sizeof(buf)); /* initialize with arbitrary data */ + sock = PR_NewTCPSocket(); + if (!sock) { + if (!debug_mode) { + failed_already=1; + } + else { + PR_fprintf(output, "client: unable to create socket\n"); + } + return; + } + + if (action != CLIENT_TIMEOUT_ACCEPT) { + + if ((rv = PR_Connect(sock, &serverAddr, + timeoutTime)) < 0) { + if (!debug_mode) { + failed_already=1; + } + else + PR_fprintf(output, + "client: unable to connect to server (%ld, %ld, %ld, %ld)\n", + iterations, rv, PR_GetError(), PR_GetOSError()); + goto ErrorExit; + } + + if (action != CLIENT_TIMEOUT_SEND) { + if ((rv = PR_Send(sock, buf, CLIENT_DATA, + 0, timeoutTime))< 0) { + if (!debug_mode) { + failed_already=1; + } else { + PR_fprintf(output, + "client: unable to send to server (%d, %ld, %ld)\n", + CLIENT_DATA, rv, PR_GetError()); + } + goto ErrorExit; + } + } else { + PR_Sleep(PR_SecondsToInterval(TIMEOUTSECS + 1)); + } + } else { + PR_Sleep(PR_SecondsToInterval(TIMEOUTSECS + 1)); + } + if (debug_mode) { + PR_fprintf(output, "."); + } + PR_Close(sock); + sock = NULL; + } + if (debug_mode) { + PR_fprintf(output, "\n"); + } + +ErrorExit: + if (sock != NULL) { + PR_Close(sock); + } +} + + +static void +RunTest(PRInt32 acceptType, PRInt32 clientAction) +{ + int i; + + /* First bind to the socket */ + listenSock = PR_NewTCPSocket(); + if (!listenSock) { + failed_already=1; + if (debug_mode) { + PR_fprintf(output, "unable to create listen socket\n"); + } + return; + } + memset(&listenAddr, 0, sizeof(listenAddr)); + listenAddr.inet.family = PR_AF_INET; + listenAddr.inet.port = PR_htons(BASE_PORT); + listenAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + while (PR_Bind(listenSock, &listenAddr) == PR_FAILURE) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + listenAddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + failed_already=1; + if (debug_mode) { + PR_fprintf(output,"accept: ERROR - PR_Bind failed\n"); + } + return; + } + + + rv = PR_Listen(listenSock, 100); + if (rv == PR_FAILURE) { + failed_already=1; + if (debug_mode) { + PR_fprintf(output, "unable to listen\n"); + } + return; + } + + clientCommand = clientAction; + clientThread = PR_CreateThread(PR_USER_THREAD, ClientThread, + (void *)&clientCommand, PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 0); + if (!clientThread) { + failed_already=1; + if (debug_mode) { + PR_fprintf(output, "error creating client thread\n"); + } + return; + } + + iterations = count; + for (; iterations--;) { + switch (acceptType) { + case ACCEPT_NORMAL: + clientSock = PR_Accept(listenSock, &clientAddr, + timeoutTime); + switch(clientAction) { + case CLIENT_TIMEOUT_ACCEPT: + TEST_ASSERT(clientSock == 0); + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + case CLIENT_NORMAL: + TEST_ASSERT(clientSock); + bytesRead = PR_Recv(clientSock, + buf, CLIENT_DATA, 0, timeoutTime); + TEST_ASSERT(bytesRead == CLIENT_DATA); + break; + case CLIENT_TIMEOUT_SEND: + TEST_ASSERT(clientSock); + bytesRead = PR_Recv(clientSock, + buf, CLIENT_DATA, 0, timeoutTime); + TEST_ASSERT(bytesRead == -1); + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + } + break; + case ACCEPT_READ: + status = PR_AcceptRead(listenSock, &clientSock, + &raddr, buf, CLIENT_DATA, timeoutTime); + switch(clientAction) { + case CLIENT_TIMEOUT_ACCEPT: + /* Invalid test case */ + TEST_ASSERT(0); + break; + case CLIENT_NORMAL: + TEST_ASSERT(clientSock); + TEST_ASSERT(status == CLIENT_DATA); + break; + case CLIENT_TIMEOUT_SEND: + TEST_ASSERT(status == -1); + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + } + break; +#ifdef WINNT + case ACCEPT_FAST: + clientSock = PR_NTFast_Accept(listenSock, + &clientAddr, timeoutTime); + switch(clientAction) { + case CLIENT_TIMEOUT_ACCEPT: + TEST_ASSERT(clientSock == 0); + if (debug_mode) { + PR_fprintf(output, "PR_GetError is %ld\n", PR_GetError()); + } + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + case CLIENT_NORMAL: + TEST_ASSERT(clientSock); + bytesRead = PR_Recv(clientSock, + buf, CLIENT_DATA, 0, timeoutTime); + TEST_ASSERT(bytesRead == CLIENT_DATA); + break; + case CLIENT_TIMEOUT_SEND: + TEST_ASSERT(clientSock); + bytesRead = PR_Recv(clientSock, + buf, CLIENT_DATA, 0, timeoutTime); + TEST_ASSERT(bytesRead == -1); + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + } + break; + break; + case ACCEPT_READ_FAST: + status = PR_NTFast_AcceptRead(listenSock, + &clientSock, &raddr, buf, 4096, timeoutTime); + switch(clientAction) { + case CLIENT_TIMEOUT_ACCEPT: + /* Invalid test case */ + TEST_ASSERT(0); + break; + case CLIENT_NORMAL: + TEST_ASSERT(clientSock); + TEST_ASSERT(status == CLIENT_DATA); + break; + case CLIENT_TIMEOUT_SEND: + TEST_ASSERT(clientSock == NULL); + TEST_ASSERT(status == -1); + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + } + break; + case ACCEPT_READ_FAST_CB: + status = PR_NTFast_AcceptRead_WithTimeoutCallback( + listenSock, &clientSock, &raddr, buf, 4096, + timeoutTime, timeout_callback, (void *)CALLBACK_MAGIC); + switch(clientAction) { + case CLIENT_TIMEOUT_ACCEPT: + /* Invalid test case */ + TEST_ASSERT(0); + break; + case CLIENT_NORMAL: + TEST_ASSERT(clientSock); + TEST_ASSERT(status == CLIENT_DATA); + break; + case CLIENT_TIMEOUT_SEND: + if (debug_mode) { + PR_fprintf(output, "clientSock = 0x%8.8lx\n", clientSock); + } + TEST_ASSERT(clientSock == NULL); + TEST_ASSERT(status == -1); + TEST_ASSERT(PR_GetError() == PR_IO_TIMEOUT_ERROR); + break; + } + break; +#endif + } + if (clientSock != NULL) { + PR_Close(clientSock); + clientSock = NULL; + } + } + PR_Close(listenSock); + + PR_JoinThread(clientThread); +} + + +void AcceptUpdatedTest(void) +{ + RunTest(ACCEPT_NORMAL, CLIENT_NORMAL); +} +void AcceptNotUpdatedTest(void) +{ + RunTest(ACCEPT_FAST, CLIENT_NORMAL); +} +void AcceptReadTest(void) +{ + RunTest(ACCEPT_READ, CLIENT_NORMAL); +} +void AcceptReadNotUpdatedTest(void) +{ + RunTest(ACCEPT_READ_FAST, CLIENT_NORMAL); +} +void AcceptReadCallbackTest(void) +{ + RunTest(ACCEPT_READ_FAST_CB, CLIENT_NORMAL); +} + +void TimeoutAcceptUpdatedTest(void) +{ + RunTest(ACCEPT_NORMAL, CLIENT_TIMEOUT_ACCEPT); +} +void TimeoutAcceptNotUpdatedTest(void) +{ + RunTest(ACCEPT_FAST, CLIENT_TIMEOUT_ACCEPT); +} +void TimeoutAcceptReadCallbackTest(void) +{ + RunTest(ACCEPT_READ_FAST_CB, CLIENT_TIMEOUT_ACCEPT); +} + +void TimeoutReadUpdatedTest(void) +{ + RunTest(ACCEPT_NORMAL, CLIENT_TIMEOUT_SEND); +} +void TimeoutReadNotUpdatedTest(void) +{ + RunTest(ACCEPT_FAST, CLIENT_TIMEOUT_SEND); +} +void TimeoutReadReadTest(void) +{ + RunTest(ACCEPT_READ, CLIENT_TIMEOUT_SEND); +} +void TimeoutReadReadNotUpdatedTest(void) +{ + RunTest(ACCEPT_READ_FAST, CLIENT_TIMEOUT_SEND); +} +void TimeoutReadReadCallbackTest(void) +{ + RunTest(ACCEPT_READ_FAST_CB, CLIENT_TIMEOUT_SEND); +} + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + if (debug_mode) { + PR_fprintf(output, "%40s: %6.2f usec\n", msg, d / count); + } + +} + +int main(int argc, char **argv) +{ + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name [-d] [-c n] + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdc:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'G': /* global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'c': /* loop counter */ + count = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + output = PR_STDERR; + PR_STDIO_INIT(); + + timeoutTime = PR_SecondsToInterval(TIMEOUTSECS); + if (debug_mode) { + PR_fprintf(output, "\nRun accept() sucessful connection tests\n"); + } + + Measure(AcceptUpdatedTest, "PR_Accept()"); + Measure(AcceptReadTest, "PR_AcceptRead()"); +#ifdef WINNT + Measure(AcceptNotUpdatedTest, "PR_NTFast_Accept()"); + Measure(AcceptReadNotUpdatedTest, "PR_NTFast_AcceptRead()"); + Measure(AcceptReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()"); +#endif + if (debug_mode) { + PR_fprintf(output, "\nRun accept() timeout in the accept tests\n"); + } +#ifdef WINNT + Measure(TimeoutReadReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()"); +#endif + Measure(TimeoutReadUpdatedTest, "PR_Accept()"); + if (debug_mode) { + PR_fprintf(output, "\nRun accept() timeout in the read tests\n"); + } + Measure(TimeoutReadReadTest, "PR_AcceptRead()"); +#ifdef WINNT + Measure(TimeoutReadNotUpdatedTest, "PR_NTFast_Accept()"); + Measure(TimeoutReadReadNotUpdatedTest, "PR_NTFast_AcceptRead()"); + Measure(TimeoutReadReadCallbackTest, "PR_NTFast_AcceptRead_WithTimeoutCallback()"); +#endif + PR_fprintf(output, "%s\n", (failed_already) ? "FAIL" : "PASS"); + return failed_already; +} diff --git a/nsprpub/pr/tests/acceptread.c b/nsprpub/pr/tests/acceptread.c new file mode 100644 index 0000000000..090b084555 --- /dev/null +++ b/nsprpub/pr/tests/acceptread.c @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <prio.h> +#include <prprf.h> +#include <prinit.h> +#include <prnetdb.h> +#include <prinrval.h> +#include <prthread.h> + +#include <plerror.h> + +#include <stdlib.h> + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define DEFAULT_PORT 12273 PORT_INC_DO PORT_INC_3264 +#define GET "GET / HTTP/1.0\n\n" +static PRFileDesc *std_out, *err_out; +static PRIntervalTime write_dally, accept_timeout; + +static PRStatus PrintAddress(const PRNetAddr* address) +{ + char buffer[100]; + PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer)); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_NetAddrToString"); + } + else PR_fprintf( + std_out, "Accepted connection from (0x%p)%s:%d\n", + address, buffer, address->inet.port); + return rv; +} /* PrintAddress */ + +static void ConnectingThread(void *arg) +{ + PRInt32 nbytes; + char buf[1024]; + PRFileDesc *sock; + PRNetAddr peer_addr, *addr; + + addr = (PRNetAddr*)arg; + + sock = PR_NewTCPSocket(); + if (sock == NULL) + { + PL_FPrintError(err_out, "PR_NewTCPSocket (client) failed"); + PR_ProcessExit(1); + } + + if (PR_Connect(sock, addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_Connect (client) failed"); + PR_ProcessExit(1); + } + if (PR_GetPeerName(sock, &peer_addr) == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_GetPeerName (client) failed"); + PR_ProcessExit(1); + } + + /* + ** Then wait between the connection coming up and sending the expected + ** data. At some point in time, the server should fail due to a timeou + ** on the AcceptRead() operation, which according to the document is + ** only due to the read() portion. + */ + PR_Sleep(write_dally); + + nbytes = PR_Send(sock, GET, sizeof(GET), 0, PR_INTERVAL_NO_TIMEOUT); + if (nbytes == -1) { + PL_FPrintError(err_out, "PR_Send (client) failed"); + } + + nbytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); + if (nbytes == -1) { + PL_FPrintError(err_out, "PR_Recv (client) failed"); + } + else + { + PR_fprintf(std_out, "PR_Recv (client) succeeded: %d bytes\n", nbytes); + buf[sizeof(buf) - 1] = '\0'; + PR_fprintf(std_out, "%s\n", buf); + } + + if (PR_FAILURE == PR_Shutdown(sock, PR_SHUTDOWN_BOTH)) { + PL_FPrintError(err_out, "PR_Shutdown (client) failed"); + } + + if (PR_FAILURE == PR_Close(sock)) { + PL_FPrintError(err_out, "PR_Close (client) failed"); + } + + return; +} /* ConnectingThread */ + +#define BUF_SIZE 117 +static void AcceptingThread(void *arg) +{ + PRStatus rv; + PRInt32 bytes; + PRSize buf_size = BUF_SIZE; + PRUint8 buf[BUF_SIZE + (2 * sizeof(PRNetAddr)) + 32]; + PRNetAddr *accept_addr, *listen_addr = (PRNetAddr*)arg; + PRFileDesc *accept_sock, *listen_sock = PR_NewTCPSocket(); + PRSocketOptionData sock_opt; + + if (NULL == listen_sock) + { + PL_FPrintError(err_out, "PR_NewTCPSocket (server) failed"); + PR_ProcessExit(1); + } + sock_opt.option = PR_SockOpt_Reuseaddr; + sock_opt.value.reuse_addr = PR_TRUE; + rv = PR_SetSocketOption(listen_sock, &sock_opt); + if (PR_FAILURE == rv) + { + PL_FPrintError(err_out, "PR_SetSocketOption (server) failed"); + PR_ProcessExit(1); + } + rv = PR_Bind(listen_sock, listen_addr); + if (PR_FAILURE == rv) + { + PL_FPrintError(err_out, "PR_Bind (server) failed"); + PR_ProcessExit(1); + } + rv = PR_Listen(listen_sock, 10); + if (PR_FAILURE == rv) + { + PL_FPrintError(err_out, "PR_Listen (server) failed"); + PR_ProcessExit(1); + } + bytes = PR_AcceptRead( + listen_sock, &accept_sock, &accept_addr, buf, buf_size, accept_timeout); + + if (-1 == bytes) { + PL_FPrintError(err_out, "PR_AcceptRead (server) failed"); + } + else + { + PrintAddress(accept_addr); + PR_fprintf( + std_out, "(Server) read [0x%p..0x%p) %s\n", + buf, &buf[BUF_SIZE], buf); + bytes = PR_Write(accept_sock, buf, bytes); + rv = PR_Shutdown(accept_sock, PR_SHUTDOWN_BOTH); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_Shutdown (server) failed"); + } + } + + if (-1 != bytes) + { + rv = PR_Close(accept_sock); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_Close (server) failed"); + } + } + + rv = PR_Close(listen_sock); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_Close (server) failed"); + } +} /* AcceptingThread */ + +int main(int argc, char **argv) +{ + PRHostEnt he; + PRStatus status; + PRIntn next_index; + PRUint16 port_number; + char netdb_buf[PR_NETDB_BUF_SIZE]; + PRNetAddr client_addr, server_addr; + PRThread *client_thread, *server_thread; + PRIntervalTime delta = PR_MillisecondsToInterval(500); + + err_out = PR_STDERR; + std_out = PR_STDOUT; + accept_timeout = PR_SecondsToInterval(2); + + if (argc != 2 && argc != 3) { + port_number = DEFAULT_PORT; + } + else { + port_number = (PRUint16)atoi(argv[(argc == 2) ? 1 : 2]); + } + + status = PR_InitializeNetAddr(PR_IpAddrAny, port_number, &server_addr); + if (PR_SUCCESS != status) + { + PL_FPrintError(err_out, "PR_InitializeNetAddr failed"); + PR_ProcessExit(1); + } + if (argc < 3) + { + status = PR_InitializeNetAddr( + PR_IpAddrLoopback, port_number, &client_addr); + if (PR_SUCCESS != status) + { + PL_FPrintError(err_out, "PR_InitializeNetAddr failed"); + PR_ProcessExit(1); + } + } + else + { + status = PR_GetHostByName( + argv[1], netdb_buf, sizeof(netdb_buf), &he); + if (status == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_GetHostByName failed"); + PR_ProcessExit(1); + } + next_index = PR_EnumerateHostEnt(0, &he, port_number, &client_addr); + if (next_index == -1) + { + PL_FPrintError(err_out, "PR_EnumerateHostEnt failed"); + PR_ProcessExit(1); + } + } + + for ( + write_dally = 0; + write_dally < accept_timeout + (2 * delta); + write_dally += delta) + { + PR_fprintf( + std_out, "Testing w/ write_dally = %d msec\n", + PR_IntervalToMilliseconds(write_dally)); + server_thread = PR_CreateThread( + PR_USER_THREAD, AcceptingThread, &server_addr, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (server_thread == NULL) + { + PL_FPrintError(err_out, "PR_CreateThread (server) failed"); + PR_ProcessExit(1); + } + + PR_Sleep(delta); /* let the server pot thicken */ + + client_thread = PR_CreateThread( + PR_USER_THREAD, ConnectingThread, &client_addr, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (client_thread == NULL) + { + PL_FPrintError(err_out, "PR_CreateThread (client) failed"); + PR_ProcessExit(1); + } + + if (PR_JoinThread(client_thread) == PR_FAILURE) { + PL_FPrintError(err_out, "PR_JoinThread (client) failed"); + } + + if (PR_JoinThread(server_thread) == PR_FAILURE) { + PL_FPrintError(err_out, "PR_JoinThread (server) failed"); + } + } + + return 0; +} diff --git a/nsprpub/pr/tests/acceptreademu.c b/nsprpub/pr/tests/acceptreademu.c new file mode 100644 index 0000000000..485b9d5025 --- /dev/null +++ b/nsprpub/pr/tests/acceptreademu.c @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This test is the same as acceptread.c except that it uses the + * emulated acceptread method instead of the regular acceptread. + */ + +#include <prio.h> +#include <prprf.h> +#include <prinit.h> +#include <prnetdb.h> +#include <prinrval.h> +#include <prthread.h> +#include <pprio.h> + +#include <plerror.h> + +#include <stdlib.h> + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define DEFAULT_PORT 12273 PORT_INC_DO PORT_INC_3264 +#define GET "GET / HTTP/1.0\n\n" +static PRFileDesc *std_out, *err_out; +static PRIntervalTime write_dally, accept_timeout; +static PRDescIdentity emu_layer_ident; +static PRIOMethods emu_layer_methods; + +/* the acceptread method in emu_layer_methods */ +static PRInt32 PR_CALLBACK emu_AcceptRead(PRFileDesc *sd, PRFileDesc **nd, + PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + return PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); +} + +static PRStatus PrintAddress(const PRNetAddr* address) +{ + char buffer[100]; + PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer)); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_NetAddrToString"); + } + else PR_fprintf( + std_out, "Accepted connection from (0x%p)%s:%d\n", + address, buffer, address->inet.port); + return rv; +} /* PrintAddress */ + +static void ConnectingThread(void *arg) +{ + PRInt32 nbytes; + char buf[1024]; + PRFileDesc *sock; + PRNetAddr peer_addr, *addr; + + addr = (PRNetAddr*)arg; + + sock = PR_NewTCPSocket(); + if (sock == NULL) + { + PL_FPrintError(err_out, "PR_NewTCPSocket (client) failed"); + PR_ProcessExit(1); + } + + if (PR_Connect(sock, addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_Connect (client) failed"); + PR_ProcessExit(1); + } + if (PR_GetPeerName(sock, &peer_addr) == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_GetPeerName (client) failed"); + PR_ProcessExit(1); + } + + /* + ** Then wait between the connection coming up and sending the expected + ** data. At some point in time, the server should fail due to a timeou + ** on the AcceptRead() operation, which according to the document is + ** only due to the read() portion. + */ + PR_Sleep(write_dally); + + nbytes = PR_Send(sock, GET, sizeof(GET), 0, PR_INTERVAL_NO_TIMEOUT); + if (nbytes == -1) { + PL_FPrintError(err_out, "PR_Send (client) failed"); + } + + nbytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); + if (nbytes == -1) { + PL_FPrintError(err_out, "PR_Recv (client) failed"); + } + else + { + PR_fprintf(std_out, "PR_Recv (client) succeeded: %d bytes\n", nbytes); + buf[sizeof(buf) - 1] = '\0'; + PR_fprintf(std_out, "%s\n", buf); + } + + if (PR_FAILURE == PR_Shutdown(sock, PR_SHUTDOWN_BOTH)) { + PL_FPrintError(err_out, "PR_Shutdown (client) failed"); + } + + if (PR_FAILURE == PR_Close(sock)) { + PL_FPrintError(err_out, "PR_Close (client) failed"); + } + + return; +} /* ConnectingThread */ + +#define BUF_SIZE 117 +static void AcceptingThread(void *arg) +{ + PRStatus rv; + PRInt32 bytes; + PRSize buf_size = BUF_SIZE; + PRUint8 buf[BUF_SIZE + (2 * sizeof(PRNetAddr)) + 32]; + PRNetAddr *accept_addr, *listen_addr = (PRNetAddr*)arg; + PRFileDesc *accept_sock, *listen_sock = PR_NewTCPSocket(); + PRFileDesc *layer; + PRSocketOptionData sock_opt; + + if (NULL == listen_sock) + { + PL_FPrintError(err_out, "PR_NewTCPSocket (server) failed"); + PR_ProcessExit(1); + } + layer = PR_CreateIOLayerStub(emu_layer_ident, &emu_layer_methods); + if (NULL == layer) + { + PL_FPrintError(err_out, "PR_CreateIOLayerStub (server) failed"); + PR_ProcessExit(1); + } + if (PR_PushIOLayer(listen_sock, PR_TOP_IO_LAYER, layer) == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_PushIOLayer (server) failed"); + PR_ProcessExit(1); + } + sock_opt.option = PR_SockOpt_Reuseaddr; + sock_opt.value.reuse_addr = PR_TRUE; + rv = PR_SetSocketOption(listen_sock, &sock_opt); + if (PR_FAILURE == rv) + { + PL_FPrintError(err_out, "PR_SetSocketOption (server) failed"); + PR_ProcessExit(1); + } + rv = PR_Bind(listen_sock, listen_addr); + if (PR_FAILURE == rv) + { + PL_FPrintError(err_out, "PR_Bind (server) failed"); + PR_ProcessExit(1); + } + rv = PR_Listen(listen_sock, 10); + if (PR_FAILURE == rv) + { + PL_FPrintError(err_out, "PR_Listen (server) failed"); + PR_ProcessExit(1); + } + bytes = PR_AcceptRead( + listen_sock, &accept_sock, &accept_addr, buf, buf_size, accept_timeout); + + if (-1 == bytes) { + PL_FPrintError(err_out, "PR_AcceptRead (server) failed"); + } + else + { + PrintAddress(accept_addr); + PR_fprintf( + std_out, "(Server) read [0x%p..0x%p) %s\n", + buf, &buf[BUF_SIZE], buf); + bytes = PR_Write(accept_sock, buf, bytes); + rv = PR_Shutdown(accept_sock, PR_SHUTDOWN_BOTH); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_Shutdown (server) failed"); + } + } + + if (-1 != bytes) + { + rv = PR_Close(accept_sock); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_Close (server) failed"); + } + } + + rv = PR_Close(listen_sock); + if (PR_FAILURE == rv) { + PL_FPrintError(err_out, "PR_Close (server) failed"); + } +} /* AcceptingThread */ + +int main(int argc, char **argv) +{ + PRHostEnt he; + PRStatus status; + PRIntn next_index; + PRUint16 port_number; + char netdb_buf[PR_NETDB_BUF_SIZE]; + PRNetAddr client_addr, server_addr; + PRThread *client_thread, *server_thread; + PRIntervalTime delta = PR_MillisecondsToInterval(500); + + err_out = PR_STDERR; + std_out = PR_STDOUT; + accept_timeout = PR_SecondsToInterval(2); + emu_layer_ident = PR_GetUniqueIdentity("Emulated AcceptRead"); + emu_layer_methods = *PR_GetDefaultIOMethods(); + emu_layer_methods.acceptread = emu_AcceptRead; + + if (argc != 2 && argc != 3) { + port_number = DEFAULT_PORT; + } + else { + port_number = (PRUint16)atoi(argv[(argc == 2) ? 1 : 2]); + } + + status = PR_InitializeNetAddr(PR_IpAddrAny, port_number, &server_addr); + if (PR_SUCCESS != status) + { + PL_FPrintError(err_out, "PR_InitializeNetAddr failed"); + PR_ProcessExit(1); + } + if (argc < 3) + { + status = PR_InitializeNetAddr( + PR_IpAddrLoopback, port_number, &client_addr); + if (PR_SUCCESS != status) + { + PL_FPrintError(err_out, "PR_InitializeNetAddr failed"); + PR_ProcessExit(1); + } + } + else + { + status = PR_GetHostByName( + argv[1], netdb_buf, sizeof(netdb_buf), &he); + if (status == PR_FAILURE) + { + PL_FPrintError(err_out, "PR_GetHostByName failed"); + PR_ProcessExit(1); + } + next_index = PR_EnumerateHostEnt(0, &he, port_number, &client_addr); + if (next_index == -1) + { + PL_FPrintError(err_out, "PR_EnumerateHostEnt failed"); + PR_ProcessExit(1); + } + } + + for ( + write_dally = 0; + write_dally < accept_timeout + (2 * delta); + write_dally += delta) + { + PR_fprintf( + std_out, "Testing w/ write_dally = %d msec\n", + PR_IntervalToMilliseconds(write_dally)); + server_thread = PR_CreateThread( + PR_USER_THREAD, AcceptingThread, &server_addr, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (server_thread == NULL) + { + PL_FPrintError(err_out, "PR_CreateThread (server) failed"); + PR_ProcessExit(1); + } + + PR_Sleep(delta); /* let the server pot thicken */ + + client_thread = PR_CreateThread( + PR_USER_THREAD, ConnectingThread, &client_addr, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (client_thread == NULL) + { + PL_FPrintError(err_out, "PR_CreateThread (client) failed"); + PR_ProcessExit(1); + } + + if (PR_JoinThread(client_thread) == PR_FAILURE) { + PL_FPrintError(err_out, "PR_JoinThread (client) failed"); + } + + if (PR_JoinThread(server_thread) == PR_FAILURE) { + PL_FPrintError(err_out, "PR_JoinThread (server) failed"); + } + } + + return 0; +} diff --git a/nsprpub/pr/tests/addrstr.c b/nsprpub/pr/tests/addrstr.c new file mode 100644 index 0000000000..b6736a74bf --- /dev/null +++ b/nsprpub/pr/tests/addrstr.c @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prnetdb.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +const char *testaddrs[] = { + "::", "::", + "::1", "::1", + "::ffff", "::ffff", + "::1:0", "::0.1.0.0", + "::127.0.0.1", "::127.0.0.1", + "::FFFF:127.0.0.1", "::ffff:127.0.0.1", + "::FFFE:9504:3501", "::fffe:9504:3501", + "0:0:1:0:35c:0:0:0", "0:0:1:0:35c::", + "0:0:3f4c:0:0:4552:0:0", "::3f4c:0:0:4552:0:0", + "0:0:1245:0:0:0:0567:0", "0:0:1245::567:0", + "0:1:2:3:4:5:6:7", "0:1:2:3:4:5:6:7", + "1:2:3:0:4:5:6:7", "1:2:3:0:4:5:6:7", + "1:2:3:4:5:6:7:0", "1:2:3:4:5:6:7:0", + "1:2:3:4:5:6:7:8", "1:2:3:4:5:6:7:8", + "1:2:3:4:5:6::7", "1:2:3:4:5:6:0:7", + 0 +}; + +const char *badaddrs[] = { + "::.1.2.3", + "ffff::.1.2.3", + "1:2:3:4:5:6:7::8", + "1:2:3:4:5:6::7:8", + "::ff99.2.3.4", + 0 +}; + +int failed_already = 0; + +int main(int argc, char **argv) +{ + const char **nexttestaddr = testaddrs; + const char **nextbadaddr = badaddrs; + const char *in, *expected_out; + PRNetAddr addr; + char buf[256]; + PRStatus rv; + + while ((in = *nexttestaddr++) != 0) { + expected_out = *nexttestaddr++; + rv = PR_StringToNetAddr(in, &addr); + if (rv) { + printf("cannot convert %s to addr: %d\n", in, rv); + failed_already = 1; + continue; + } + rv = PR_NetAddrToString(&addr, buf, sizeof(buf)); + if (rv) { + printf("cannot convert %s back to string: %d\n", in, rv); + failed_already = 1; + continue; + } + if (strcmp(buf, expected_out)) { + /* This is not necessarily an error */ + printf("%s expected %s got %s\n", in, expected_out, buf); + } + } + while ((in = *nextbadaddr++) != 0) { + if (PR_StringToNetAddr(in, &addr) == PR_SUCCESS) { + printf("converted bad addr %s\n", in); + failed_already = 1; + } + } + if (failed_already) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/affinity.c b/nsprpub/pr/tests/affinity.c new file mode 100644 index 0000000000..14d08efc11 --- /dev/null +++ b/nsprpub/pr/tests/affinity.c @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "pprthred.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/nsprpub/pr/tests/alarm.c b/nsprpub/pr/tests/alarm.c new file mode 100644 index 0000000000..10a148e448 --- /dev/null +++ b/nsprpub/pr/tests/alarm.c @@ -0,0 +1,552 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1996 - Netscape Communications Corporation +** +** Name: alarmtst.c +** +** Description: Test alarms +** +** Modification History: +** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ + +#include "prlog.h" +#include "prinit.h" +#include "obsolete/pralarm.h" +#include "prlock.h" +#include "prlong.h" +#include "prcvar.h" +#include "prinrval.h" +#include "prtime.h" + +/* Used to get the command line option */ +#include "plgetopt.h" +#include <stdio.h> +#include <stdlib.h> + +#if defined(XP_UNIX) +#include <sys/time.h> +#endif + +static PRIntn debug_mode; +static PRIntn failed_already=0; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +typedef struct notifyData { + PRLock *ml; + PRCondVar *child; + PRCondVar *parent; + PRBool pending; + PRUint32 counter; +} NotifyData; + +static void Notifier(void *arg) +{ + NotifyData *notifyData = (NotifyData*)arg; + PR_Lock(notifyData->ml); + while (notifyData->counter > 0) + { + while (!notifyData->pending) { + PR_WaitCondVar(notifyData->child, PR_INTERVAL_NO_TIMEOUT); + } + notifyData->counter -= 1; + notifyData->pending = PR_FALSE; + PR_NotifyCondVar(notifyData->parent); + } + PR_Unlock(notifyData->ml); +} /* Notifier */ +/*********************************************************************** +** PRIVATE FUNCTION: ConditionNotify +** DESCRIPTION: +** +** INPUTS: loops +** OUTPUTS: None +** RETURN: overhead +** SIDE EFFECTS: +** +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: +** +***********************************************************************/ + + +static PRIntervalTime ConditionNotify(PRUint32 loops) +{ + PRThread *thread; + NotifyData notifyData; + PRIntervalTime timein, overhead; + + timein = PR_IntervalNow(); + + notifyData.counter = loops; + notifyData.ml = PR_NewLock(); + notifyData.child = PR_NewCondVar(notifyData.ml); + notifyData.parent = PR_NewCondVar(notifyData.ml); + thread = PR_CreateThread( + PR_USER_THREAD, Notifier, ¬ifyData, + PR_GetThreadPriority(PR_GetCurrentThread()), + thread_scope, PR_JOINABLE_THREAD, 0); + + overhead = PR_IntervalNow() - timein; /* elapsed so far */ + + PR_Lock(notifyData.ml); + while (notifyData.counter > 0) + { + notifyData.pending = PR_TRUE; + PR_NotifyCondVar(notifyData.child); + while (notifyData.pending) { + PR_WaitCondVar(notifyData.parent, PR_INTERVAL_NO_TIMEOUT); + } + } + PR_Unlock(notifyData.ml); + + timein = PR_IntervalNow(); + + (void)PR_JoinThread(thread); + PR_DestroyCondVar(notifyData.child); + PR_DestroyCondVar(notifyData.parent); + PR_DestroyLock(notifyData.ml); + + overhead += (PR_IntervalNow() - timein); /* more overhead */ + + return overhead; +} /* ConditionNotify */ + +static PRIntervalTime ConditionTimeout(PRUint32 loops) +{ + PRUintn count; + PRIntervalTime overhead, timein = PR_IntervalNow(); + + PRLock *ml = PR_NewLock(); + PRCondVar *cv = PR_NewCondVar(ml); + PRIntervalTime interval = PR_MillisecondsToInterval(50); + + overhead = PR_IntervalNow() - timein; + + PR_Lock(ml); + for (count = 0; count < loops; ++count) + { + overhead += interval; + PR_ASSERT(PR_WaitCondVar(cv, interval) == PR_SUCCESS); + } + PR_Unlock(ml); + + timein = PR_IntervalNow(); + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + overhead += (PR_IntervalNow() - timein); + + return overhead; +} /* ConditionTimeout */ + +typedef struct AlarmData { + PRLock *ml; + PRCondVar *cv; + PRUint32 rate, late, times; + PRIntervalTime duration, timein, period; +} AlarmData; + +static PRBool AlarmFn1(PRAlarmID *id, void *clientData, PRUint32 late) +{ + PRStatus rv = PR_SUCCESS; + PRBool keepGoing, resetAlarm; + PRIntervalTime interval, now = PR_IntervalNow(); + AlarmData *ad = (AlarmData*)clientData; + + PR_Lock(ad->ml); + ad->late += late; + ad->times += 1; + keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ? + PR_TRUE : PR_FALSE; + if (!keepGoing) { + rv = PR_NotifyCondVar(ad->cv); + } + resetAlarm = ((ad->times % 31) == 0) ? PR_TRUE : PR_FALSE; + + interval = (ad->period + ad->rate - 1) / ad->rate; + if (!late && (interval > 10)) + { + interval &= (now & 0x03) + 1; + PR_WaitCondVar(ad->cv, interval); + } + + PR_Unlock(ad->ml); + + if (rv != PR_SUCCESS) + { + if (!debug_mode) { + failed_already=1; + } + else { + printf("AlarmFn: notify status: FAIL\n"); + } + + } + + if (resetAlarm) + { + ad->rate += 3; + ad->late = ad->times = 0; + if (PR_ResetAlarm(id, ad->period, ad->rate) != PR_SUCCESS) + { + if (!debug_mode) { + failed_already=1; + } + else { + printf("AlarmFn: Resetting alarm status: FAIL\n"); + } + + keepGoing = PR_FALSE; + } + + } + + return keepGoing; +} /* AlarmFn1 */ + +static PRIntervalTime Alarms1(PRUint32 loops) +{ + PRAlarm *alarm; + AlarmData ad; + PRIntervalTime overhead, timein = PR_IntervalNow(); + PRIntervalTime duration = PR_SecondsToInterval(3); + + PRLock *ml = PR_NewLock(); + PRCondVar *cv = PR_NewCondVar(ml); + + ad.ml = ml; + ad.cv = cv; + ad.rate = 1; + ad.times = loops; + ad.late = ad.times = 0; + ad.duration = duration; + ad.timein = PR_IntervalNow(); + ad.period = PR_SecondsToInterval(1); + + alarm = PR_CreateAlarm(); + + (void)PR_SetAlarm( + alarm, ad.period, ad.rate, AlarmFn1, &ad); + + overhead = PR_IntervalNow() - timein; + + PR_Lock(ml); + while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration) { + PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(ml); + + timein = PR_IntervalNow(); + (void)PR_DestroyAlarm(alarm); + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + overhead += (PR_IntervalNow() - timein); + + return duration + overhead; +} /* Alarms1 */ + +static PRBool AlarmFn2(PRAlarmID *id, void *clientData, PRUint32 late) +{ + PRBool keepGoing; + PRStatus rv = PR_SUCCESS; + AlarmData *ad = (AlarmData*)clientData; + PRIntervalTime interval, now = PR_IntervalNow(); + + PR_Lock(ad->ml); + ad->times += 1; + keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ? + PR_TRUE : PR_FALSE; + interval = (ad->period + ad->rate - 1) / ad->rate; + + if (!late && (interval > 10)) + { + interval &= (now & 0x03) + 1; + PR_WaitCondVar(ad->cv, interval); + } + + if (!keepGoing) { + rv = PR_NotifyCondVar(ad->cv); + } + + PR_Unlock(ad->ml); + + + if (rv != PR_SUCCESS) { + failed_already=1; + }; + + return keepGoing; +} /* AlarmFn2 */ + +static PRIntervalTime Alarms2(PRUint32 loops) +{ + PRStatus rv; + PRAlarm *alarm; + PRIntervalTime overhead, timein = PR_IntervalNow(); + AlarmData ad; + PRIntervalTime duration = PR_SecondsToInterval(30); + + PRLock *ml = PR_NewLock(); + PRCondVar *cv = PR_NewCondVar(ml); + + ad.ml = ml; + ad.cv = cv; + ad.rate = 1; + ad.times = loops; + ad.late = ad.times = 0; + ad.duration = duration; + ad.timein = PR_IntervalNow(); + ad.period = PR_SecondsToInterval(1); + + alarm = PR_CreateAlarm(); + + (void)PR_SetAlarm( + alarm, ad.period, ad.rate, AlarmFn2, &ad); + + overhead = PR_IntervalNow() - timein; + + PR_Lock(ml); + while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration) { + PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(ml); + + timein = PR_IntervalNow(); + + rv = PR_DestroyAlarm(alarm); + if (rv != PR_SUCCESS) + { + if (!debug_mode) { + failed_already=1; + } + else { + printf("***Destroying alarm status: FAIL\n"); + } + } + + + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + + overhead += (PR_IntervalNow() - timein); + + return duration + overhead; +} /* Alarms2 */ + +static PRIntervalTime Alarms3(PRUint32 loops) +{ + PRIntn i; + PRStatus rv; + PRAlarm *alarm; + AlarmData ad[3]; + PRIntervalTime duration = PR_SecondsToInterval(30); + PRIntervalTime overhead, timein = PR_IntervalNow(); + + PRLock *ml = PR_NewLock(); + PRCondVar *cv = PR_NewCondVar(ml); + + for (i = 0; i < 3; ++i) + { + ad[i].ml = ml; + ad[i].cv = cv; + ad[i].rate = 1; + ad[i].times = loops; + ad[i].duration = duration; + ad[i].late = ad[i].times = 0; + ad[i].timein = PR_IntervalNow(); + ad[i].period = PR_SecondsToInterval(1); + + /* more loops, faster rate => same elapsed time */ + ad[i].times = (i + 1) * loops; + ad[i].rate = (i + 1) * 10; + } + + alarm = PR_CreateAlarm(); + + for (i = 0; i < 3; ++i) + { + (void)PR_SetAlarm( + alarm, ad[i].period, ad[i].rate, + AlarmFn2, &ad[i]); + } + + overhead = PR_IntervalNow() - timein; + + PR_Lock(ml); + for (i = 0; i < 3; ++i) + { + while ((PRIntervalTime)(PR_IntervalNow() - ad[i].timein) < duration) { + PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); + } + } + PR_Unlock(ml); + + timein = PR_IntervalNow(); + + if (debug_mode) + printf + ("Alarms3 finished at %u, %u, %u\n", + ad[0].timein, ad[1].timein, ad[2].timein); + + rv = PR_DestroyAlarm(alarm); + if (rv != PR_SUCCESS) + { + if (!debug_mode) { + failed_already=1; + } + else { + printf("***Destroying alarm status: FAIL\n"); + } + } + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + + overhead += (duration / 3); + overhead += (PR_IntervalNow() - timein); + + return overhead; +} /* Alarms3 */ + +static PRUint32 TimeThis( + const char *msg, PRUint32 (*func)(PRUint32 loops), PRUint32 loops) +{ + PRUint32 overhead, usecs; + PRIntervalTime predicted, timein, timeout, ticks; + + if (debug_mode) { + printf("Testing %s ...", msg); + } + + timein = PR_IntervalNow(); + predicted = func(loops); + timeout = PR_IntervalNow(); + + if (debug_mode) { + printf(" done\n"); + } + + ticks = timeout - timein; + usecs = PR_IntervalToMicroseconds(ticks); + overhead = PR_IntervalToMicroseconds(predicted); + + if(ticks < predicted) + { + if (debug_mode) { + printf("\tFinished in negative time\n"); + printf("\tpredicted overhead was %d usecs\n", overhead); + printf("\ttest completed in %d usecs\n\n", usecs); + } + } + else + { + if (debug_mode) + printf( + "\ttotal: %d usecs\n\toverhead: %d usecs\n\tcost: %6.3f usecs\n\n", + usecs, overhead, ((double)(usecs - overhead) / (double)loops)); + } + + return overhead; +} /* TimeThis */ + +int prmain(int argc, char** argv) +{ + PRUint32 cpu, cpus = 0, loops = 0; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name [-d] + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:c:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'G': /* GLOBAL threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'l': /* loop count */ + loops = atoi(opt->value); + break; + case 'c': /* concurrency limit */ + cpus = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + + if (cpus == 0) { + cpus = 1; + } + if (loops == 0) { + loops = 4; + } + + if (debug_mode) { + printf("Alarm: Using %d loops\n", loops); + } + + if (debug_mode) { + printf("Alarm: Using %d cpu(s)\n", cpus); + } + + for (cpu = 1; cpu <= cpus; ++cpu) + { + if (debug_mode) { + printf("\nAlarm: Using %d CPU(s)\n", cpu); + } + + PR_SetConcurrency(cpu); + + /* some basic time test */ + (void)TimeThis("ConditionNotify", ConditionNotify, loops); + (void)TimeThis("ConditionTimeout", ConditionTimeout, loops); + (void)TimeThis("Alarms1", Alarms1, loops); + (void)TimeThis("Alarms2", Alarms2, loops); + (void)TimeThis("Alarms3", Alarms3, loops); + } + return 0; +} + +int main(int argc, char** argv) +{ + PR_Initialize(prmain, argc, argv, 0); + PR_STDIO_INIT(); + if (failed_already) { + return 1; + } + else { + return 0; + } + +} /* main */ + + +/* alarmtst.c */ diff --git a/nsprpub/pr/tests/anonfm.c b/nsprpub/pr/tests/anonfm.c new file mode 100644 index 0000000000..529be6fae0 --- /dev/null +++ b/nsprpub/pr/tests/anonfm.c @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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, LL_ZERO, 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, LL_ZERO, 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() */ + + +int main(int 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/nsprpub/pr/tests/append.c b/nsprpub/pr/tests/append.c new file mode 100644 index 0000000000..64fe3ff2c1 --- /dev/null +++ b/nsprpub/pr/tests/append.c @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: append.c +** Description: Testing File writes where PR_APPEND was used on open +** +** append attempts to verify that a file opened with PR_APPEND +** will always append to the end of file, regardless where the +** current file pointer is positioned. To do this, PR_Seek() is +** called before each write with the position set to beginning of +** file. Subsequent writes should always append. +** The file is read back, summing the integer data written to the +** file. If the expected result is equal, the test passes. +** +** See BugSplat: 4090 +*/ +#include "plgetopt.h" +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> + +PRIntn debug = 0; +PRIntn verbose = 0; +PRBool failedAlready = PR_FALSE; +const PRInt32 addedBytes = 1000; +const PRInt32 buf = 1; /* constant written to fd, addedBytes times */ +PRInt32 inBuf; /* read it back into here */ + +int main(int argc, char **argv) +{ + PRStatus rc; + PRInt32 rv; + PRFileDesc *fd; + PRIntn i; + PRInt32 sum = 0; + + { /* Get command line options */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "vd"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug */ + debug = 1; + break; + case 'v': /* verbose */ + verbose = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } /* end block "Get command line options" */ + /* ---------------------------------------------------------------------- */ + fd = PR_Open( "./tmp-nsprAppend", (PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY), 0666 ); + if ( NULL == fd ) { + if (debug) { + printf("PR_Open() failed for writing: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + + for ( i = 0; i < addedBytes ; i++ ) { + rv = PR_Write( fd, &buf, sizeof(buf)); + if ( sizeof(buf) != rv ) { + if (debug) { + printf("PR_Write() failed: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + rv = PR_Seek( fd, 0, PR_SEEK_SET ); + if ( -1 == rv ) { + if (debug) { + printf("PR_Seek() failed: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + } + rc = PR_Close( fd ); + if ( PR_FAILURE == rc ) { + if (debug) { + printf("PR_Close() failed after writing: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + /* ---------------------------------------------------------------------- */ + fd = PR_Open( "./tmp-nsprAppend", PR_RDONLY, 0 ); + if ( NULL == fd ) { + if (debug) { + printf("PR_Open() failed for reading: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + + for ( i = 0; i < addedBytes ; i++ ) { + rv = PR_Read( fd, &inBuf, sizeof(inBuf)); + if ( sizeof(inBuf) != rv) { + if (debug) { + printf("PR_Write() failed: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + sum += inBuf; + } + + rc = PR_Close( fd ); + if ( PR_FAILURE == rc ) { + if (debug) { + printf("PR_Close() failed after reading: %d\n", PR_GetError()); + } + failedAlready = PR_TRUE; + goto Finished; + } + if ( sum != addedBytes ) { + if (debug) { + printf("Uh Oh! addedBytes: %d. Sum: %d\n", addedBytes, sum); + } + failedAlready = PR_TRUE; + goto Finished; + } + + /* ---------------------------------------------------------------------- */ +Finished: + if (debug || verbose) { + printf("%s\n", (failedAlready)? "FAILED" : "PASSED" ); + } + return( (failedAlready)? 1 : 0 ); +} /* main() */ + +/* append.c */ diff --git a/nsprpub/pr/tests/atomic.c b/nsprpub/pr/tests/atomic.c new file mode 100644 index 0000000000..42d25da8df --- /dev/null +++ b/nsprpub/pr/tests/atomic.c @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prprf.h" +#include "pratom.h" + +/* + * TODO: create a macro to generate the six lines of code that are repeated + * for every test. Also rewrite the statement + * result = result | ((EXPRESSION) ? 0 : 1); + * as + * result |= !(EXPRESSION); + */ + +int main(int argc, char **argv) +{ + PRInt32 rv, oldval, test, result = 0; + PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput); + + /***********************/ + /* Test the functions. */ + /***********************/ + + oldval = test = -2; + rv = PR_AtomicIncrement(&test); + result = result | ((rv == -1) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicIncrement(%d) == %d: %s\n", + oldval, rv, (rv == -1) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_AtomicIncrement(&test); + result = result | ((rv == 0) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicIncrement(%d) == %d: %s\n", + oldval, rv, (rv == 0) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_AtomicIncrement(&test); + result = result | ((rv == 1) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicIncrement(%d) == %d: %s\n", + oldval, rv, (rv == 1) ? "PASSED" : "FAILED"); + + oldval = test = -2; + rv = PR_AtomicAdd(&test,1); + result = result | ((rv == -1) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicAdd(%d,%d) == %d: %s\n", + oldval, 1, rv, (rv == -1) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_AtomicAdd(&test, 4); + result = result | ((rv == 3) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicAdd(%d,%d) == %d: %s\n", + oldval, 4, rv, (rv == 3) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_AtomicAdd(&test, -6); + result = result | ((rv == -3) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicAdd(%d,%d) == %d: %s\n", + oldval, -6, rv, (rv == -3) ? "PASSED" : "FAILED"); + + oldval = test = 2; + rv = PR_AtomicDecrement(&test); + result = result | ((rv == 1) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicDecrement(%d) == %d: %s\n", + oldval, rv, (rv == 1) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_AtomicDecrement(&test); + result = result | ((rv == 0) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicDecrement(%d) == %d: %s\n", + oldval, rv, (rv == 0) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_AtomicDecrement(&test); + result = result | ((rv == -1) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicDecrement(%d) == %d: %s\n", + oldval, rv, (rv == -1) ? "PASSED" : "FAILED"); + + /* set to a different value */ + oldval = test = -2; + rv = PR_AtomicSet(&test, 2); + result = result | (((rv == -2) && (test == 2)) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicSet(%d, %d) == %d: %s\n", + oldval, 2, rv, ((rv == -2) && (test == 2)) ? "PASSED" : "FAILED"); + + /* set to the same value */ + oldval = test = -2; + rv = PR_AtomicSet(&test, -2); + result = result | (((rv == -2) && (test == -2)) ? 0 : 1); + PR_fprintf( + output, "PR_AtomicSet(%d, %d) == %d: %s\n", + oldval, -2, rv, ((rv == -2) && (test == -2)) ? "PASSED" : "FAILED"); + + /***********************/ + /* Test the macros. */ + /***********************/ + + oldval = test = -2; + rv = PR_ATOMIC_INCREMENT(&test); + result = result | ((rv == -1) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_INCREMENT(%d) == %d: %s\n", + oldval, rv, (rv == -1) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_ATOMIC_INCREMENT(&test); + result = result | ((rv == 0) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_INCREMENT(%d) == %d: %s\n", + oldval, rv, (rv == 0) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_ATOMIC_INCREMENT(&test); + result = result | ((rv == 1) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_INCREMENT(%d) == %d: %s\n", + oldval, rv, (rv == 1) ? "PASSED" : "FAILED"); + + oldval = test = -2; + rv = PR_ATOMIC_ADD(&test,1); + result = result | ((rv == -1) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_ADD(%d,%d) == %d: %s\n", + oldval, 1, rv, (rv == -1) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_ATOMIC_ADD(&test, 4); + result = result | ((rv == 3) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_ADD(%d,%d) == %d: %s\n", + oldval, 4, rv, (rv == 3) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_ATOMIC_ADD(&test, -6); + result = result | ((rv == -3) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_ADD(%d,%d) == %d: %s\n", + oldval, -6, rv, (rv == -3) ? "PASSED" : "FAILED"); + + oldval = test = 2; + rv = PR_ATOMIC_DECREMENT(&test); + result = result | ((rv == 1) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_DECREMENT(%d) == %d: %s\n", + oldval, rv, (rv == 1) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_ATOMIC_DECREMENT(&test); + result = result | ((rv == 0) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_DECREMENT(%d) == %d: %s\n", + oldval, rv, (rv == 0) ? "PASSED" : "FAILED"); + oldval = test; + rv = PR_ATOMIC_DECREMENT(&test); + result = result | ((rv == -1) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_DECREMENT(%d) == %d: %s\n", + oldval, rv, (rv == -1) ? "PASSED" : "FAILED"); + + /* set to a different value */ + oldval = test = -2; + rv = PR_ATOMIC_SET(&test, 2); + result = result | (((rv == -2) && (test == 2)) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_SET(%d, %d) == %d: %s\n", + oldval, 2, rv, ((rv == -2) && (test == 2)) ? "PASSED" : "FAILED"); + + /* set to the same value */ + oldval = test = -2; + rv = PR_ATOMIC_SET(&test, -2); + result = result | (((rv == -2) && (test == -2)) ? 0 : 1); + PR_fprintf( + output, "PR_ATOMIC_SET(%d, %d) == %d: %s\n", + oldval, -2, rv, ((rv == -2) && (test == -2)) ? "PASSED" : "FAILED"); + + PR_fprintf( + output, "Atomic operations test %s\n", + (result == 0) ? "PASSED" : "FAILED"); + return result; +} /* main */ + +/* atomic.c */ diff --git a/nsprpub/pr/tests/attach.c b/nsprpub/pr/tests/attach.c new file mode 100644 index 0000000000..2c1ac99799 --- /dev/null +++ b/nsprpub/pr/tests/attach.c @@ -0,0 +1,299 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1996 - Netscape Communications Corporation +** +** Name: attach.c +** +** Description: Platform-specific code to create a native thread. The native thread will +** repeatedly call PR_AttachThread and PR_DetachThread. The +** primordial thread waits for this new thread to finish. +** +** Modification History: +** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 Revert to return code 0 and 1. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ + +/* Used to get the command line option */ +#include "nspr.h" +#include "pprthred.h" +#include "plgetopt.h" + +#include <stdio.h> + +#ifdef WIN32 +#include <windows.h> +#include <process.h> +#elif defined(_PR_PTHREADS) +#include <pthread.h> +#include "md/_pth.h" +#elif defined(SOLARIS) +#include <thread.h> +#elif defined(OS2) +#define INCL_DOS +#define INCL_ERRORS +#include <os2.h> +#include <process.h> +#endif + +#define DEFAULT_COUNT 1000 +PRIntn failed_already=0; +PRIntn debug_mode; + + +int count; + + +static void +AttachDetach(void) +{ + PRThread *me; + PRInt32 index; + + for (index=0; index<count; index++) { + me = PR_AttachThread(PR_USER_THREAD, + PR_PRIORITY_NORMAL, + NULL); + + if (!me) { + fprintf(stderr, "Error attaching thread %d: PR_AttachThread failed\n", + count); + failed_already = 1; + return; + } + PR_DetachThread(); + } +} + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + if (debug_mode) { + printf("%40s: %6.2f usec\n", msg, d / count); + } +} + +#ifdef WIN32 +static unsigned __stdcall threadStartFunc(void *arg) +#else +static void * threadStartFunc(void *arg) +#endif +{ + Measure(AttachDetach, "Attach/Detach"); + + return 0; +} + +int main(int argc, char **argv) +{ +#ifdef _PR_PTHREADS + int rv; + pthread_t threadID; + pthread_attr_t attr; +#elif defined(SOLARIS) + int rv; + thread_t threadID; +#elif defined(WIN32) + DWORD rv; + unsigned threadID; + HANDLE hThread; +#elif defined(OS2) + int rv; + TID threadID; +#endif + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name [-d] [-c n] + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'c': /* loop count */ + count = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + +#if defined(WIN16) + printf("attach: This test is not valid for Win16\n"); + goto exit_now; +#endif + + if(0 == count) { + count = DEFAULT_COUNT; + } + + /* + * To force the implicit initialization of nspr20 + */ + PR_SetError(0, 0); + PR_STDIO_INIT(); + + /* + * Platform-specific code to create a native thread. The native + * thread will repeatedly call PR_AttachThread and PR_DetachThread. + * The primordial thread waits for this new thread to finish. + */ + +#ifdef _PR_PTHREADS + + rv = _PT_PTHREAD_ATTR_INIT(&attr); + if (debug_mode) { + PR_ASSERT(0 == rv); + } + else if (0 != rv) { + failed_already=1; + goto exit_now; + } + + rv = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + if (debug_mode) { + PR_ASSERT(0 == rv); + } + else if (0 != rv) { + failed_already=1; + goto exit_now; + } + rv = _PT_PTHREAD_CREATE(&threadID, attr, threadStartFunc, NULL); + if (rv != 0) { + fprintf(stderr, "thread creation failed: error code %d\n", rv); + failed_already=1; + goto exit_now; + } + else { + if (debug_mode) { + printf ("thread creation succeeded \n"); + } + + } + rv = _PT_PTHREAD_ATTR_DESTROY(&attr); + if (debug_mode) { + PR_ASSERT(0 == rv); + } + else if (0 != rv) { + failed_already=1; + goto exit_now; + } + rv = pthread_join(threadID, NULL); + if (debug_mode) { + PR_ASSERT(0 == rv); + } + else if (0 != rv) { + failed_already=1; + goto exit_now; + } + +#elif defined(SOLARIS) + + rv = thr_create(NULL, 0, threadStartFunc, NULL, 0, &threadID); + if (rv != 0) { + if(!debug_mode) { + failed_already=1; + goto exit_now; + } else { + fprintf(stderr, "thread creation failed: error code %d\n", rv); + } + } + rv = thr_join(threadID, NULL, NULL); + if (debug_mode) { + PR_ASSERT(0 == rv); + } + else if (0 != rv) + { + failed_already=1; + goto exit_now; + } + + +#elif defined(WIN32) + + hThread = (HANDLE) _beginthreadex(NULL, 0, threadStartFunc, NULL, + STACK_SIZE_PARAM_IS_A_RESERVATION, &threadID); + if (hThread == 0) { + fprintf(stderr, "thread creation failed: error code %d\n", + GetLastError()); + failed_already=1; + goto exit_now; + } + rv = WaitForSingleObject(hThread, INFINITE); + if (debug_mode) { + PR_ASSERT(rv != WAIT_FAILED); + } + else if (rv == WAIT_FAILED) { + failed_already=1; + goto exit_now; + } + +#elif defined(OS2) + + threadID = (TID) _beginthread((void *)threadStartFunc, NULL, + 32768, NULL); + if (threadID == -1) { + fprintf(stderr, "thread creation failed: error code %d\n", errno); + failed_already=1; + goto exit_now; + } + rv = DosWaitThread(&threadID, DCWW_WAIT); + if (debug_mode) { + PR_ASSERT(rv == NO_ERROR); + } else if (rv != NO_ERROR) { + failed_already=1; + goto exit_now; + } + +#else + if (!debug_mode) { + failed_already=1; + } + else + printf("The attach test does not apply to this platform because\n" + "either this platform does not have native threads or the\n" + "test needs to be written for this platform.\n"); + goto exit_now; +#endif + +exit_now: + if(failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/bigfile.c b/nsprpub/pr/tests/bigfile.c new file mode 100644 index 0000000000..e17a767e69 --- /dev/null +++ b/nsprpub/pr/tests/bigfile.c @@ -0,0 +1,358 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prmem.h" +#include "prprf.h" +#include "prinit.h" +#include "prerror.h" +#include "prthread.h" + +#include "plerror.h" +#include "plgetopt.h" + +#define DEFAULT_COUNT 10 +#define DEFAULT_FILESIZE 1 +#define BUFFER_SIZE 1000000 + +typedef enum {v_silent, v_whisper, v_shout} Verbosity; +static void Verbose(Verbosity, const char*, const char*, PRIntn); + +#define VERBOSE(_l, _m) Verbose(_l, _m, __FILE__, __LINE__) + +static PRIntn test_result = 2; +static PRFileDesc *output = NULL; +static PRIntn verbose = v_silent; +static PRIntn filesize = DEFAULT_FILESIZE; + +static PRIntn Usage(void) +{ + PR_fprintf(output, "Bigfile test usage:\n"); + PR_fprintf(output, ">bigfile [-G] [-d] [-v[*v]] [-s <n>] <filename>\n"); + PR_fprintf(output, "\td\tdebug mode (equivalent to -vvv)\t(false)\n"); + PR_fprintf(output, "\tv\tAdditional levels of output\t(none)\n"); + PR_fprintf(output, "\tk\tKeep data file after exit\t(false)\n"); + PR_fprintf(output, "\ts <n>\tFile size in megabytes\t\t(1 megabyte)\n"); + PR_fprintf(output, "\t<filename>\tName of test file\t(none)\n"); + return 2; /* nothing happened */ +} /* Usage */ + +static PRStatus DeleteIfFound(const char *filename) +{ + PRStatus rv; + VERBOSE(v_shout, "Checking for existing file"); + rv = PR_Access(filename, PR_ACCESS_WRITE_OK); + if (PR_SUCCESS == rv) + { + VERBOSE(v_shout, "Deleting existing file"); + rv = PR_Delete(filename); + if (PR_FAILURE == rv) { + VERBOSE(v_shout, "Cannot delete big file"); + } + } + else if (PR_FILE_NOT_FOUND_ERROR != PR_GetError()) { + VERBOSE(v_shout, "Cannot access big file"); + } + else { + rv = PR_SUCCESS; + } + return rv; +} /* DeleteIfFound */ + +static PRIntn Error(const char *msg, const char *filename) +{ + PRInt32 error = PR_GetError(); + if (NULL != msg) + { + if (0 == error) { + PR_fprintf(output, msg); + } + else { + PL_FPrintError(output, msg); + } + } + (void)DeleteIfFound(filename); + if (v_shout == verbose) { + PR_Abort(); + } + return 1; +} /* Error */ + +static void Verbose( + Verbosity level, const char *msg, const char *file, PRIntn line) +{ + if (level <= verbose) { + PR_fprintf(output, "[%s : %d]: %s\n", file, line, msg); + } +} /* Verbose */ + +static void PrintInfo(PRFileInfo64 *info, const char *filename) +{ + PRExplodedTime tm; + char ctime[40], mtime[40]; + static const char *types[] = {"FILE", "DIRECTORY", "OTHER"}; + PR_fprintf( + output, "[%s : %d]: File info for %s\n", + __FILE__, __LINE__, filename); + PR_fprintf( + output, " type: %s, size: %llu bytes,\n", + types[info->type - 1], info->size); + + PR_ExplodeTime(info->creationTime, PR_GMTParameters, &tm); + (void)PR_FormatTime(ctime, sizeof(ctime), "%c GMT", &tm); + PR_ExplodeTime(info->modifyTime, PR_GMTParameters, &tm); + (void)PR_FormatTime(mtime, sizeof(mtime), "%c GMT", &tm); + + PR_fprintf( + output, " creation: %s,\n modify: %s\n", ctime, mtime); +} /* PrintInfo */ + +int main(int argc, char **argv) +{ + PRStatus rv; + char *buffer; + PLOptStatus os; + PRInt32 loop, bytes; + PRFileInfo small_info; + PRFileInfo64 big_info; + PRBool keep = PR_FALSE; + PRFileDesc *file = NULL; + const char *filename = NULL; + PRIntn count = DEFAULT_COUNT; + PRInt64 filesize64, big_answer, big_size, one_meg, zero_meg, big_fragment; + PRInt64 sevenFox = LL_INIT(0,0x7fffffff); + + PLOptState *opt = PL_CreateOptState(argc, argv, "dtvhs:"); + + output = PR_GetSpecialFD(PR_StandardError); + PR_STDIO_INIT(); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: + filename = opt->value; + break; + case 'd': /* debug mode */ + verbose = v_shout; + break; + case 'k': /* keep file */ + keep = PR_TRUE; + break; + case 'v': /* verbosity */ + if (v_shout > verbose) { + verbose += 1; + } + break; + case 'c': /* loop counter */ + count = atoi(opt->value); + break; + case 's': /* filesize */ + filesize = atoi(opt->value); + break; + case 'h': /* confused */ + default: + return Usage(); + } + } + PL_DestroyOptState(opt); + + if (0 == count) { + count = DEFAULT_COUNT; + } + if (0 == filesize) { + filesize = DEFAULT_FILESIZE; + } + if (NULL == filename) + { +#define FILE_NAME "bigfile.dat" + if (DEFAULT_FILESIZE != filesize) { + return Usage(); + } + else { + filename = FILE_NAME; + } + } + + if (PR_FAILURE == DeleteIfFound(filename)) { + return 1; + } + + test_result = 0; + + LL_I2L(zero_meg, 0); + LL_I2L(one_meg, 1000000); + LL_I2L(filesize64, filesize); + buffer = (char*)PR_MALLOC(BUFFER_SIZE); + LL_I2L(big_fragment, BUFFER_SIZE); + LL_MUL(filesize64, filesize64, one_meg); + + for (loop = 0; loop < BUFFER_SIZE; ++loop) { + buffer[loop] = (char)loop; + } + + VERBOSE(v_whisper, "Creating big file"); + file = PR_Open(filename, PR_CREATE_FILE | PR_WRONLY, 0666); + if (NULL == file) { + return Error("PR_Open()", filename); + } + + VERBOSE(v_whisper, "Testing available space in empty file"); + big_answer = file->methods->available64(file); + if (!LL_IS_ZERO(big_answer)) { + return Error("empty available64()", filename); + } + + LL_SUB(big_size, filesize64, one_meg); + VERBOSE(v_whisper, "Creating sparse big file by seeking to end"); + big_answer = file->methods->seek64(file, big_size, PR_SEEK_SET); + if (!LL_EQ(big_answer, big_size)) { + return Error("seek", filename); + } + + VERBOSE(v_whisper, "Writing block at end of sparse file"); + bytes = file->methods->write(file, buffer, BUFFER_SIZE); + if (bytes != BUFFER_SIZE) { + return Error("write", filename); + } + + VERBOSE(v_whisper, "Testing available space at end of sparse file"); + big_answer = file->methods->available64(file); + if (!LL_IS_ZERO(big_answer)) { + return Error("eof available64()", filename); + } + + VERBOSE(v_whisper, "Getting big info on sparse big file"); + rv = file->methods->fileInfo64(file, &big_info); + if (PR_FAILURE == rv) { + return Error("fileInfo64()", filename); + } + if (v_shout <= verbose) { + PrintInfo(&big_info, filename); + } + + VERBOSE(v_whisper, "Getting small info on sparse big file"); + rv = file->methods->fileInfo(file, &small_info); + if (LL_CMP(sevenFox, <, filesize64) && (PR_SUCCESS == rv)) + { + VERBOSE(v_whisper, "Should have failed and didn't"); + return Error("fileInfo()", filename); + } + else if (LL_CMP(sevenFox, >, filesize64) && (PR_FAILURE == rv)) + { + VERBOSE(v_whisper, "Should have succeeded and didn't"); + return Error("fileInfo()", filename); + } + + VERBOSE(v_whisper, "Rewinding big file"); + big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_SET); + if (!LL_IS_ZERO(big_answer)) { + return Error("rewind seek64()", filename); + } + + VERBOSE(v_whisper, "Establishing available space in rewound file"); + big_answer = file->methods->available64(file); + if (LL_NE(filesize64, big_answer)) { + return Error("bof available64()", filename); + } + + VERBOSE(v_whisper, "Closing big file"); + rv = file->methods->close(file); + if (PR_FAILURE == rv) { + return Error("close()", filename); + } + + VERBOSE(v_whisper, "Reopening big file"); + file = PR_Open(filename, PR_RDWR, 0666); + if (NULL == file) { + return Error("open failed", filename); + } + + VERBOSE(v_whisper, "Checking available data in reopened file"); + big_answer = file->methods->available64(file); + if (LL_NE(filesize64, big_answer)) { + return Error("reopened available64()", filename); + } + + big_answer = zero_meg; + VERBOSE(v_whisper, "Rewriting every byte of big file data"); + do + { + bytes = file->methods->write(file, buffer, BUFFER_SIZE); + if (bytes != BUFFER_SIZE) { + return Error("write", filename); + } + LL_ADD(big_answer, big_answer, big_fragment); + } while (LL_CMP(big_answer, <, filesize64)); + + VERBOSE(v_whisper, "Checking position at eof"); + big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_CUR); + if (LL_NE(big_answer, filesize64)) { + return Error("file size error", filename); + } + + VERBOSE(v_whisper, "Testing available space at eof"); + big_answer = file->methods->available64(file); + if (!LL_IS_ZERO(big_answer)) { + return Error("eof available64()", filename); + } + + VERBOSE(v_whisper, "Rewinding full file"); + big_answer = file->methods->seek64(file, zero_meg, PR_SEEK_SET); + if (!LL_IS_ZERO(big_answer)) { + return Error("bof seek64()", filename); + } + + VERBOSE(v_whisper, "Testing available space in rewound file"); + big_answer = file->methods->available64(file); + if (LL_NE(big_answer, filesize64)) { + return Error("bof available64()", filename); + } + + VERBOSE(v_whisper, "Seeking to end of big file"); + big_answer = file->methods->seek64(file, filesize64, PR_SEEK_SET); + if (LL_NE(big_answer, filesize64)) { + return Error("eof seek64()", filename); + } + + VERBOSE(v_whisper, "Getting info on big file while it's open"); + rv = file->methods->fileInfo64(file, &big_info); + if (PR_FAILURE == rv) { + return Error("fileInfo64()", filename); + } + if (v_shout <= verbose) { + PrintInfo(&big_info, filename); + } + + VERBOSE(v_whisper, "Closing big file"); + rv = file->methods->close(file); + if (PR_FAILURE == rv) { + return Error("close()", filename); + } + + VERBOSE(v_whisper, "Getting info on big file after it's closed"); + rv = PR_GetFileInfo64(filename, &big_info); + if (PR_FAILURE == rv) { + return Error("fileInfo64()", filename); + } + if (v_shout <= verbose) { + PrintInfo(&big_info, filename); + } + + VERBOSE(v_whisper, "Deleting big file"); + rv = PR_Delete(filename); + if (PR_FAILURE == rv) { + return Error("PR_Delete()", filename); + } + + PR_DELETE(buffer); + return test_result; +} /* main */ + +/* bigfile.c */ diff --git a/nsprpub/pr/tests/bigfile2.c b/nsprpub/pr/tests/bigfile2.c new file mode 100644 index 0000000000..1a7d45b0f5 --- /dev/null +++ b/nsprpub/pr/tests/bigfile2.c @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <windows.h> +#endif + +#define TEST_FILE_NAME "bigfile2.txt" +#ifdef WINCE +#define TEST_FILE_NAME_FOR_CREATEFILE L"bigfile2.txt" +#else +#define TEST_FILE_NAME_FOR_CREATEFILE TEST_FILE_NAME +#endif + +#define MESSAGE "Hello world!" +#define MESSAGE_SIZE 13 + +int main(int argc, char **argv) +{ + PRFileDesc *fd; + PRInt64 offset, position; + PRInt32 nbytes; + char buf[MESSAGE_SIZE]; +#ifdef _WIN32 + HANDLE hFile; + LARGE_INTEGER li; +#endif /* _WIN32 */ + + LL_I2L(offset, 1); + LL_SHL(offset, offset, 32); + + fd = PR_Open(TEST_FILE_NAME, + PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0666); + if (fd == NULL) { + fprintf(stderr, "PR_Open failed\n"); + exit(1); + } + position = PR_Seek64(fd, offset, PR_SEEK_SET); + if (!LL_GE_ZERO(position)) { + fprintf(stderr, "PR_Seek64 failed\n"); + exit(1); + } + PR_ASSERT(LL_EQ(position, offset)); + strcpy(buf, MESSAGE); + nbytes = PR_Write(fd, buf, sizeof(buf)); + if (nbytes != sizeof(buf)) { + fprintf(stderr, "PR_Write failed\n"); + exit(1); + } + if (PR_Close(fd) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + memset(buf, 0, sizeof(buf)); + +#ifdef _WIN32 + hFile = CreateFile(TEST_FILE_NAME_FOR_CREATEFILE, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + fprintf(stderr, "CreateFile failed\n"); + exit(1); + } + li.QuadPart = offset; + li.LowPart = SetFilePointer(hFile, li.LowPart, &li.HighPart, FILE_BEGIN); + if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR) { + fprintf(stderr, "SetFilePointer failed\n"); + exit(1); + } + PR_ASSERT(li.QuadPart == offset); + if (ReadFile(hFile, buf, sizeof(buf), &nbytes, NULL) == 0) { + fprintf(stderr, "ReadFile failed\n"); + exit(1); + } + PR_ASSERT(nbytes == sizeof(buf)); + if (strcmp(buf, MESSAGE)) { + fprintf(stderr, "corrupt data:$%s$\n", buf); + exit(1); + } + if (CloseHandle(hFile) == 0) { + fprintf(stderr, "CloseHandle failed\n"); + exit(1); + } +#endif /* _WIN32 */ + + if (PR_Delete(TEST_FILE_NAME) == PR_FAILURE) { + fprintf(stderr, "PR_Delete failed\n"); + exit(1); + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/bigfile3.c b/nsprpub/pr/tests/bigfile3.c new file mode 100644 index 0000000000..aeb4d65e41 --- /dev/null +++ b/nsprpub/pr/tests/bigfile3.c @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <windows.h> +#endif + +#define TEST_FILE_NAME "bigfile3.txt" +#ifdef WINCE +#define TEST_FILE_NAME_FOR_CREATEFILE L"bigfile3.txt" +#else +#define TEST_FILE_NAME_FOR_CREATEFILE TEST_FILE_NAME +#endif + +#define MESSAGE "Hello world!" +#define MESSAGE_SIZE 13 + +int main(int argc, char **argv) +{ + PRFileDesc *fd; + PRInt64 offset, position; + PRInt32 nbytes; + char buf[MESSAGE_SIZE]; +#ifdef _WIN32 + HANDLE hFile; + LARGE_INTEGER li; +#endif /* _WIN32 */ + + LL_I2L(offset, 1); + LL_SHL(offset, offset, 32); + +#ifdef _WIN32 + hFile = CreateFile(TEST_FILE_NAME_FOR_CREATEFILE, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + fprintf(stderr, "CreateFile failed\n"); + exit(1); + } + li.QuadPart = offset; + li.LowPart = SetFilePointer(hFile, li.LowPart, &li.HighPart, FILE_BEGIN); + if (li.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR) { + fprintf(stderr, "SetFilePointer failed\n"); + exit(1); + } + PR_ASSERT(li.QuadPart == offset); + strcpy(buf, MESSAGE); + if (WriteFile(hFile, buf, sizeof(buf), &nbytes, NULL) == 0) { + fprintf(stderr, "WriteFile failed\n"); + exit(1); + } + PR_ASSERT(nbytes == sizeof(buf)); + if (CloseHandle(hFile) == 0) { + fprintf(stderr, "CloseHandle failed\n"); + exit(1); + } +#endif /* _WIN32 */ + + memset(buf, 0, sizeof(buf)); + + fd = PR_Open(TEST_FILE_NAME, PR_RDONLY, 0666); + if (fd == NULL) { + fprintf(stderr, "PR_Open failed\n"); + exit(1); + } + position = PR_Seek64(fd, offset, PR_SEEK_SET); + if (!LL_GE_ZERO(position)) { + fprintf(stderr, "PR_Seek64 failed\n"); + exit(1); + } + PR_ASSERT(LL_EQ(position, offset)); + nbytes = PR_Read(fd, buf, sizeof(buf)); + if (nbytes != sizeof(buf)) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + if (strcmp(buf, MESSAGE)) { + fprintf(stderr, "corrupt data:$%s$\n", buf); + exit(1); + } + if (PR_Close(fd) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + if (PR_Delete(TEST_FILE_NAME) == PR_FAILURE) { + fprintf(stderr, "PR_Delete failed\n"); + exit(1); + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/bug1test.c b/nsprpub/pr/tests/bug1test.c new file mode 100644 index 0000000000..62ca90d4aa --- /dev/null +++ b/nsprpub/pr/tests/bug1test.c @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +Attached is a test program that uses the nspr1 to demonstrate a bug +under NT4.0. The fix has already been mentioned (add a ResetEvent just +before leaving the critical section in _PR_CondWait in hwmon.c). +*/ + +#include "prthread.h" +#include "prtypes.h" +#include "prinit.h" +#include "prmon.h" +#include "prlog.h" + +typedef struct Arg_s +{ + PRInt32 a, b; +} Arg_t; + +PRMonitor* gMonitor; // the monitor +PRInt32 gReading; // number of read locks +PRInt32 gWriteWaiting; // number of threads waiting for write lock +PRInt32 gReadWaiting; // number of threads waiting for read lock + +PRInt32 gCounter; // a counter + +// stats +PRInt32 gReads; // number of successful reads +PRInt32 gMaxReads; // max number of simultaneous reads +PRInt32 gMaxWriteWaits; // max number of writes that waited for read +PRInt32 gMaxReadWaits; // max number of reads that waited for write wait + + +void spin (PRInt32 aDelay) +{ + PRInt32 index; + PRInt32 delay = aDelay * 1000; + + PR_Sleep(0); + + // randomize delay a bit + delay = (delay / 2) + (PRInt32)((float)delay * + ((float)rand () / (float)RAND_MAX)); + + for (index = 0; index < delay * 10; index++) + // consume a bunch of cpu cycles + ; + PR_Sleep(0); +} + +void doWriteThread (void* arg) +{ + PRInt32 last; + Arg_t *args = (Arg_t*)arg; + PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; + PR_Sleep(0); + + while (1) + { + // -- enter write lock + PR_EnterMonitor (gMonitor); + + if (0 < gReading) // wait for read locks to go away + { + PRIntervalTime fiveSecs = PR_SecondsToInterval(5); + + gWriteWaiting++; + if (gWriteWaiting > gMaxWriteWaits) { // stats + gMaxWriteWaits = gWriteWaiting; + } + while (0 < gReading) { + PR_Wait (gMonitor, fiveSecs); + } + gWriteWaiting--; + } + // -- write lock entered + + last = gCounter; + gCounter++; + + spin (aWorkDelay); + + PR_ASSERT (gCounter == (last + 1)); // test invariance + + // -- exit write lock +// if (0 < gReadWaiting) // notify waiting reads (do it anyway to show off the CondWait bug) + PR_NotifyAll (gMonitor); + + PR_ExitMonitor (gMonitor); + // -- write lock exited + + spin (aWaitDelay); + } +} + +void doReadThread (void* arg) +{ + PRInt32 last; + Arg_t *args = (Arg_t*)arg; + PRInt32 aWorkDelay = args->a, aWaitDelay = args->b; + PR_Sleep(0); + + while (1) + { + // -- enter read lock + PR_EnterMonitor (gMonitor); + + if (0 < gWriteWaiting) // give up the monitor to waiting writes + { + PRIntervalTime fiveSecs = PR_SecondsToInterval(5); + + gReadWaiting++; + if (gReadWaiting > gMaxReadWaits) { // stats + gMaxReadWaits = gReadWaiting; + } + while (0 < gWriteWaiting) { + PR_Wait (gMonitor, fiveSecs); + } + gReadWaiting--; + } + + gReading++; + + gReads++; // stats + if (gReading > gMaxReads) { // stats + gMaxReads = gReading; + } + + PR_ExitMonitor (gMonitor); + // -- read lock entered + + last = gCounter; + + spin (aWorkDelay); + + PR_ASSERT (gCounter == last); // test invariance + + // -- exit read lock + PR_EnterMonitor (gMonitor); // read unlock + gReading--; + +// if ((0 == gReading) && (0 < gWriteWaiting)) // notify waiting writes (do it anyway to show off the CondWait bug) + PR_NotifyAll (gMonitor); + PR_ExitMonitor (gMonitor); + // -- read lock exited + + spin (aWaitDelay); + } +} + + +void fireThread ( + char* aName, void (*aProc)(void *arg), Arg_t *aArg) +{ + PRThread *thread = PR_CreateThread( + PR_USER_THREAD, aProc, aArg, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); +} + +int pseudoMain (int argc, char** argv, char *pad) +{ + PRInt32 lastWriteCount = gCounter; + PRInt32 lastReadCount = gReads; + Arg_t a1 = {500, 250}; + Arg_t a2 = {500, 500}; + Arg_t a3 = {250, 500}; + Arg_t a4 = {750, 250}; + Arg_t a5 = {100, 750}; + Arg_t a6 = {100, 500}; + Arg_t a7 = {100, 750}; + + gMonitor = PR_NewMonitor (); + + fireThread ("R1", doReadThread, &a1); + fireThread ("R2", doReadThread, &a2); + fireThread ("R3", doReadThread, &a3); + fireThread ("R4", doReadThread, &a4); + + fireThread ("W1", doWriteThread, &a5); + fireThread ("W2", doWriteThread, &a6); + fireThread ("W3", doWriteThread, &a7); + + fireThread ("R5", doReadThread, &a1); + fireThread ("R6", doReadThread, &a2); + fireThread ("R7", doReadThread, &a3); + fireThread ("R8", doReadThread, &a4); + + fireThread ("W4", doWriteThread, &a5); + fireThread ("W5", doWriteThread, &a6); + fireThread ("W6", doWriteThread, &a7); + + while (1) + { + PRInt32 writeCount, readCount; + PRIntervalTime fiveSecs = PR_SecondsToInterval(5); + PR_Sleep (fiveSecs); // get out of the way + + // print some stats, not threadsafe, informative only + writeCount = gCounter; + readCount = gReads; + printf ("\ntick %d writes (+%d), %d reads (+%d) [max %d, %d, %d]", + writeCount, writeCount - lastWriteCount, + readCount, readCount - lastReadCount, + gMaxReads, gMaxWriteWaits, gMaxReadWaits); + lastWriteCount = writeCount; + lastReadCount = readCount; + gMaxReads = gMaxWriteWaits = gMaxReadWaits = 0; + } + return 0; +} + + +static void padStack (int argc, char** argv) +{ + char pad[512]; /* Work around bug in nspr on windoze */ + pseudoMain (argc, argv, pad); +} + +int main(int argc, char **argv) +{ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + padStack (argc, argv); +} + + +/* bug1test.c */ diff --git a/nsprpub/pr/tests/cleanup.c b/nsprpub/pr/tests/cleanup.c new file mode 100644 index 0000000000..679fe59c92 --- /dev/null +++ b/nsprpub/pr/tests/cleanup.c @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prprf.h" +#include "prio.h" +#include "prinit.h" +#include "prthread.h" +#include "prinrval.h" + +#include "plgetopt.h" + +#include <stdlib.h> + +static void PR_CALLBACK Thread(void *sleep) +{ + PR_Sleep(PR_SecondsToInterval((PRUint32)sleep)); + printf("Thread exiting\n"); +} + +static void Help(void) +{ + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + PR_fprintf(err, "Cleanup usage: [-g] [-s n] [-t n] [-c n] [-h]\n"); + PR_fprintf(err, "\t-c Call cleanup before exiting (default: false)\n"); + PR_fprintf(err, "\t-G Use global threads only (default: local)\n"); + PR_fprintf(err, "\t-t n Number of threads involved (default: 1)\n"); + PR_fprintf(err, "\t-s n Seconds thread(s) should dally (defaut: 10)\n"); + PR_fprintf(err, "\t-S n Seconds main() should dally (defaut: 5)\n"); + PR_fprintf(err, "\t-C n Value to set concurrency (default 1)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + PRBool cleanup = PR_FALSE; + PRThreadScope type = PR_LOCAL_THREAD; + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + PLOptState *opt = PL_CreateOptState(argc, argv, "Ghs:S:t:cC:"); + PRIntn concurrency = 1, child_sleep = 10, main_sleep = 5, threads = 1; + + PR_STDIO_INIT(); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'c': /* call PR_Cleanup() before exiting */ + cleanup = PR_TRUE; + break; + case 'G': /* local vs global threads */ + type = PR_GLOBAL_THREAD; + break; + case 's': /* time to sleep */ + child_sleep = atoi(opt->value); + break; + case 'S': /* time to sleep */ + main_sleep = atoi(opt->value); + break; + case 'C': /* number of cpus to create */ + concurrency = atoi(opt->value); + break; + case 't': /* number of threads to create */ + threads = atoi(opt->value); + break; + case 'h': /* user wants some guidance */ + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_fprintf(err, "Cleanup settings\n"); + PR_fprintf(err, "\tThread type: %s\n", + (PR_LOCAL_THREAD == type) ? "LOCAL" : "GLOBAL"); + PR_fprintf(err, "\tConcurrency: %d\n", concurrency); + PR_fprintf(err, "\tNumber of threads: %d\n", threads); + PR_fprintf(err, "\tThread sleep: %d\n", child_sleep); + PR_fprintf(err, "\tMain sleep: %d\n", main_sleep); + PR_fprintf(err, "\tCleanup will %sbe called\n\n", (cleanup) ? "" : "NOT "); + + PR_SetConcurrency(concurrency); + + while (threads-- > 0) + (void)PR_CreateThread( + PR_USER_THREAD, Thread, (void*)child_sleep, PR_PRIORITY_NORMAL, + type, PR_UNJOINABLE_THREAD, 0); + PR_Sleep(PR_SecondsToInterval(main_sleep)); + + if (cleanup) { + PR_Cleanup(); + } + + PR_fprintf(err, "main() exiting\n"); + return 0; +} /* main */ diff --git a/nsprpub/pr/tests/cltsrv.c b/nsprpub/pr/tests/cltsrv.c new file mode 100644 index 0000000000..2dbe4c7ae9 --- /dev/null +++ b/nsprpub/pr/tests/cltsrv.c @@ -0,0 +1,1271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Notes: + * [1] lth. The call to Sleep() is a hack to get the test case to run + * on Windows 95. Without it, the test case fails with an error + * WSAECONNRESET following a recv() call. The error is caused by the + * server side thread termination without a shutdown() or closesocket() + * call. Windows docmunentation suggests that this is predicted + * behavior; that other platforms get away with it is ... serindipity. + * The test case should shutdown() or closesocket() before + * thread termination. I didn't have time to figure out where or how + * to do it. The Sleep() call inserts enough delay to allow the + * client side to recv() all his data before the server side thread + * terminates. Whew! ... + * + ** Modification History: + * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. + * The debug mode will print all of the printfs associated with this test. + * The regress mode will be the default mode. Since the regress tool limits + * the output to a one line status:PASS or FAIL,all of the printf statements + * have been handled with an if (debug_mode) statement. + */ + +#include "prclist.h" +#include "prcvar.h" +#include "prerror.h" +#include "prinit.h" +#include "prinrval.h" +#include "prio.h" +#include "prlock.h" +#include "prlog.h" +#include "prtime.h" +#include "prmem.h" +#include "prnetdb.h" +#include "prprf.h" +#include "prthread.h" + +#include "pprio.h" +#include "primpl.h" + +#include "plstr.h" +#include "plerror.h" +#include "plgetopt.h" + +#include <stdlib.h> +#include <string.h> + +#if defined(XP_UNIX) +#include <math.h> +#endif + +/* +** This is the beginning of the test +*/ + +#define RECV_FLAGS 0 +#define SEND_FLAGS 0 +#define DEFAULT_LOW 0 +#define DEFAULT_HIGH 0 +#define BUFFER_SIZE 1024 +#define DEFAULT_BACKLOG 5 + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define DEFAULT_PORT 12849 PORT_INC_DO PORT_INC_3264 + +#define DEFAULT_CLIENTS 1 +#define ALLOWED_IN_ACCEPT 1 +#define DEFAULT_CLIPPING 1000 +#define DEFAULT_WORKERS_MIN 1 +#define DEFAULT_WORKERS_MAX 1 +#define DEFAULT_SERVER "localhost" +#define DEFAULT_EXECUTION_TIME 10 +#define DEFAULT_CLIENT_TIMEOUT 4000 +#define DEFAULT_SERVER_TIMEOUT 4000 +#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH + +typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t; + +static void PR_CALLBACK Worker(void *arg); +typedef struct CSPool_s CSPool_t; +typedef struct CSWorker_s CSWorker_t; +typedef struct CSServer_s CSServer_t; +typedef enum Verbosity +{ + TEST_LOG_ALWAYS, + TEST_LOG_ERROR, + TEST_LOG_WARNING, + TEST_LOG_NOTICE, + TEST_LOG_INFO, + TEST_LOG_STATUS, + TEST_LOG_VERBOSE +} Verbosity; + +static PRInt32 domain = AF_INET; +static PRInt32 protocol = 6; /* TCP */ +static PRFileDesc *debug_out = NULL; +static PRBool debug_mode = PR_FALSE; +static PRBool pthread_stats = PR_FALSE; +static Verbosity verbosity = TEST_LOG_ALWAYS; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +struct CSWorker_s +{ + PRCList element; /* list of the server's workers */ + + PRThread *thread; /* this worker objects thread */ + CSServer_t *server; /* back pointer to server structure */ +}; + +struct CSPool_s +{ + PRCondVar *exiting; + PRCondVar *acceptComplete; + PRUint32 accepting, active, workers; +}; + +struct CSServer_s +{ + PRCList list; /* head of worker list */ + + PRLock *ml; + PRThread *thread; /* the main server thread */ + PRCondVar *stateChange; + + PRUint16 port; /* port we're listening on */ + PRUint32 backlog; /* size of our listener backlog */ + PRFileDesc *listener; /* the fd accepting connections */ + + CSPool_t pool; /* statistics on worker threads */ + CSState_t state; /* the server's state */ + struct /* controlling worker counts */ + { + PRUint32 minimum, maximum, accepting; + } workers; + + /* statistics */ + PRIntervalTime started, stopped; + PRUint32 operations, bytesTransferred; +}; + +typedef struct CSDescriptor_s +{ + PRInt32 size; /* size of transfer */ + char filename[60]; /* filename, null padded */ +} CSDescriptor_t; + +typedef struct CSClient_s +{ + PRLock *ml; + PRThread *thread; + PRCondVar *stateChange; + PRNetAddr serverAddress; + + CSState_t state; + + /* statistics */ + PRIntervalTime started, stopped; + PRUint32 operations, bytesTransferred; +} CSClient_t; + +#define TEST_LOG(l, p, a) \ + do { \ + if (debug_mode || (p <= verbosity)) printf a; \ + } while (0) + +PRLogModuleInfo *cltsrv_log_file = NULL; + +#define MY_ASSERT(_expr) \ + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) + +#define TEST_ASSERT(_expr) \ + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) + +static void _MY_Assert(const char *s, const char *file, PRIntn ln) +{ + PL_PrintError(NULL); + PR_Assert(s, file, ln); +} /* _MY_Assert */ + +static PRBool Aborted(PRStatus rv) +{ + return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ? + PR_TRUE : PR_FALSE; +} + +static void TimeOfDayMessage(const char *msg, PRThread* me) +{ + char buffer[100]; + PRExplodedTime tod; + PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod); + (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("%s(0x%p): %s\n", msg, me, buffer)); +} /* TimeOfDayMessage */ + + +static void PR_CALLBACK Client(void *arg) +{ + PRStatus rv; + PRIntn index; + char buffer[1024]; + PRFileDesc *fd = NULL; + PRUintn clipping = DEFAULT_CLIPPING; + PRThread *me = PR_GetCurrentThread(); + CSClient_t *client = (CSClient_t*)arg; + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT); + + + for (index = 0; index < sizeof(buffer); ++index) { + buffer[index] = (char)index; + } + + client->started = PR_IntervalNow(); + + PR_Lock(client->ml); + client->state = cs_run; + PR_NotifyCondVar(client->stateChange); + PR_Unlock(client->ml); + + TimeOfDayMessage("Client started at", me); + + while (cs_run == client->state) + { + PRInt32 bytes, descbytes, filebytes, netbytes; + + (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer)); + TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, + ("\tClient(0x%p): connecting to server at %s\n", me, buffer)); + + fd = PR_Socket(domain, SOCK_STREAM, protocol); + TEST_ASSERT(NULL != fd); + rv = PR_Connect(fd, &client->serverAddress, timeout); + if (PR_FAILURE == rv) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): conection failed (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + + memset(descriptor, 0, sizeof(*descriptor)); + descriptor->size = PR_htonl(descbytes = rand() % clipping); + PR_snprintf( + descriptor->filename, sizeof(descriptor->filename), + "CS%p%p-%p.dat", client->started, me, client->operations); + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes)); + bytes = PR_Send( + fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout); + if (sizeof(CSDescriptor_t) != bytes) + { + if (Aborted(PR_FAILURE)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): send descriptor timeout\n", me)); + goto retry; + } + } + TEST_ASSERT(sizeof(*descriptor) == bytes); + + netbytes = 0; + while (netbytes < descbytes) + { + filebytes = sizeof(buffer); + if ((descbytes - netbytes) < filebytes) { + filebytes = descbytes - netbytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tClient(0x%p): sending %d bytes\n", me, filebytes)); + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); + if (filebytes != bytes) + { + if (Aborted(PR_FAILURE)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): send data timeout\n", me)); + goto retry; + } + } + TEST_ASSERT(bytes == filebytes); + netbytes += bytes; + } + filebytes = 0; + while (filebytes < descbytes) + { + netbytes = sizeof(buffer); + if ((descbytes - filebytes) < netbytes) { + netbytes = descbytes - filebytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tClient(0x%p): receiving %d bytes\n", me, netbytes)); + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); + if (-1 == bytes) + { + if (Aborted(PR_FAILURE)) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): receive data aborted\n", me)); + goto aborted; + } + else if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): receive data timeout\n", me)); + else + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): receive error (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto retry; + } + if (0 == bytes) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tClient(0x%p): unexpected end of stream\n", + PR_GetCurrentThread())); + break; + } + filebytes += bytes; + } + + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); + if (Aborted(rv)) { + goto aborted; + } + TEST_ASSERT(PR_SUCCESS == rv); +retry: + (void)PR_Close(fd); fd = NULL; + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("\tClient(0x%p): disconnected from server\n", me)); + + PR_Lock(client->ml); + client->operations += 1; + client->bytesTransferred += 2 * descbytes; + rv = PR_WaitCondVar(client->stateChange, rand() % clipping); + PR_Unlock(client->ml); + if (Aborted(rv)) { + break; + } + } + +aborted: + client->stopped = PR_IntervalNow(); + + PR_ClearInterrupt(); + if (NULL != fd) { + rv = PR_Close(fd); + } + + PR_Lock(client->ml); + client->state = cs_exit; + PR_NotifyCondVar(client->stateChange); + PR_Unlock(client->ml); + PR_DELETE(descriptor); + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("\tClient(0x%p): stopped after %u operations and %u bytes\n", + PR_GetCurrentThread(), client->operations, client->bytesTransferred)); + +} /* Client */ + +static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server) +{ + PRStatus drv, rv; + char buffer[1024]; + PRFileDesc *file = NULL; + PRThread * me = PR_GetCurrentThread(); + PRInt32 bytes, descbytes, netbytes, filebytes = 0; + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): receiving desciptor\n", me)); + bytes = PR_Recv( + fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout); + if (-1 == bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto exit; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tProcessRequest(0x%p): receive timeout\n", me)); + } + goto exit; + } + if (0 == bytes) + { + rv = PR_FAILURE; + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tProcessRequest(0x%p): unexpected end of file\n", me)); + goto exit; + } + descbytes = PR_ntohl(descriptor->size); + TEST_ASSERT(sizeof(*descriptor) == bytes); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n", + me, descbytes, descriptor->filename)); + + file = PR_Open( + descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666); + if (NULL == file) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tProcessRequest(0x%p): open file timeout\n", me)); + goto aborted; + } + } + TEST_ASSERT(NULL != file); + + filebytes = 0; + while (filebytes < descbytes) + { + netbytes = sizeof(buffer); + if ((descbytes - filebytes) < netbytes) { + netbytes = descbytes - filebytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes)); + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); + if (-1 == bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): receive data timeout\n", me)); + goto aborted; + } + /* + * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED) + * on NT here. This is equivalent to ECONNRESET on Unix. + * -wtc + */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_WARNING, + ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + if(0 == bytes) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_WARNING, + ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me)); + rv = PR_FAILURE; + goto aborted; + } + filebytes += bytes; + netbytes = bytes; + /* The byte count for PR_Write should be positive */ + MY_ASSERT(netbytes > 0); + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes)); + bytes = PR_Write(file, buffer, netbytes); + if (netbytes != bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): write file timeout\n", me)); + goto aborted; + } + } + TEST_ASSERT(bytes > 0); + } + + PR_Lock(server->ml); + server->operations += 1; + server->bytesTransferred += filebytes; + PR_Unlock(server->ml); + + rv = PR_Close(file); + if (Aborted(rv)) { + goto aborted; + } + TEST_ASSERT(PR_SUCCESS == rv); + file = NULL; + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename)); + file = PR_Open(descriptor->filename, PR_RDONLY, 0); + if (NULL == file) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): open file timeout\n", + PR_GetCurrentThread())); + goto aborted; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + TEST_ASSERT(NULL != file); + + netbytes = 0; + while (netbytes < descbytes) + { + filebytes = sizeof(buffer); + if ((descbytes - netbytes) < filebytes) { + filebytes = descbytes - netbytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes)); + bytes = PR_Read(file, buffer, filebytes); + if (filebytes != bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): read file timeout\n", me)); + else + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + TEST_ASSERT(bytes > 0); + netbytes += bytes; + filebytes = bytes; + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes)); + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); + if (filebytes != bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): send data timeout\n", me)); + goto aborted; + } + break; + } + TEST_ASSERT(bytes > 0); + } + + PR_Lock(server->ml); + server->bytesTransferred += filebytes; + PR_Unlock(server->ml); + + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); + if (Aborted(rv)) { + goto aborted; + } + + rv = PR_Close(file); + if (Aborted(rv)) { + goto aborted; + } + TEST_ASSERT(PR_SUCCESS == rv); + file = NULL; + +aborted: + PR_ClearInterrupt(); + if (NULL != file) { + PR_Close(file); + } + drv = PR_Delete(descriptor->filename); + TEST_ASSERT(PR_SUCCESS == drv); +exit: + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): Finished\n", me)); + + PR_DELETE(descriptor); + +#if defined(WIN95) + PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */ +#endif + return rv; +} /* ProcessRequest */ + +static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool) +{ + CSWorker_t *worker = PR_NEWZAP(CSWorker_t); + worker->server = server; + PR_INIT_CLIST(&worker->element); + worker->thread = PR_CreateThread( + PR_USER_THREAD, Worker, worker, + DEFAULT_SERVER_PRIORITY, thread_scope, + PR_UNJOINABLE_THREAD, 0); + if (NULL == worker->thread) + { + PR_DELETE(worker); + return PR_FAILURE; + } + + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, + ("\tCreateWorker(0x%p): create new worker (0x%p)\n", + PR_GetCurrentThread(), worker->thread)); + + return PR_SUCCESS; +} /* CreateWorker */ + +static void PR_CALLBACK Worker(void *arg) +{ + PRStatus rv; + PRNetAddr from; + PRFileDesc *fd = NULL; + PRThread *me = PR_GetCurrentThread(); + CSWorker_t *worker = (CSWorker_t*)arg; + CSServer_t *server = worker->server; + CSPool_t *pool = &server->pool; + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1)); + + PR_Lock(server->ml); + PR_APPEND_LINK(&worker->element, &server->list); + pool->workers += 1; /* define our existance */ + + while (cs_run == server->state) + { + while (pool->accepting >= server->workers.accepting) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", + me, pool->accepting)); + rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT); + if (Aborted(rv) || (cs_run != server->state)) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\tWorker(0x%p): has been %s\n", + me, (Aborted(rv) ? "interrupted" : "stopped"))); + goto exit; + } + } + pool->accepting += 1; /* how many are really in accept */ + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tWorker(0x%p): calling accept\n", me)); + fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT); + + PR_Lock(server->ml); + pool->accepting -= 1; + PR_NotifyCondVar(pool->acceptComplete); + + if ((NULL == fd) && Aborted(PR_FAILURE)) + { + if (NULL != server->listener) + { + PR_Close(server->listener); + server->listener = NULL; + } + goto exit; + } + + if (NULL != fd) + { + /* + ** Create another worker of the total number of workers is + ** less than the minimum specified or we have none left in + ** accept() AND we're not over the maximum. + ** This sort of presumes that the number allowed in accept + ** is at least as many as the minimum. Otherwise we'll keep + ** creating new threads and deleting them soon after. + */ + PRBool another = + ((pool->workers < server->workers.minimum) || + ((0 == pool->accepting) + && (pool->workers < server->workers.maximum))) ? + PR_TRUE : PR_FALSE; + pool->active += 1; + PR_Unlock(server->ml); + + if (another) { + (void)CreateWorker(server, pool); + } + + rv = ProcessRequest(fd, server); + if (PR_SUCCESS != rv) + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tWorker(0x%p): server process ended abnormally\n", me)); + (void)PR_Close(fd); fd = NULL; + + PR_Lock(server->ml); + pool->active -= 1; + } + } + +exit: + PR_ClearInterrupt(); + PR_Unlock(server->ml); + + if (NULL != fd) + { + (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH); + (void)PR_Close(fd); + } + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers)); + + PR_Lock(server->ml); + pool->workers -= 1; /* undefine our existance */ + PR_REMOVE_AND_INIT_LINK(&worker->element); + PR_NotifyCondVar(pool->exiting); + PR_Unlock(server->ml); + + PR_DELETE(worker); /* destruction of the "worker" object */ + +} /* Worker */ + +static void PR_CALLBACK Server(void *arg) +{ + PRStatus rv; + PRNetAddr serverAddress; + PRThread *me = PR_GetCurrentThread(); + CSServer_t *server = (CSServer_t*)arg; + PRSocketOptionData sockOpt; + + server->listener = PR_Socket(domain, SOCK_STREAM, protocol); + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + rv = PR_SetSocketOption(server->listener, &sockOpt); + TEST_ASSERT(PR_SUCCESS == rv); + + memset(&serverAddress, 0, sizeof(serverAddress)); + if (PR_AF_INET6 != domain) { + TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS, + ("server binding to ip port %s\n", DEFAULT_PORT)); + rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress); + } + else { + TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS, + ("server binding to ipv6 port %s\n", DEFAULT_PORT)); + rv = PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, DEFAULT_PORT, + &serverAddress); + } + rv = PR_Bind(server->listener, &serverAddress); + TEST_ASSERT(PR_SUCCESS == rv); + + rv = PR_Listen(server->listener, server->backlog); + TEST_ASSERT(PR_SUCCESS == rv); + + server->started = PR_IntervalNow(); + TimeOfDayMessage("Server started at", me); + + PR_Lock(server->ml); + server->state = cs_run; + PR_NotifyCondVar(server->stateChange); + PR_Unlock(server->ml); + + /* + ** Create the first worker (actually, a thread that accepts + ** connections and then processes the work load as needed). + ** From this point on, additional worker threads are created + ** as they are needed by existing worker threads. + */ + rv = CreateWorker(server, &server->pool); + TEST_ASSERT(PR_SUCCESS == rv); + + /* + ** From here on this thread is merely hanging around as the contact + ** point for the main test driver. It's just waiting for the driver + ** to declare the test complete. + */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tServer(0x%p): waiting for state change\n", me)); + + PR_Lock(server->ml); + while ((cs_run == server->state) && !Aborted(rv)) + { + rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(server->ml); + PR_ClearInterrupt(); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("\tServer(0x%p): shutting down workers\n", me)); + + /* + ** Get all the worker threads to exit. They know how to + ** clean up after themselves, so this is just a matter of + ** waiting for clorine in the pool to take effect. During + ** this stage we're ignoring interrupts. + */ + server->workers.minimum = server->workers.maximum = 0; + + PR_Lock(server->ml); + while (!PR_CLIST_IS_EMPTY(&server->list)) + { + PRCList *head = PR_LIST_HEAD(&server->list); + CSWorker_t *worker = (CSWorker_t*)head; + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker)); + rv = PR_Interrupt(worker->thread); + TEST_ASSERT(PR_SUCCESS == rv); + PR_REMOVE_AND_INIT_LINK(head); + } + + while (server->pool.workers > 0) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\tServer(0x%p): waiting for %u workers to exit\n", + me, server->pool.workers)); + (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT); + } + + server->state = cs_exit; + PR_NotifyCondVar(server->stateChange); + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("\tServer(0x%p): stopped after %u operations and %u bytes\n", + me, server->operations, server->bytesTransferred)); + + if (NULL != server->listener) { + PR_Close(server->listener); + } + server->stopped = PR_IntervalNow(); + +} /* Server */ + +static void WaitForCompletion(PRIntn execution) +{ + while (execution > 0) + { + PRIntn dally = (execution > 30) ? 30 : execution; + PR_Sleep(PR_SecondsToInterval(dally)); + if (pthread_stats) { + PT_FPrintStats(debug_out, "\nPThread Statistics\n"); + } + execution -= dally; + } +} /* WaitForCompletion */ + +static void Help(void) +{ + PR_fprintf(debug_out, "cltsrv test program usage:\n"); + PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n"); + PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n"); + PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n"); + PR_fprintf(debug_out, "\t-f <low> low water mark for fd caching (0)\n"); + PR_fprintf(debug_out, "\t-F <high> high water mark for fd caching (0)\n"); + PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n"); + PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n"); + PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n"); + PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n"); + PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n"); + PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n"); + PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n"); + PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n"); + PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n"); + PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n"); + PR_fprintf(debug_out, "\t-h this message\n"); +} /* Help */ + +static Verbosity IncrementVerbosity(void) +{ + PRIntn verboge = (PRIntn)verbosity + 1; + return (Verbosity)verboge; +} /* IncrementVerbosity */ + +int main(int argc, char** argv) +{ + PRUintn index; + PRBool boolean; + CSClient_t *client; + PRStatus rv, joinStatus; + CSServer_t *server = NULL; + + PRUintn backlog = DEFAULT_BACKLOG; + PRUintn clients = DEFAULT_CLIENTS; + const char *serverName = DEFAULT_SERVER; + PRBool serverIsLocal = PR_TRUE; + PRUintn accepting = ALLOWED_IN_ACCEPT; + PRUintn workersMin = DEFAULT_WORKERS_MIN; + PRUintn workersMax = DEFAULT_WORKERS_MAX; + PRIntn execution = DEFAULT_EXECUTION_TIME; + PRIntn low = DEFAULT_LOW, high = DEFAULT_HIGH; + + /* + * -G use global threads + * -a <n> threads allowed in accept + * -b <n> backlock for listen + * -c <threads> number of clients to create + * -f <low> low water mark for caching FDs + * -F <high> high water mark for caching FDs + * -w <threads> minimal number of server threads + * -W <threads> maximum number of server threads + * -e <seconds> duration of the test in seconds + * -s <string> dsn name of server (implies no server here) + * -v verbosity + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:f:F:w:W:e:s:vdhp"); + + debug_out = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'X': /* use XTP as transport */ + protocol = 36; + break; + case '6': /* Use IPv6 */ + domain = PR_AF_INET6; + break; + case 'a': /* the value for accepting */ + accepting = atoi(opt->value); + break; + case 'b': /* the value for backlock */ + backlog = atoi(opt->value); + break; + case 'c': /* number of client threads */ + clients = atoi(opt->value); + break; + case 'f': /* low water fd cache */ + low = atoi(opt->value); + break; + case 'F': /* low water fd cache */ + high = atoi(opt->value); + break; + case 'w': /* minimum server worker threads */ + workersMin = atoi(opt->value); + break; + case 'W': /* maximum server worker threads */ + workersMax = atoi(opt->value); + break; + case 'e': /* program execution time in seconds */ + execution = atoi(opt->value); + break; + case 's': /* server's address */ + serverName = opt->value; + break; + case 'v': /* verbosity */ + verbosity = IncrementVerbosity(); + break; + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'p': /* pthread mode */ + pthread_stats = PR_TRUE; + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) { + serverIsLocal = PR_FALSE; + } + if (0 == execution) { + execution = DEFAULT_EXECUTION_TIME; + } + if (0 == workersMax) { + workersMax = DEFAULT_WORKERS_MAX; + } + if (0 == workersMin) { + workersMin = DEFAULT_WORKERS_MIN; + } + if (0 == accepting) { + accepting = ALLOWED_IN_ACCEPT; + } + if (0 == backlog) { + backlog = DEFAULT_BACKLOG; + } + + if (workersMin > accepting) { + accepting = workersMin; + } + + PR_STDIO_INIT(); + TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread()); + + cltsrv_log_file = PR_NewLogModule("cltsrv_log"); + MY_ASSERT(NULL != cltsrv_log_file); + boolean = PR_SetLogFile("cltsrv.log"); + MY_ASSERT(boolean); + + rv = PR_SetFDCacheSize(low, high); + PR_ASSERT(PR_SUCCESS == rv); + + if (serverIsLocal) + { + /* Establish the server */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("main(0x%p): starting server\n", PR_GetCurrentThread())); + + server = PR_NEWZAP(CSServer_t); + PR_INIT_CLIST(&server->list); + server->state = cs_init; + server->ml = PR_NewLock(); + server->backlog = backlog; + server->port = DEFAULT_PORT; + server->workers.minimum = workersMin; + server->workers.maximum = workersMax; + server->workers.accepting = accepting; + server->stateChange = PR_NewCondVar(server->ml); + server->pool.exiting = PR_NewCondVar(server->ml); + server->pool.acceptComplete = PR_NewCondVar(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("main(0x%p): creating server thread\n", PR_GetCurrentThread())); + + server->thread = PR_CreateThread( + PR_USER_THREAD, Server, server, PR_PRIORITY_HIGH, + thread_scope, PR_JOINABLE_THREAD, 0); + TEST_ASSERT(NULL != server->thread); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): waiting for server init\n", PR_GetCurrentThread())); + + PR_Lock(server->ml); + while (server->state == cs_init) { + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): server init complete (port #%d)\n", + PR_GetCurrentThread(), server->port)); + } + + if (clients != 0) + { + /* Create all of the clients */ + PRHostEnt host; + char buffer[BUFFER_SIZE]; + client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t)); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): creating %d client threads\n", + PR_GetCurrentThread(), clients)); + + if (!serverIsLocal) + { + rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host); + if (PR_SUCCESS != rv) + { + PL_FPrintError(PR_STDERR, "PR_GetHostByName"); + return 2; + } + } + + for (index = 0; index < clients; ++index) + { + client[index].state = cs_init; + client[index].ml = PR_NewLock(); + if (serverIsLocal) + { + if (PR_AF_INET6 != domain) { + TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS, + ("loopback client ip port %s\n", DEFAULT_PORT)); + (void)PR_InitializeNetAddr( + PR_IpAddrLoopback, DEFAULT_PORT, + &client[index].serverAddress); + } + else { + TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS, + ("loopback client ipv6 port %s\n", DEFAULT_PORT)); + rv = PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, + DEFAULT_PORT, &client[index].serverAddress); + } + } + else + { + TEST_LOG(cltsrv_log_file, TEST_LOG_ALWAYS, + ("client enumerate port %s\n", DEFAULT_PORT)); + (void)PR_EnumerateHostEnt( + 0, &host, DEFAULT_PORT, &client[index].serverAddress); + } + client[index].stateChange = PR_NewCondVar(client[index].ml); + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("main(0x%p): creating client threads\n", PR_GetCurrentThread())); + client[index].thread = PR_CreateThread( + PR_USER_THREAD, Client, &client[index], PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + TEST_ASSERT(NULL != client[index].thread); + PR_Lock(client[index].ml); + while (cs_init == client[index].state) { + PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(client[index].ml); + } + } + + /* Then just let them go at it for a bit */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("main(0x%p): waiting for execution interval (%d seconds)\n", + PR_GetCurrentThread(), execution)); + + WaitForCompletion(execution); + + TimeOfDayMessage("Shutting down", PR_GetCurrentThread()); + + if (clients != 0) + { + for (index = 0; index < clients; ++index) + { + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, + ("main(0x%p): notifying client(0x%p) to stop\n", + PR_GetCurrentThread(), client[index].thread)); + + PR_Lock(client[index].ml); + if (cs_run == client[index].state) + { + client[index].state = cs_stop; + PR_Interrupt(client[index].thread); + while (cs_stop == client[index].state) + PR_WaitCondVar( + client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(client[index].ml); + + TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): joining client(0x%p)\n", + PR_GetCurrentThread(), client[index].thread)); + + joinStatus = PR_JoinThread(client[index].thread); + TEST_ASSERT(PR_SUCCESS == joinStatus); + PR_DestroyCondVar(client[index].stateChange); + PR_DestroyLock(client[index].ml); + } + PR_DELETE(client); + } + + if (NULL != server) + { + /* All clients joined - retrieve the server */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("main(0x%p): notifying server(0x%p) to stop\n", + PR_GetCurrentThread(), server->thread)); + + PR_Lock(server->ml); + server->state = cs_stop; + PR_Interrupt(server->thread); + while (cs_exit != server->state) { + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("main(0x%p): joining server(0x%p)\n", + PR_GetCurrentThread(), server->thread)); + joinStatus = PR_JoinThread(server->thread); + TEST_ASSERT(PR_SUCCESS == joinStatus); + + PR_DestroyCondVar(server->stateChange); + PR_DestroyCondVar(server->pool.exiting); + PR_DestroyCondVar(server->pool.acceptComplete); + PR_DestroyLock(server->ml); + PR_DELETE(server); + } + + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("main(0x%p): test complete\n", PR_GetCurrentThread())); + + PT_FPrintStats(debug_out, "\nPThread Statistics\n"); + + TimeOfDayMessage("Test exiting at", PR_GetCurrentThread()); + PR_Cleanup(); + return 0; +} /* main */ + +/* cltsrv.c */ diff --git a/nsprpub/pr/tests/concur.c b/nsprpub/pr/tests/concur.c new file mode 100644 index 0000000000..5940290213 --- /dev/null +++ b/nsprpub/pr/tests/concur.c @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: concur.c +** Description: test of adding and removing concurrency options +*/ + +#include "prcvar.h" +#include "prinit.h" +#include "prinrval.h" +#include "prlock.h" +#include "prprf.h" +#include "prmem.h" +#include "prlog.h" + +#include "plgetopt.h" + +#include "private/pprio.h" + +#include <stdlib.h> + +#define DEFAULT_RANGE 10 +#define DEFAULT_LOOPS 100 + +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +typedef struct Context +{ + PRLock *ml; + PRCondVar *cv; + PRIntn want, have; +} Context; + + +/* +** Make the instance of 'context' static (not on the stack) +** for Win16 threads +*/ +static Context context = {NULL, NULL, 0, 0}; + +static void PR_CALLBACK Dull(void *arg) +{ + Context *context = (Context*)arg; + PR_Lock(context->ml); + context->have += 1; + while (context->want >= context->have) { + PR_WaitCondVar(context->cv, PR_INTERVAL_NO_TIMEOUT); + } + context->have -= 1; + PR_Unlock(context->ml); +} /* Dull */ + +PRIntn PR_CALLBACK Concur(PRIntn argc, char **argv) +{ + PRUintn cpus; + PLOptStatus os; + PRThread **threads; + PRBool debug = PR_FALSE; + PRUintn range = DEFAULT_RANGE; + PRStatus rc; + PRUintn cnt; + PRUintn loops = DEFAULT_LOOPS; + PRIntervalTime hundredMills = PR_MillisecondsToInterval(100); + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:r:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'G': /* GLOBAL threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'd': /* debug mode */ + debug = PR_TRUE; + break; + case 'r': /* range limit */ + range = atoi(opt->value); + break; + case 'l': /* loop counter */ + loops = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (0 == range) { + range = DEFAULT_RANGE; + } + if (0 == loops) { + loops = DEFAULT_LOOPS; + } + + context.ml = PR_NewLock(); + context.cv = PR_NewCondVar(context.ml); + + if (debug) + PR_fprintf( + PR_STDERR, "Testing with %d CPUs and %d interations\n", range, loops); + + threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * range); + while (--loops > 0) + { + for (cpus = 1; cpus <= range; ++cpus) + { + PR_SetConcurrency(cpus); + context.want = cpus; + + threads[cpus - 1] = PR_CreateThread( + PR_USER_THREAD, Dull, &context, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + } + + PR_Sleep(hundredMills); + + for (cpus = range; cpus > 0; cpus--) + { + PR_SetConcurrency(cpus); + context.want = cpus - 1; + + PR_Lock(context.ml); + PR_NotifyCondVar(context.cv); + PR_Unlock(context.ml); + } + for(cnt = 0; cnt < range; cnt++) { + rc = PR_JoinThread(threads[cnt]); + PR_ASSERT(rc == PR_SUCCESS); + } + } + + + if (debug) + PR_fprintf( + PR_STDERR, "Waiting for %d thread(s) to exit\n", context.have); + + while (context.have > 0) { + PR_Sleep(hundredMills); + } + + if (debug) + PR_fprintf( + PR_STDERR, "Finished [want: %d, have: %d]\n", + context.want, context.have); + + PR_DestroyLock(context.ml); + PR_DestroyCondVar(context.cv); + PR_DELETE(threads); + + PR_fprintf(PR_STDERR, "PASSED\n"); + + return 0; +} /* Concur */ + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + return PR_Initialize(Concur, argc, argv, 0); +} /* main */ + +/* concur.c */ diff --git a/nsprpub/pr/tests/cvar.c b/nsprpub/pr/tests/cvar.c new file mode 100644 index 0000000000..4dc891d3f9 --- /dev/null +++ b/nsprpub/pr/tests/cvar.c @@ -0,0 +1,309 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1996 - Netscape Communications Corporation +** +** Name: cvar.c +** +** Description: Tests Condition Variable Operations +** +** Modification History: +** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 Revert to return code 0 and 1. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ + +#include "nspr.h" + +/* Used to get the command line option */ +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRMonitor *mon; +#define DEFAULT_COUNT 1000 +PRInt32 count = 0; +PRIntn debug_mode; + +#define kQSIZE 1 + +typedef struct { + PRLock *bufLock; + int startIdx; + int numFull; + PRCondVar *notFull; + PRCondVar *notEmpty; + void *data[kQSIZE]; +} CircBuf; + +static PRBool failed = PR_FALSE; + +/* +** NewCB creates and initializes a new circular buffer. +*/ +static CircBuf* NewCB(void) +{ + CircBuf *cbp; + + cbp = PR_NEW(CircBuf); + if (cbp == NULL) { + return (NULL); + } + + cbp->bufLock = PR_NewLock(); + cbp->startIdx = 0; + cbp->numFull = 0; + cbp->notFull = PR_NewCondVar(cbp->bufLock); + cbp->notEmpty = PR_NewCondVar(cbp->bufLock); + + return (cbp); +} + +/* +** DeleteCB frees a circular buffer. +*/ +static void DeleteCB(CircBuf *cbp) +{ + PR_DestroyLock(cbp->bufLock); + PR_DestroyCondVar(cbp->notFull); + PR_DestroyCondVar(cbp->notEmpty); + PR_DELETE(cbp); +} + + +/* +** PutCBData puts new data on the queue. If the queue is full, it waits +** until there is room. +*/ +static void PutCBData(CircBuf *cbp, void *data) +{ + PR_Lock(cbp->bufLock); + /* wait while the buffer is full */ + while (cbp->numFull == kQSIZE) { + PR_WaitCondVar(cbp->notFull,PR_INTERVAL_NO_TIMEOUT); + } + cbp->data[(cbp->startIdx + cbp->numFull) % kQSIZE] = data; + cbp->numFull += 1; + + /* let a waiting reader know that there is data */ + PR_NotifyCondVar(cbp->notEmpty); + PR_Unlock(cbp->bufLock); + +} + + +/* +** GetCBData gets the oldest data on the queue. If the queue is empty, it waits +** until new data appears. +*/ +static void* GetCBData(CircBuf *cbp) +{ + void *data; + + PR_Lock(cbp->bufLock); + /* wait while the buffer is empty */ + while (cbp->numFull == 0) { + PR_WaitCondVar(cbp->notEmpty,PR_INTERVAL_NO_TIMEOUT); + } + data = cbp->data[cbp->startIdx]; + cbp->startIdx =(cbp->startIdx + 1) % kQSIZE; + cbp->numFull -= 1; + + /* let a waiting writer know that there is room */ + PR_NotifyCondVar(cbp->notFull); + PR_Unlock(cbp->bufLock); + + return (data); +} + + +/************************************************************************/ + +static int alive; + +static void PR_CALLBACK CXReader(void *arg) +{ + CircBuf *cbp = (CircBuf *)arg; + PRInt32 i, n; + void *data; + + n = count / 2; + for (i = 0; i < n; i++) { + data = GetCBData(cbp); + if ((int)data != i) + if (debug_mode) { + printf("data mismatch at for i = %d usec\n", i); + } + } + + PR_EnterMonitor(mon); + --alive; + PR_Notify(mon); + PR_ExitMonitor(mon); +} + +static void PR_CALLBACK CXWriter(void *arg) +{ + CircBuf *cbp = (CircBuf *)arg; + PRInt32 i, n; + + n = count / 2; + for (i = 0; i < n; i++) { + PutCBData(cbp, (void *)i); + } + + PR_EnterMonitor(mon); + --alive; + PR_Notify(mon); + PR_ExitMonitor(mon); +} + +static void CondWaitContextSwitch(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *t1, *t2; + CircBuf *cbp; + + PR_EnterMonitor(mon); + + alive = 2; + + cbp = NewCB(); + + t1 = PR_CreateThread(PR_USER_THREAD, + CXReader, cbp, + PR_PRIORITY_NORMAL, + scope1, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t1); + t2 = PR_CreateThread(PR_USER_THREAD, + CXWriter, cbp, + PR_PRIORITY_NORMAL, + scope2, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t2); + + /* Wait for both of the threads to exit */ + while (alive) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + + DeleteCB(cbp); + + PR_ExitMonitor(mon); +} + +static void CondWaitContextSwitchUU(void) +{ + CondWaitContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD); +} + +static void CondWaitContextSwitchUK(void) +{ + CondWaitContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); +} + +static void CondWaitContextSwitchKK(void) +{ + CondWaitContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); +} + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + if (debug_mode) { + printf("%40s: %6.2f usec\n", msg, d / count); + } + + if (0 == d) { + failed = PR_TRUE; + } +} + +static PRIntn PR_CALLBACK RealMain(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name [-d] [-c n] + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'c': /* loop count */ + count = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (0 == count) { + count = DEFAULT_COUNT; + } + + mon = PR_NewMonitor(); + + Measure(CondWaitContextSwitchUU, "cond var wait context switch- user/user"); + Measure(CondWaitContextSwitchUK, "cond var wait context switch- user/kernel"); + Measure(CondWaitContextSwitchKK, "cond var wait context switch- kernel/kernel"); + + PR_DestroyMonitor(mon); + + if (debug_mode) { + printf("%s\n", (failed) ? "FAILED" : "PASSED"); + } + + if(failed) { + return 1; + } + else { + return 0; + } +} + + +int main(int argc, char *argv[]) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/cvar2.c b/nsprpub/pr/tests/cvar2.c new file mode 100644 index 0000000000..c9f57d5cc6 --- /dev/null +++ b/nsprpub/pr/tests/cvar2.c @@ -0,0 +1,976 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1996 - Netscape Communications Corporation +** +** Name: cvar2.c +** +** Description: Simple test creates several local and global threads; +** half use a single,shared condvar, and the +** other half have their own condvar. The main thread then loops +** notifying them to wakeup. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ + +#include "nspr.h" +#include "plerror.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int _debug_on = 0; +#define DPRINTF(arg) if (_debug_on) printf arg + +#define DEFAULT_COUNT 100 +#define DEFAULT_THREADS 5 +PRInt32 count = DEFAULT_COUNT; + +typedef struct threadinfo { + PRThread *thread; + PRInt32 id; + PRBool internal; + PRInt32 *tcount; + PRLock *lock; + PRCondVar *cvar; + PRIntervalTime timeout; + PRInt32 loops; + + PRLock *exitlock; + PRCondVar *exitcvar; + PRInt32 *exitcount; +} threadinfo; + +/* +** Make exitcount, tcount static. for Win16. +*/ +static PRInt32 exitcount=0; +static PRInt32 tcount=0; + + +/* Thread that gets notified; many threads share the same condvar */ +void PR_CALLBACK +SharedCondVarThread(void *_info) +{ + threadinfo *info = (threadinfo *)_info; + PRInt32 index; + + for (index=0; index<info->loops; index++) { + PR_Lock(info->lock); + if (*info->tcount == 0) { + PR_WaitCondVar(info->cvar, info->timeout); + } +#if 0 + printf("shared thread %ld notified in loop %ld\n", info->id, index); +#endif + (*info->tcount)--; + PR_Unlock(info->lock); + + PR_Lock(info->exitlock); + (*info->exitcount)++; + PR_NotifyCondVar(info->exitcvar); + PR_Unlock(info->exitlock); + } +#if 0 + printf("shared thread %ld terminating\n", info->id); +#endif +} + +/* Thread that gets notified; no other threads use the same condvar */ +void PR_CALLBACK +PrivateCondVarThread(void *_info) +{ + threadinfo *info = (threadinfo *)_info; + PRInt32 index; + + for (index=0; index<info->loops; index++) { + PR_Lock(info->lock); + if (*info->tcount == 0) { + DPRINTF(("PrivateCondVarThread: thread 0x%lx waiting on cvar = 0x%lx\n", + PR_GetCurrentThread(), info->cvar)); + PR_WaitCondVar(info->cvar, info->timeout); + } +#if 0 + printf("solo thread %ld notified in loop %ld\n", info->id, index); +#endif + (*info->tcount)--; + PR_Unlock(info->lock); + + PR_Lock(info->exitlock); + (*info->exitcount)++; + PR_NotifyCondVar(info->exitcvar); + DPRINTF(("PrivateCondVarThread: thread 0x%lx notified exitcvar = 0x%lx cnt = %ld\n", + PR_GetCurrentThread(), info->exitcvar,(*info->exitcount))); + PR_Unlock(info->exitlock); + } +#if 0 + printf("solo thread %ld terminating\n", info->id); +#endif +} + +void +CreateTestThread(threadinfo *info, + PRInt32 id, + PRLock *lock, + PRCondVar *cvar, + PRInt32 loops, + PRIntervalTime timeout, + PRInt32 *tcount, + PRLock *exitlock, + PRCondVar *exitcvar, + PRInt32 *exitcount, + PRBool shared, + PRThreadScope scope) +{ + info->id = id; + info->internal = (shared) ? PR_FALSE : PR_TRUE; + info->lock = lock; + info->cvar = cvar; + info->loops = loops; + info->timeout = timeout; + info->tcount = tcount; + info->exitlock = exitlock; + info->exitcvar = exitcvar; + info->exitcount = exitcount; + info->thread = PR_CreateThread( + PR_USER_THREAD, + shared?SharedCondVarThread:PrivateCondVarThread, + info, + PR_PRIORITY_NORMAL, + scope, + PR_JOINABLE_THREAD, + 0); + if (!info->thread) { + PL_PrintError("error creating thread\n"); + } +} + + +void +CondVarTestSUU(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + + exitcount=0; + tcount=0; + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg; ) { + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_INTERVAL_NO_TIMEOUT, + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_LOCAL_THREAD); + index++; + DPRINTF(("CondVarTestSUU: created thread 0x%lx\n",list[index].thread)); + } + + for (loops = 0; loops < count; loops++) { + /* Notify the threads */ + for(index=0; index<(arg); index++) { + PR_Lock(list[index].lock); + (*list[index].tcount)++; + PR_NotifyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + DPRINTF(("PrivateCondVarThread: thread 0x%lx notified cvar = 0x%lx\n", + PR_GetCurrentThread(), list[index].cvar)); + } + + /* Wait for threads to finish */ + PR_Lock(exitlock); + while(exitcount < arg) { + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg); + exitcount -= arg; + PR_Unlock(exitlock); + } + + /* Join all the threads */ + for(index=0; index<(arg); index++) { + PR_JoinThread(list[index].thread); + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + PR_DestroyCondVar(exitcvar); + PR_DestroyLock(exitlock); + + PR_DELETE(list); +} + +void +CondVarTestSUK(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + exitcount=0; + tcount=0; + + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg; ) { + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_INTERVAL_NO_TIMEOUT, + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_GLOBAL_THREAD); + index++; + } + + for (loops = 0; loops < count; loops++) { + /* Notify the threads */ + for(index=0; index<(arg); index++) { + + PR_Lock(list[index].lock); + (*list[index].tcount)++; + PR_NotifyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + } + +#if 0 + printf("wait for threads to be done\n"); +#endif + /* Wait for threads to finish */ + PR_Lock(exitlock); + while(exitcount < arg) { + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg); + exitcount -= arg; + PR_Unlock(exitlock); +#if 0 + printf("threads ready\n"); +#endif + } + + /* Join all the threads */ + for(index=0; index<(arg); index++) { + PR_JoinThread(list[index].thread); + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + PR_DestroyCondVar(exitcvar); + PR_DestroyLock(exitlock); + + PR_DELETE(list); +} + +void +CondVarTestPUU(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + PRInt32 *tcount, *saved_tcount; + + exitcount=0; + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg; ) { + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_INTERVAL_NO_TIMEOUT, + tcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_LOCAL_THREAD); + + DPRINTF(("CondVarTestPUU: created thread 0x%lx\n",list[index].thread)); + index++; + tcount++; + } + + for (loops = 0; loops < count; loops++) { + /* Notify the threads */ + for(index=0; index<(arg); index++) { + + PR_Lock(list[index].lock); + (*list[index].tcount)++; + PR_NotifyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + } + + PR_Lock(exitlock); + /* Wait for threads to finish */ + while(exitcount < arg) { + DPRINTF(("CondVarTestPUU: thread 0x%lx waiting on exitcvar = 0x%lx cnt = %ld\n", + PR_GetCurrentThread(), exitcvar, exitcount)); + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg); + exitcount -= arg; + PR_Unlock(exitlock); + } + + /* Join all the threads */ + for(index=0; index<(arg); index++) { + DPRINTF(("CondVarTestPUU: joining thread 0x%lx\n",list[index].thread)); + PR_JoinThread(list[index].thread); + if (list[index].internal) { + PR_Lock(list[index].lock); + PR_DestroyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + PR_DestroyLock(list[index].lock); + } + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + PR_DestroyCondVar(exitcvar); + PR_DestroyLock(exitlock); + + PR_DELETE(list); + PR_DELETE(saved_tcount); +} + +void +CondVarTestPUK(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + PRInt32 *tcount, *saved_tcount; + + exitcount=0; + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + saved_tcount = tcount = (PRInt32 *)PR_CALLOC(sizeof(*tcount) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg; ) { + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_INTERVAL_NO_TIMEOUT, + tcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_GLOBAL_THREAD); + + index++; + tcount++; + } + + for (loops = 0; loops < count; loops++) { + /* Notify the threads */ + for(index=0; index<(arg); index++) { + + PR_Lock(list[index].lock); + (*list[index].tcount)++; + PR_NotifyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + } + + /* Wait for threads to finish */ + PR_Lock(exitlock); + while(exitcount < arg) { + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg); + exitcount -= arg; + PR_Unlock(exitlock); + } + + /* Join all the threads */ + for(index=0; index<(arg); index++) { + PR_JoinThread(list[index].thread); + if (list[index].internal) { + PR_Lock(list[index].lock); + PR_DestroyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + PR_DestroyLock(list[index].lock); + } + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + PR_DestroyCondVar(exitcvar); + PR_DestroyLock(exitlock); + + PR_DELETE(list); + PR_DELETE(saved_tcount); +} + +void +CondVarTest(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + PRInt32 *ptcount, *saved_ptcount; + + exitcount=0; + tcount=0; + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + saved_ptcount = ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg*4; ) { + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_INTERVAL_NO_TIMEOUT, + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_LOCAL_THREAD); + + index++; + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_INTERVAL_NO_TIMEOUT, + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_GLOBAL_THREAD); + + index++; + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_INTERVAL_NO_TIMEOUT, + ptcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_LOCAL_THREAD); + index++; + ptcount++; + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_INTERVAL_NO_TIMEOUT, + ptcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_GLOBAL_THREAD); + + index++; + ptcount++; + } + + for (loops = 0; loops < count; loops++) { + + /* Notify the threads */ + for(index=0; index<(arg*4); index++) { + PR_Lock(list[index].lock); + (*list[index].tcount)++; + PR_NotifyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + } + +#if 0 + printf("wait for threads done\n"); +#endif + + /* Wait for threads to finish */ + PR_Lock(exitlock); + while(exitcount < arg*4) { + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg*4); + exitcount -= arg*4; + PR_Unlock(exitlock); +#if 0 + printf("threads ready\n"); +#endif + } + + /* Join all the threads */ + for(index=0; index<(arg*4); index++) { + PR_JoinThread(list[index].thread); + if (list[index].internal) { + PR_Lock(list[index].lock); + PR_DestroyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + PR_DestroyLock(list[index].lock); + } + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + PR_DestroyCondVar(exitcvar); + PR_DestroyLock(exitlock); + + PR_DELETE(list); + PR_DELETE(saved_ptcount); +} + +void +CondVarTimeoutTest(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg*4; ) { + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_MillisecondsToInterval(50), + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_LOCAL_THREAD); + index++; + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_MillisecondsToInterval(50), + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_GLOBAL_THREAD); + index++; + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_MillisecondsToInterval(50), + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_LOCAL_THREAD); + index++; + + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_MillisecondsToInterval(50), + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_GLOBAL_THREAD); + + index++; + } + + for (loops = 0; loops < count; loops++) { + + /* Wait for threads to finish */ + PR_Lock(exitlock); + while(exitcount < arg*4) { + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg*4); + exitcount -= arg*4; + PR_Unlock(exitlock); + } + + + /* Join all the threads */ + for(index=0; index<(arg*4); index++) { + PR_JoinThread(list[index].thread); + if (list[index].internal) { + PR_Lock(list[index].lock); + PR_DestroyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + PR_DestroyLock(list[index].lock); + } + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + PR_DestroyCondVar(exitcvar); + PR_DestroyLock(exitlock); + + PR_DELETE(list); +} + +void +CondVarMixedTest(void *_arg) +{ + PRInt32 arg = (PRInt32)_arg; + PRInt32 index, loops; + threadinfo *list; + PRLock *sharedlock; + PRCondVar *sharedcvar; + PRLock *exitlock; + PRCondVar *exitcvar; + PRInt32 *ptcount; + + exitcount=0; + tcount=0; + list = (threadinfo *)PR_MALLOC(sizeof(threadinfo) * (arg * 4)); + ptcount = (PRInt32 *)PR_CALLOC(sizeof(*ptcount) * (arg * 4)); + + sharedlock = PR_NewLock(); + sharedcvar = PR_NewCondVar(sharedlock); + exitlock = PR_NewLock(); + exitcvar = PR_NewCondVar(exitlock); + + /* Create the threads */ + for(index=0; index<arg*4; ) { + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_MillisecondsToInterval(50), + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_LOCAL_THREAD); + index++; + CreateTestThread(&list[index], + index, + sharedlock, + sharedcvar, + count, + PR_MillisecondsToInterval(50), + &tcount, + exitlock, + exitcvar, + &exitcount, + PR_TRUE, + PR_GLOBAL_THREAD); + index++; + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_MillisecondsToInterval(50), + ptcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_LOCAL_THREAD); + index++; + ptcount++; + + list[index].lock = PR_NewLock(); + list[index].cvar = PR_NewCondVar(list[index].lock); + CreateTestThread(&list[index], + index, + list[index].lock, + list[index].cvar, + count, + PR_MillisecondsToInterval(50), + ptcount, + exitlock, + exitcvar, + &exitcount, + PR_FALSE, + PR_GLOBAL_THREAD); + index++; + ptcount++; + } + + + /* Notify every 3rd thread */ + for (loops = 0; loops < count; loops++) { + + /* Notify the threads */ + for(index=0; index<(arg*4); index+=3) { + + PR_Lock(list[index].lock); + *list[index].tcount++; + PR_NotifyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + + } + /* Wait for threads to finish */ + PR_Lock(exitlock); + while(exitcount < arg*4) { + PR_WaitCondVar(exitcvar, PR_SecondsToInterval(60)); + } + PR_ASSERT(exitcount >= arg*4); + exitcount -= arg*4; + PR_Unlock(exitlock); + } + + /* Join all the threads */ + for(index=0; index<(arg*4); index++) { + PR_JoinThread(list[index].thread); + if (list[index].internal) { + PR_Lock(list[index].lock); + PR_DestroyCondVar(list[index].cvar); + PR_Unlock(list[index].lock); + PR_DestroyLock(list[index].lock); + } + } + + PR_DestroyCondVar(sharedcvar); + PR_DestroyLock(sharedlock); + + PR_DELETE(list); +} + +void +CondVarCombinedTest(void *arg) +{ + PRThread *threads[3]; + + threads[0] = PR_CreateThread(PR_USER_THREAD, + CondVarTest, + (void *)arg, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + threads[1] = PR_CreateThread(PR_USER_THREAD, + CondVarTimeoutTest, + (void *)arg, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + threads[2] = PR_CreateThread(PR_USER_THREAD, + CondVarMixedTest, + (void *)arg, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + + PR_JoinThread(threads[0]); + PR_JoinThread(threads[1]); + PR_JoinThread(threads[2]); +} + +/************************************************************************/ + +static void Measure(void (*func)(void *), PRInt32 arg, const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)((void *)arg); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + printf("%40s: %6.2f usec\n", msg, d / count); +} + +static PRIntn PR_CALLBACK RealMain(int argc, char **argv) +{ + PRInt32 threads, default_threads = DEFAULT_THREADS; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "vc:t:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'v': /* debug mode */ + _debug_on = 1; + break; + case 'c': /* loop counter */ + count = atoi(opt->value); + break; + case 't': /* number of threads involved */ + default_threads = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (0 == count) { + count = DEFAULT_COUNT; + } + if (0 == default_threads) { + default_threads = DEFAULT_THREADS; + } + + printf("\n\ +CondVar Test: \n\ + \n\ +Simple test creates several local and global threads; half use a single,\n\ +shared condvar, and the other half have their own condvar. The main \n\ +thread then loops notifying them to wakeup. \n\ + \n\ +The timeout test is very similar except that the threads are not \n\ +notified. They will all wakeup on a 1 second timeout. \n\ + \n\ +The mixed test combines the simple test and the timeout test; every \n\ +third thread is notified, the other threads are expected to timeout \n\ +correctly. \n\ + \n\ +Lastly, the combined test creates a thread for each of the above three \n\ +cases and they all run simultaneously. \n\ + \n\ +This test is run with %d, %d, %d, and %d threads of each type.\n\n", + default_threads, default_threads*2, default_threads*3, default_threads*4); + + PR_SetConcurrency(2); + + for (threads = default_threads; threads < default_threads*5; threads+=default_threads) { + printf("\n%ld Thread tests\n", threads); + Measure(CondVarTestSUU, threads, "Condvar simple test shared UU"); + Measure(CondVarTestSUK, threads, "Condvar simple test shared UK"); + Measure(CondVarTestPUU, threads, "Condvar simple test priv UU"); + Measure(CondVarTestPUK, threads, "Condvar simple test priv UK"); + Measure(CondVarTest, threads, "Condvar simple test All"); + Measure(CondVarTimeoutTest, threads, "Condvar timeout test"); +#if 0 + Measure(CondVarMixedTest, threads, "Condvar mixed timeout test"); + Measure(CondVarCombinedTest, threads, "Combined condvar test"); +#endif + } + + printf("PASS\n"); + + return 0; +} + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/dbmalloc.c b/nsprpub/pr/tests/dbmalloc.c new file mode 100644 index 0000000000..2a7ad4fa85 --- /dev/null +++ b/nsprpub/pr/tests/dbmalloc.c @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dbmalloc.c +** +** Description: Testing malloc (OBSOLETE) +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include "nspr.h" + +void +usage +( + void +) +{ + fprintf(stderr, "Usage: dbmalloc ('-m'|'-s') '-f' num_fails ('-d'|'-n') filename [...]\n"); + exit(0); +} + +typedef struct node_struct +{ + struct node_struct *next, *prev; + int line; + char value[4]; +} +node_t, +*node_pt; + +node_pt get_node(const char *line) +{ + node_pt rv; + int l = strlen(line); + rv = (node_pt)PR_MALLOC(sizeof(node_t) + l + 1 - 4); + if( (node_pt)0 == rv ) { + return (node_pt)0; + } + memcpy(&rv->value[0], line, l+1); + rv->next = rv->prev = (node_pt)0; + return rv; +} + +void +dump +( + const char *name, + node_pt node, + int mf, + int debug +) +{ + if( (node_pt)0 != node->prev ) { + dump(name, node->prev, mf, debug); + } + if( 0 != debug ) { + printf("[%s]: %6d: %s", name, node->line, node->value); + } + if( node->line == mf ) { + fprintf(stderr, "[%s]: Line %d was allocated!\n", name, node->line); + } + if( (node_pt)0 != node->next ) { + dump(name, node->next, mf, debug); + } + return; +} + +void +release +( + node_pt node +) +{ + if( (node_pt)0 != node->prev ) { + release(node->prev); + } + if( (node_pt)0 != node->next ) { + release(node->next); + } + PR_DELETE(node); +} + +int +t2 +( + const char *name, + int mf, + int debug +) +{ + int rv; + FILE *fp; + int l = 0; + node_pt head = (node_pt)0; + char buffer[ BUFSIZ ]; + + fp = fopen(name, "r"); + if( (FILE *)0 == fp ) + { + fprintf(stderr, "[%s]: Cannot open \"%s.\"\n", name, name); + return -1; + } + + /* fgets mallocs a buffer, first time through. */ + if( (char *)0 == fgets(buffer, BUFSIZ, fp) ) + { + fprintf(stderr, "[%s]: \"%s\" is empty.\n", name, name); + (void)fclose(fp); + return -1; + } + + rewind(fp); + + if( PR_SUCCESS != PR_ClearMallocCount() ) + { + fprintf(stderr, "[%s]: Cannot clear malloc count.\n", name); + (void)fclose(fp); + return -1; + } + + if( PR_SUCCESS != PR_SetMallocCountdown(mf) ) + { + fprintf(stderr, "[%s]: Cannot set malloc countdown to %d\n", name, mf); + (void)fclose(fp); + return -1; + } + + while( fgets(buffer, BUFSIZ, fp) ) + { + node_pt n; + node_pt *w = &head; + + if( (strlen(buffer) == (BUFSIZ-1)) && (buffer[BUFSIZ-2] != '\n') ) { + buffer[BUFSIZ-2] == '\n'; + } + + l++; + + n = get_node(buffer); + if( (node_pt)0 == n ) + { + printf("[%s]: Line %d: malloc failure!\n", name, l); + continue; + } + + n->line = l; + + while( 1 ) + { + int comp; + + if( (node_pt)0 == *w ) + { + *w = n; + break; + } + + comp = strcmp((*w)->value, n->value); + if( comp < 0 ) { + w = &(*w)->next; + } + else { + w = &(*w)->prev; + } + } + } + + (void)fclose(fp); + + dump(name, head, mf, debug); + + rv = PR_GetMallocCount(); + PR_ClearMallocCountdown(); + + release(head); + + return rv; +} + +int nf = 0; +int debug = 0; + +void +test +( + const char *name +) +{ + int n, i; + + extern int nf, debug; + + printf("[%s]: starting test 0\n", name); + n = t2(name, 0, debug); + if( -1 == n ) { + return; + } + printf("[%s]: test 0 had %ld allocations.\n", name, n); + + if( 0 >= n ) { + return; + } + + for( i = 0; i < nf; i++ ) + { + int which = rand() % n; + if( 0 == which ) { + printf("[%s]: starting test %d -- no allocation should fail\n", name, i+1); + } + else { + printf("[%s]: starting test %d -- allocation %d should fail\n", name, i+1, which); + } + (void)t2(name, which, debug); + printf("[%s]: test %d done.\n", name, i+1); + } + + return; +} + +int main(int argc, char **argv) +{ + int okay = 0; + int multithread = 0; + + struct threadlist + { + struct threadlist *next; + PRThread *thread; + } + *threadhead = (struct threadlist *)0; + + extern int nf, debug; + + srand(time(0)); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + printf("[main]: We %s using the debugging malloc.\n", + PR_IsDebuggingMalloc() ? "ARE" : "ARE NOT"); + + while( argv++, --argc ) + { + if( '-' == argv[0][0] ) + { + switch( argv[0][1] ) + { + case 'f': + nf = atoi(argv[0][2] ? &argv[0][2] : + --argc ? *++argv : "0"); + break; + case 'd': + debug = 1; + break; + case 'n': + debug = 0; + break; + case 'm': + multithread = 1; + break; + case 's': + multithread = 0; + break; + default: + usage(); + break; + } + } + else + { + FILE *fp = fopen(*argv, "r"); + if( (FILE *)0 == fp ) + { + fprintf(stderr, "Cannot open \"%s.\"\n", *argv); + continue; + } + + okay++; + (void)fclose(fp); + if( multithread ) + { + struct threadlist *n; + + n = (struct threadlist *)malloc(sizeof(struct threadlist)); + if( (struct threadlist *)0 == n ) + { + fprintf(stderr, "This is getting tedious. \"%s\"\n", *argv); + continue; + } + + n->next = threadhead; + n->thread = PR_CreateThread(PR_USER_THREAD, (void (*)(void *))test, + *argv, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, + 0); + if( (PRThread *)0 == n->thread ) + { + fprintf(stderr, "Can't create thread for \"%s.\"\n", *argv); + continue; + } + else + { + threadhead = n; + } + } + else + { + test(*argv); + } + } + } + + if( okay == 0 ) { + usage(); + } + else while( (struct threadlist *)0 != threadhead ) + { + struct threadlist *x = threadhead->next; + (void)PR_JoinThread(threadhead->thread); + PR_DELETE(threadhead); + threadhead = x; + } + + return 0; +} + diff --git a/nsprpub/pr/tests/dbmalloc1.c b/nsprpub/pr/tests/dbmalloc1.c new file mode 100644 index 0000000000..a7e995c409 --- /dev/null +++ b/nsprpub/pr/tests/dbmalloc1.c @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dbmalloc1.c (OBSOLETE) +** +** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** +** 12-June-97 AGarcia Revert to return code 0 and 1, remove debug option (obsolete). +***********************************************************************/ + + +/*********************************************************************** +** Includes +***********************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include "nspr.h" + +PRIntn failed_already=0; +PRIntn debug_mode; + +/* variable used for both r1 and r2 tests */ +int should_fail =0; +int actually_failed=0; + + +void +r1 +( + void +) +{ + int i; + actually_failed=0; + for( i = 0; i < 5; i++ ) + { + void *x = PR_MALLOC(128); + if( (void *)0 == x ) { + if (debug_mode) { + printf("\tMalloc %d failed.\n", i+1); + } + actually_failed = 1; + } + PR_DELETE(x); + } + + if (((should_fail != actually_failed) & (!debug_mode))) { + failed_already=1; + } + + + return; +} + +void +r2 +( + void +) +{ + int i; + + for( i = 0; i <= 5; i++ ) + { + should_fail =0; + if( 0 == i ) { + if (debug_mode) { + printf("No malloc should fail:\n"); + } + } + else { + if (debug_mode) { + printf("Malloc %d should fail:\n", i); + } + should_fail = 1; + } + PR_SetMallocCountdown(i); + r1(); + PR_ClearMallocCountdown(); + } +} + +int main(int argc, char **argv) +{ + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + r2(); + + if(failed_already) { + return 1; + } + else { + return 0; + } + + +} + diff --git a/nsprpub/pr/tests/dceemu.c b/nsprpub/pr/tests/dceemu.c new file mode 100644 index 0000000000..2b1d0901a9 --- /dev/null +++ b/nsprpub/pr/tests/dceemu.c @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: dceemu.c +** Description: testing the DCE emulation api +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 Revert to return code 0 and 1, remove debug option (obsolete). +**/ + +/*********************************************************************** +** Includes +***********************************************************************/ + +#include "prlog.h" +#include "prinit.h" +#include "prpdce.h" + +#include <stdio.h> +#include <stdlib.h> + +PRIntn failed_already=0; +PRIntn debug_mode=0; + +static PRIntn prmain(PRIntn argc, char **argv) +{ + PRStatus rv; + PRLock *ml = PR_NewLock(); + PRCondVar *cv = PRP_NewNakedCondVar(); + PRIntervalTime tenmsecs = PR_MillisecondsToInterval(10); + + rv = PRP_TryLock(ml); + PR_ASSERT(PR_SUCCESS == rv); + if ((rv != PR_SUCCESS) & (!debug_mode)) { + failed_already=1; + } + + rv = PRP_TryLock(ml); + PR_ASSERT(PR_FAILURE == rv); + if ((rv != PR_FAILURE) & (!debug_mode)) { + failed_already=1; + } + + rv = PRP_NakedNotify(cv); + PR_ASSERT(PR_SUCCESS == rv); + if ((rv != PR_SUCCESS) & (!debug_mode)) { + failed_already=1; + } + + rv = PRP_NakedBroadcast(cv); + PR_ASSERT(PR_SUCCESS == rv); + if ((rv != PR_SUCCESS) & (!debug_mode)) { + failed_already=1; + } + + rv = PRP_NakedWait(cv, ml, tenmsecs); + PR_ASSERT(PR_SUCCESS == rv); + if ((rv != PR_SUCCESS) & (!debug_mode)) { + failed_already=1; + } + + PR_Unlock(ml); + + rv = PRP_NakedNotify(cv); + PR_ASSERT(PR_SUCCESS == rv); + if ((rv != PR_SUCCESS) & (!debug_mode)) { + failed_already=1; + } + + rv = PRP_NakedBroadcast(cv); + PR_ASSERT(PR_SUCCESS == rv); + if ((rv != PR_SUCCESS) & (!debug_mode)) { + failed_already=1; + } + + PRP_DestroyNakedCondVar(cv); + PR_DestroyLock(ml); + + if (debug_mode) { + printf("Test succeeded\n"); + } + + return 0; + +} /* prmain */ + +int main(int argc, char **argv) +{ + PR_Initialize(prmain, argc, argv, 0); + if(failed_already) { + return 1; + } + else { + return 0; + } +} /* main */ + + +/* decemu.c */ diff --git a/nsprpub/pr/tests/depend.c b/nsprpub/pr/tests/depend.c new file mode 100644 index 0000000000..f3689bc460 --- /dev/null +++ b/nsprpub/pr/tests/depend.c @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1996 - Netscape Communications Corporation +** +** +** Name: depend.c +** Description: Test to enumerate the dependencies +* +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ +#include "prinit.h" + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> + +static void PrintVersion( + const char *msg, const PRVersion* info, PRIntn tab) +{ + static const len = 20; + static const char *tabs = {" "}; + + tab *= 2; + if (tab > len) { + tab = len; + } + printf("%s", &tabs[len - tab]); + printf("%s ", msg); + printf("%s ", info->id); + printf("%d.%d", info->major, info->minor); + if (0 != info->patch) { + printf(".p%d", info->patch); + } + printf("\n"); +} /* PrintDependency */ + +static void ChaseDependents(const PRVersionInfo *info, PRIntn tab) +{ + PrintVersion("exports", &info->selfExport, tab); + if (NULL != info->importEnumerator) + { + const PRDependencyInfo *dependent = NULL; + while (NULL != (dependent = info->importEnumerator(dependent))) + { + const PRVersionInfo *import = dependent->exportInfoFn(); + PrintVersion("imports", &dependent->importNeeded, tab); + ChaseDependents(import, tab + 1); + } + } +} /* ChaseDependents */ + +static PRVersionInfo hack_export; +static PRVersionInfo dummy_export; +static PRDependencyInfo dummy_imports[2]; + +static const PRVersionInfo *HackExportInfo(void) +{ + hack_export.selfExport.major = 11; + hack_export.selfExport.minor = 10; + hack_export.selfExport.patch = 200; + hack_export.selfExport.id = "Hack"; + hack_export.importEnumerator = NULL; + return &hack_export; +} + +static const PRDependencyInfo *DummyImports( + const PRDependencyInfo *previous) +{ + if (NULL == previous) { + return &dummy_imports[0]; + } + else if (&dummy_imports[0] == previous) { + return &dummy_imports[1]; + } + else if (&dummy_imports[1] == previous) { + return NULL; + } +} /* DummyImports */ + +static const PRVersionInfo *DummyLibVersion(void) +{ + dummy_export.selfExport.major = 1; + dummy_export.selfExport.minor = 0; + dummy_export.selfExport.patch = 0; + dummy_export.selfExport.id = "Dumbass application"; + dummy_export.importEnumerator = DummyImports; + + dummy_imports[0].importNeeded.major = 2; + dummy_imports[0].importNeeded.minor = 0; + dummy_imports[0].importNeeded.patch = 0; + dummy_imports[0].importNeeded.id = "Netscape Portable Runtime"; + dummy_imports[0].exportInfoFn = PR_ExportInfo; + + dummy_imports[1].importNeeded.major = 5; + dummy_imports[1].importNeeded.minor = 1; + dummy_imports[1].importNeeded.patch = 2; + dummy_imports[1].importNeeded.id = "Hack Library"; + dummy_imports[1].exportInfoFn = HackExportInfo; + + return &dummy_export; +} /* DummyLibVersion */ + +int main(int argc, char **argv) +{ + PRIntn tab = 0; + const PRVersionInfo *info = DummyLibVersion(); + const char *buildDate = __DATE__, *buildTime = __TIME__; + + printf("Depend.c build time is %s %s\n", buildDate, buildTime); + + if (NULL != info) { + ChaseDependents(info, tab); + } + + return 0; +} /* main */ + +/* depend.c */ diff --git a/nsprpub/pr/tests/dll/Makefile.in b/nsprpub/pr/tests/dll/Makefile.in new file mode 100644 index 0000000000..8b8c5ac20d --- /dev/null +++ b/nsprpub/pr/tests/dll/Makefile.in @@ -0,0 +1,77 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#! gmake + +MOD_DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(MOD_DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/config.mk + +CSRCS = mygetval.c mysetval.c + +INCLUDES = -I$(dist_includedir) + +OBJS = $(OBJDIR)/mygetval.$(OBJ_SUFFIX) \ + $(OBJDIR)/mysetval.$(OBJ_SUFFIX) + +ifeq ($(OS_TARGET), WIN16) +W16OBJS = $(subst $(space),$(comma)$(space),$(OBJS)) +endif + +ifeq ($(OS_ARCH), WINNT) +ifeq ($(OS_TARGET), WIN16) +# do nothing +else +RES=$(OBJDIR)/my.res +RESNAME=../../../pr/src/nspr.rc +endif +endif + +ifeq (,$(filter-out WINNT OS2,$(OS_ARCH))) +IMPORT_LIBRARY = $(OBJDIR)/my.$(LIB_SUFFIX) +SHARED_LIBRARY = $(OBJDIR)/my.dll +ifeq ($(OS_ARCH), OS2) +MAPFILE = $(OBJDIR)/my.def +GARBAGE += $(MAPFILE) +MKSHLIB += $(MAPFILE) +endif +TARGETS = $(SHARED_LIBRARY) $(IMPORT_LIBRARY) +else +ifdef MKSHLIB +SHARED_LIBRARY = $(OBJDIR)/libmy.$(DLL_SUFFIX) +endif +TARGETS = $(SHARED_LIBRARY) +endif + +# +# To create a loadable module on Darwin, we must override +# -dynamiclib with -bundle. +# +ifeq ($(OS_ARCH),Darwin) +DSO_LDOPTS = -bundle +endif + +include $(topsrcdir)/config/rules.mk + +ifeq ($(OS_TARGET), WIN16) +# Note: The Win16 target: my.dll requires these macros +# to be overridden to build the test .dll +# default values in win16...mk are for release targets. +# +OS_DLL_OPTION = NOCASEEXACT +OS_LIB_FLAGS = -irn +endif + +ifdef SHARED_LIBRARY +export:: $(TARGETS) + +clean:: + rm -rf $(TARGETS) +endif diff --git a/nsprpub/pr/tests/dll/my.def b/nsprpub/pr/tests/dll/my.def new file mode 100644 index 0000000000..75b71f5ac1 --- /dev/null +++ b/nsprpub/pr/tests/dll/my.def @@ -0,0 +1,26 @@ +;+# +;+# This Source Code Form is subject to the terms of the Mozilla Public +;+# License, v. 2.0. If a copy of the MPL was not distributed with this +;+# file, You can obtain one at http://mozilla.org/MPL/2.0/. +;+# +;+# OK, this file is meant to support SUN, LINUX, AIX, OS/2 and WINDOWS +;+# 1. For all unix platforms, the string ";-" means "remove this line" +;+# 2. For all unix platforms, the string " DATA " will be removed from any +;+# line on which it occurs. +;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX. +;+# On AIX, lines containing ";+" will be removed. +;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed. +;+# 5. For all unix platforms, after the above processing has taken place, +;+# all characters after the first ";" on the line will be removed. +;+# And for AIX, the first ";" will also be removed. +;+# This file is passed directly to windows. Since ';' is a comment, all UNIX +;+# directives are hidden behind ";", ";+", and ";-" +;+# +;+MY_1.0 { +;+ global: +LIBRARY my ;- +EXPORTS ;- + My_GetValue; + My_SetValue; +;+ local: *; +;+}; diff --git a/nsprpub/pr/tests/dll/mygetval.c b/nsprpub/pr/tests/dll/mygetval.c new file mode 100644 index 0000000000..7fb7002441 --- /dev/null +++ b/nsprpub/pr/tests/dll/mygetval.c @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#if defined(WIN16) +#include <windows.h> +#endif +#include "prtypes.h" + +extern PRIntn my_global; + +PR_IMPLEMENT(PRIntn) My_GetValue() +{ + return my_global; +} + +#if defined(WIN16) +int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, + WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + return TRUE; +} +#endif /* WIN16 */ + diff --git a/nsprpub/pr/tests/dll/mysetval.c b/nsprpub/pr/tests/dll/mysetval.c new file mode 100644 index 0000000000..232428adf8 --- /dev/null +++ b/nsprpub/pr/tests/dll/mysetval.c @@ -0,0 +1,13 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prtypes.h" + +PRIntn my_global = 0; + +PR_IMPLEMENT(void) My_SetValue(PRIntn val) +{ + my_global = val; +} diff --git a/nsprpub/pr/tests/dlltest.c b/nsprpub/pr/tests/dlltest.c new file mode 100644 index 0000000000..d687322810 --- /dev/null +++ b/nsprpub/pr/tests/dlltest.c @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dlltest.c +** +** Description: test dll functionality. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 Revert to return code 0 and 1. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +#include "prinit.h" +#include "prlink.h" +#include "prmem.h" +#include "prerror.h" + +#include "plstr.h" + +#include <stdio.h> +#include <stdlib.h> + +typedef PRIntn (PR_CALLBACK *GetFcnType)(void); +typedef void (PR_CALLBACK *SetFcnType)(PRIntn); + +PRIntn failed_already=0; +PRIntn debug_mode; + +int main(int argc, char** argv) +{ + PRLibrary *lib, *lib2; /* two handles to the same library */ + GetFcnType getFcn; + SetFcnType setFcn; + PRIntn value; + PRStatus status; + char *libName; + + if (argc >= 2 && PL_strcmp(argv[1], "-d") == 0) { + debug_mode = 1; + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + /* + * Test 1: load the library, look up the symbols, call the functions, + * and check the results. + */ + + libName = PR_GetLibraryName("dll", "my"); + if (debug_mode) { + printf("Loading library %s\n", libName); + } + lib = PR_LoadLibrary(libName); + PR_FreeLibraryName(libName); + if (lib == NULL) { + PRInt32 textLength = PR_GetErrorTextLength(); + char *text = (char*)PR_MALLOC(textLength + 1); + text[0] = '\0'; + (void)PR_GetErrorText(text); + fprintf( + stderr, "PR_LoadLibrary failed (%d, %d, %s)\n", + PR_GetError(), PR_GetOSError(), text); + if (!debug_mode) { + failed_already=1; + } + } + getFcn = (GetFcnType) PR_FindSymbol(lib, "My_GetValue"); + setFcn = (SetFcnType) PR_FindFunctionSymbol(lib, "My_SetValue"); + (*setFcn)(888); + value = (*getFcn)(); + if (value != 888) { + fprintf(stderr, "Test 1 failed: set value to 888, but got %d\n", value); + if (!debug_mode) { + failed_already=1; + } + } + if (debug_mode) { + printf("Test 1 passed\n"); + } + + /* + * Test 2: get a second handle to the same library (this should increment + * the reference count), look up the symbols, call the functions, and + * check the results. + */ + + getFcn = (GetFcnType) PR_FindSymbolAndLibrary("My_GetValue", &lib2); + if (NULL == getFcn || lib != lib2) { + fprintf(stderr, "Test 2 failed: handles for the same library are not " + "equal: handle 1: %p, handle 2: %p\n", lib, lib2); + if (!debug_mode) { + failed_already=1; + } + } + setFcn = (SetFcnType) PR_FindSymbol(lib2, "My_SetValue"); + value = (*getFcn)(); + if (value != 888) { + fprintf(stderr, "Test 2 failed: value should be 888, but got %d\n", + value); + if (!debug_mode) { + failed_already=1; + } + } + (*setFcn)(777); + value = (*getFcn)(); + if (value != 777) { + fprintf(stderr, "Test 2 failed: set value to 777, but got %d\n", value); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + if (debug_mode) { + printf("Test 2 passed\n"); + } + + /* + * Test 3: unload the library. The library should still be accessible + * via the second handle. do the same things as above. + */ + + status = PR_UnloadLibrary(lib); + if (PR_FAILURE == status) { + fprintf(stderr, "Test 3 failed: cannot unload library: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + getFcn = (GetFcnType) PR_FindFunctionSymbol(lib2, "My_GetValue"); + setFcn = (SetFcnType) PR_FindSymbol(lib2, "My_SetValue"); + (*setFcn)(666); + value = (*getFcn)(); + if (value != 666) { + fprintf(stderr, "Test 3 failed: set value to 666, but got %d\n", value); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + if (debug_mode) { + printf("Test 3 passed\n"); + } + + /* + * Test 4: unload the library, testing the reference count mechanism. + */ + + status = PR_UnloadLibrary(lib2); + if (PR_FAILURE == status) { + fprintf(stderr, "Test 4 failed: cannot unload library: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + getFcn = (GetFcnType) PR_FindFunctionSymbolAndLibrary("My_GetValue", &lib2); + if (NULL != getFcn) { + fprintf(stderr, "Test 4 failed: how can we find a symbol " + "in an already unloaded library?\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + if (debug_mode) { + printf("Test 4 passed\n"); + } + + /* + ** Test 5: LoadStaticLibrary() + */ + { + PRStaticLinkTable slt[10]; + PRLibrary *lib; + + lib = PR_LoadStaticLibrary( "my.dll", slt ); + if ( lib == NULL ) + { + fprintf(stderr, "Test 5: LoadStatiLibrary() failed\n" ); + goto exit_now; + } + if (debug_mode) + { + printf("Test 5 passed\n"); + } + } + + goto exit_now; +exit_now: + PR_Cleanup(); + + if (failed_already) { + printf("FAILED\n"); + return 1; + } else { + printf("PASSED\n"); + return 0; + } +} diff --git a/nsprpub/pr/tests/dtoa.c b/nsprpub/pr/tests/dtoa.c new file mode 100644 index 0000000000..ef2e4dc4de --- /dev/null +++ b/nsprpub/pr/tests/dtoa.c @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/****************************************************************************** + * + * This file contains a test program for the function conversion functions + * for double precision code: + * PR_strtod + * PR_dtoa + * PR_cnvtf + * + * This file was ns/nspr/tests/dtoa.c, created by rrj on 1996/06/22. + * + *****************************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <locale.h> +#include "prprf.h" +#include "prdtoa.h" + +static int failed_already = 0; + +int main(int argc, char **argv) +{ + double num; + double num1; + double zero = 0.0; + char cnvt[50]; + char *thousands; + + num = 1e24; + num1 = PR_strtod("1e24",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","1e24"); + failed_already = 1; + } + + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("1e+24",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = 0.001e7; + num1 = PR_strtod("0.001e7",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","0.001e7"); + failed_already = 1; + } + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("10000",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = 0.0000000000000753; + num1 = PR_strtod("0.0000000000000753",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n", + "0.0000000000000753"); + failed_already = 1; + } + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("7.53e-14",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = 1.867e73; + num1 = PR_strtod("1.867e73",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","1.867e73"); + failed_already = 1; + } + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("1.867e+73",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + + num = -1.867e73; + num1 = PR_strtod("-1.867e73",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e73"); + failed_already = 1; + } + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("-1.867e+73",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = -1.867e-73; + num1 = PR_strtod("-1.867e-73",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e-73"); + failed_already = 1; + } + + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("-1.867e-73",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + /* Testing for infinity */ + num = 1.0 / zero; + num1 = PR_strtod("1.867e765",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","1.867e765"); + failed_already = 1; + } + + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("Infinity",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = -1.0 / zero; + num1 = PR_strtod("-1.867e765",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n","-1.867e765"); + failed_already = 1; + } + + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("-Infinity",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + /* Testing for NaN. PR_strtod can't parse "NaN" and "Infinity" */ + num = zero / zero; + + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("NaN",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = - zero / zero; + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("NaN",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = 1.0000000001e21; + num1 = PR_strtod("1.0000000001e21",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n", + "1.0000000001e21"); + failed_already = 1; + } + + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("1.0000000001e+21",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + num = -1.0000000001e-21; + num1 = PR_strtod("-1.0000000001e-21",NULL); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n", + "-1.0000000001e-21"); + failed_already = 1; + } + PR_cnvtf(cnvt,sizeof(cnvt),20,num); + if(strcmp("-1.0000000001e-21",cnvt) != 0) { + fprintf(stderr,"Failed to convert numeric value %lf %s\n",num,cnvt); + failed_already = 1; + } + + /* + * Bug 414772: should not exit with "Zero passed to d2b" in debug + * build. + */ + num1 = PR_strtod("4e-356",NULL); + + /* + * A very long input with ~384K digits. + * Bug 516396: Should not crash. + * Bug 521306: Should return 0 without converting the input. + */ +#define LENGTH (384 * 1024) + thousands = (char *)malloc(LENGTH); + thousands[0] = '0'; + thousands[1] = '.'; + memset(&thousands[2], '1', LENGTH - 3); + thousands[LENGTH - 1] = '\0'; + num = 0; + num1 = PR_strtod(thousands,NULL); + free(thousands); + if(num1 != num) { + fprintf(stderr,"Failed to convert numeric value %s\n", + "0.1111111111111111..."); + failed_already = 1; + } + + if (failed_already) { + printf("FAILED\n"); + } else { + printf("PASSED\n"); + } + return failed_already; +} diff --git a/nsprpub/pr/tests/env.c b/nsprpub/pr/tests/env.c new file mode 100644 index 0000000000..17b0eeb9b8 --- /dev/null +++ b/nsprpub/pr/tests/env.c @@ -0,0 +1,370 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: env.c +** Description: Testing environment variable operations +** +*/ +#include "prenv.h" +#include "prmem.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn debug = 0; +PRIntn verbose = 0; +PRIntn secure = 0; +PRBool failedAlready = PR_FALSE; + +#define ENVNAME "NSPR_ENVIRONMENT_TEST_VARIABLE" +#define ENVVALUE "The expected result" +#define ENVBUFSIZE 256 + +char *envBuf; /* buffer pointer. We leak memory here on purpose! */ + +static char * NewBuffer( size_t size ) +{ + char *buf = malloc( size ); + if ( NULL == buf ) { + printf("env: NewBuffer() failed\n"); + exit(1); + } + return(buf); +} /* end NewBuffer() */ + +int main(int argc, char **argv) +{ + char *value; + PRStatus rc; + + { /* Get command line options */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "vds"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug */ + debug = 1; + break; + case 'v': /* verbose */ + verbose = 1; + break; + case 's': /* secure / set[ug]id */ + /* + ** To test PR_GetEnvSecure, make this executable (or a + ** copy of it) setuid / setgid / otherwise inherently + ** privileged (e.g., file capabilities) and run it + ** with this flag. + */ + secure = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } /* end block "Get command line options" */ + +#if 0 + { + /* + ** This uses Windows native environment manipulation + ** as an experiment. Note the separation of namespace! + */ + BOOL rv; + DWORD size; + rv = SetEnvironmentVariable( ENVNAME, ENVVALUE ); + if ( rv == 0 ) { + if (debug) { + printf("env: Shit! SetEnvironmentVariable() failed\n"); + } + failedAlready = PR_TRUE; + } + if (verbose) { + printf("env: SetEnvironmentVariable() worked\n"); + } + + size = GetEnvironmentVariable( ENVNAME, envBuf, ENVBUFSIZE ); + if ( size == 0 ) { + if (debug) { + printf("env: Shit! GetEnvironmentVariable() failed. Found: %s\n", envBuf ); + } + failedAlready = PR_TRUE; + } + if (verbose) { + printf("env: GetEnvironmentVariable() worked. Found: %s\n", envBuf); + } + + value = PR_GetEnv( ENVNAME ); + if ( (NULL == value ) || (strcmp( value, ENVVALUE))) { + if (debug) { + printf( "env: PR_GetEnv() failed retrieving WinNative. Found: %s\n", value); + } + failedAlready = PR_TRUE; + } + if (verbose) { + printf("env: PR_GetEnv() worked. Found: %s\n", value); + } + } +#endif + + /* set an environment variable, read it back */ + envBuf = NewBuffer( ENVBUFSIZE ); + sprintf( envBuf, ENVNAME "=" ENVVALUE ); + rc = PR_SetEnv( envBuf ); + if ( PR_FAILURE == rc ) { + if (debug) { + printf( "env: PR_SetEnv() failed setting\n"); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_SetEnv() worked.\n"); + } + } + + value = PR_GetEnv( ENVNAME ); + if ( (NULL == value ) || (strcmp( value, ENVVALUE))) { + if (debug) { + printf( "env: PR_GetEnv() Failed after setting\n" ); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_GetEnv() worked after setting it. Found: %s\n", value ); + } + } + + if ( secure ) { + /* + ** In this case we've been run with elevated privileges, so + ** test that PR_GetEnvSecure *doesn't* find that env var. + */ + value = PR_GetEnvSecure( ENVNAME ); + if ( NULL != value ) { + if (debug) { + printf( "env: PR_GetEnvSecure() failed; expected NULL, found \"%s\"\n", value ); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_GetEnvSecure() worked\n" ); + } + } + } else { + /* + ** In this case the program is being run normally, so do the + ** same check for PR_GetEnvSecure as for PR_GetEnv. + */ + value = PR_GetEnvSecure( ENVNAME ); + if ( (NULL == value ) || (strcmp( value, ENVVALUE))) { + if (debug) { + printf( "env: PR_GetEnvSecure() Failed after setting\n" ); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_GetEnvSecure() worked after setting it. Found: %s\n", value ); + } + } + } + + /* ---------------------------------------------------------------------- */ + /* check that PR_DuplicateEnvironment() agrees with PR_GetEnv() */ + { +#if defined(XP_UNIX) && (!defined(DARWIN) || defined(HAVE_CRT_EXTERNS_H)) + static const PRBool expect_failure = PR_FALSE; +#else + static const PRBool expect_failure = PR_TRUE; +#endif + char **i, **dupenv = PR_DuplicateEnvironment(); + + + if ( NULL == dupenv ) { + if (expect_failure) { + if (verbose) printf("env: PR_DuplicateEnvironment failed, " + "as expected on this platform.\n"); + } else { + if (debug) { + printf("env: PR_DuplicateEnvironment() failed.\n"); + } + failedAlready = PR_TRUE; + } + } else { + unsigned found = 0; + + if (expect_failure) { + if (debug) printf("env: PR_DuplicateEnvironment() succeeded, " + "but failure is expected on this platform.\n"); + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_DuplicateEnvironment() succeeded.\n"); + } + } + for (i = dupenv; *i; i++) { + char *equals = strchr(*i, '='); + + if ( equals == NULL ) { + if (debug) printf("env: PR_DuplicateEnvironment() returned a string" + " with no '=': %s\n", *i); + failedAlready = PR_TRUE; + } else { + /* We own this string, so we can temporarily alter it */ + /* *i is the null-terminated name; equals + 1 is the value */ + *equals = '\0'; + + if ( strcmp(*i, ENVNAME) == 0) { + found++; + if (verbose) printf("env: PR_DuplicateEnvironment() found " ENVNAME + " (%u so far).\n", found); + } + + /* Multiple values for the same name can't happen, according to POSIX. */ + value = PR_GetEnv(*i); + if ( value == NULL ) { + if (debug) printf("env: PR_DuplicateEnvironment() returned a name" + " which PR_GetEnv() failed to find: %s\n", *i); + failedAlready = PR_TRUE; + } else if ( strcmp(equals + 1, value) != 0) { + if (debug) printf("env: PR_DuplicateEnvironment() returned the wrong" + " value for %s: expected %s; found %s\n", + *i, value, equals + 1); + failedAlready = PR_TRUE; + } else { + if (verbose) printf("env: PR_DuplicateEnvironment() agreed with" + " PR_GetEnv() about %s\n", *i); + } + } + PR_Free(*i); + } + PR_Free(dupenv); + + if (found != 1) { + if (debug) printf("env: PR_DuplicateEnvironment() found %u entries for " ENVNAME + " (expected 1)\n", found); + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_DuplicateEnvironment() found 1 entry for " ENVNAME "\n"); + } + } + } + } + + /* ---------------------------------------------------------------------- */ + /* un-set the variable, using RAW name... should not work */ + envBuf = NewBuffer( ENVBUFSIZE ); + sprintf( envBuf, ENVNAME ); + rc = PR_SetEnv( envBuf ); + if ( PR_FAILURE == rc ) { + if (verbose) { + printf( "env: PR_SetEnv() not un-set using RAW name. Good!\n"); + } + } else { + if (debug) { + printf("env: PR_SetEnv() un-set using RAW name. Bad!\n" ); + } + failedAlready = PR_TRUE; + } + + value = PR_GetEnv( ENVNAME ); + if ( NULL == value ) { + if (debug) { + printf("env: PR_GetEnv() after un-set using RAW name. Bad!\n" ); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf( "env: PR_GetEnv() after RAW un-set found: %s\n", value ); + } + } + + /* ---------------------------------------------------------------------- */ + /* set it again ... */ + envBuf = NewBuffer( ENVBUFSIZE ); + sprintf( envBuf, ENVNAME "=" ENVVALUE ); + rc = PR_SetEnv( envBuf ); + if ( PR_FAILURE == rc ) { + if (debug) { + printf( "env: PR_SetEnv() failed setting the second time.\n"); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_SetEnv() worked.\n"); + } + } + + /* un-set the variable using the form name= */ + envBuf = NewBuffer( ENVBUFSIZE ); + sprintf( envBuf, ENVNAME "=" ); + rc = PR_SetEnv( envBuf ); + if ( PR_FAILURE == rc ) { + if (debug) { + printf( "env: PR_SetEnv() failed un-setting using name=\n"); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_SetEnv() un-set using name= worked\n" ); + } + } + + value = PR_GetEnv( ENVNAME ); + if (( NULL == value ) || ( 0x00 == *value )) { + if (verbose) { + printf("env: PR_GetEnv() after un-set using name= worked\n" ); + } + } else { + if (debug) { + printf( "env: PR_GetEnv() after un-set using name=. Found: %s\n", value ); + } + failedAlready = PR_TRUE; + } + /* ---------------------------------------------------------------------- */ + /* un-set the variable using the form name= */ + envBuf = NewBuffer( ENVBUFSIZE ); + sprintf( envBuf, ENVNAME "999=" ); + rc = PR_SetEnv( envBuf ); + if ( PR_FAILURE == rc ) { + if (debug) { + printf( "env: PR_SetEnv() failed un-setting using name=\n"); + } + failedAlready = PR_TRUE; + } else { + if (verbose) { + printf("env: PR_SetEnv() un-set using name= worked\n" ); + } + } + + value = PR_GetEnv( ENVNAME "999" ); + if (( NULL == value ) || ( 0x00 == *value )) { + if (verbose) { + printf("env: PR_GetEnv() after un-set using name= worked\n" ); + } + } else { + if (debug) { + printf( "env: PR_GetEnv() after un-set using name=. Found: %s\n", value ); + } + failedAlready = PR_TRUE; + } + + /* ---------------------------------------------------------------------- */ + if (debug || verbose) { + printf("\n%s\n", (failedAlready)? "FAILED" : "PASSED" ); + } + return( (failedAlready)? 1 : 0 ); +} /* main() */ + +/* env.c */ diff --git a/nsprpub/pr/tests/errcodes.c b/nsprpub/pr/tests/errcodes.c new file mode 100644 index 0000000000..591b630b23 --- /dev/null +++ b/nsprpub/pr/tests/errcodes.c @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: errcodes.c +** +** Description: print nspr error codes +** +*/ +#include "prerror.h" +#include "plgetopt.h" + +#include <stdio.h> + +static int _debug_on = 0; + +struct errinfo { + PRErrorCode errcode; + char *errname; +}; + +struct errinfo errcodes[] = { + {PR_OUT_OF_MEMORY_ERROR, "PR_OUT_OF_MEMORY_ERROR"}, + {PR_BAD_DESCRIPTOR_ERROR, "PR_BAD_DESCRIPTOR_ERROR"}, + {PR_WOULD_BLOCK_ERROR, "PR_WOULD_BLOCK_ERROR"}, + {PR_ACCESS_FAULT_ERROR, "PR_ACCESS_FAULT_ERROR"}, + {PR_INVALID_METHOD_ERROR, "PR_INVALID_METHOD_ERROR"}, + {PR_ILLEGAL_ACCESS_ERROR, "PR_ILLEGAL_ACCESS_ERROR"}, + {PR_UNKNOWN_ERROR, "PR_UNKNOWN_ERROR"}, + {PR_PENDING_INTERRUPT_ERROR, "PR_PENDING_INTERRUPT_ERROR"}, + {PR_NOT_IMPLEMENTED_ERROR, "PR_NOT_IMPLEMENTED_ERROR"}, + {PR_IO_ERROR, "PR_IO_ERROR"}, + {PR_IO_TIMEOUT_ERROR, "PR_IO_TIMEOUT_ERROR"}, + {PR_IO_PENDING_ERROR, "PR_IO_PENDING_ERROR"}, + {PR_DIRECTORY_OPEN_ERROR, "PR_DIRECTORY_OPEN_ERROR"}, + {PR_INVALID_ARGUMENT_ERROR, "PR_INVALID_ARGUMENT_ERROR"}, + {PR_ADDRESS_NOT_AVAILABLE_ERROR, "PR_ADDRESS_NOT_AVAILABLE_ERROR"}, + {PR_ADDRESS_NOT_SUPPORTED_ERROR, "PR_ADDRESS_NOT_SUPPORTED_ERROR"}, + {PR_IS_CONNECTED_ERROR, "PR_IS_CONNECTED_ERROR"}, + {PR_BAD_ADDRESS_ERROR, "PR_BAD_ADDRESS_ERROR"}, + {PR_ADDRESS_IN_USE_ERROR, "PR_ADDRESS_IN_USE_ERROR"}, + {PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"}, + {PR_NETWORK_UNREACHABLE_ERROR, "PR_NETWORK_UNREACHABLE_ERROR"}, + {PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"}, + {PR_NOT_CONNECTED_ERROR, "PR_NOT_CONNECTED_ERROR"}, + {PR_LOAD_LIBRARY_ERROR, "PR_LOAD_LIBRARY_ERROR"}, + {PR_UNLOAD_LIBRARY_ERROR, "PR_UNLOAD_LIBRARY_ERROR"}, + {PR_FIND_SYMBOL_ERROR, "PR_FIND_SYMBOL_ERROR"}, + {PR_INSUFFICIENT_RESOURCES_ERROR, "PR_INSUFFICIENT_RESOURCES_ERROR"}, + {PR_DIRECTORY_LOOKUP_ERROR, "PR_DIRECTORY_LOOKUP_ERROR"}, + {PR_TPD_RANGE_ERROR, "PR_TPD_RANGE_ERROR"}, + {PR_PROC_DESC_TABLE_FULL_ERROR, "PR_PROC_DESC_TABLE_FULL_ERROR"}, + {PR_SYS_DESC_TABLE_FULL_ERROR, "PR_SYS_DESC_TABLE_FULL_ERROR"}, + {PR_NOT_SOCKET_ERROR, "PR_NOT_SOCKET_ERROR"}, + {PR_NOT_TCP_SOCKET_ERROR, "PR_NOT_TCP_SOCKET_ERROR"}, + {PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "PR_SOCKET_ADDRESS_IS_BOUND_ERROR"}, + {PR_NO_ACCESS_RIGHTS_ERROR, "PR_NO_ACCESS_RIGHTS_ERROR"}, + {PR_OPERATION_NOT_SUPPORTED_ERROR, "PR_OPERATION_NOT_SUPPORTED_ERROR"}, + {PR_PROTOCOL_NOT_SUPPORTED_ERROR, "PR_PROTOCOL_NOT_SUPPORTED_ERROR"}, + {PR_REMOTE_FILE_ERROR, "PR_REMOTE_FILE_ERROR"}, + {PR_BUFFER_OVERFLOW_ERROR, "PR_BUFFER_OVERFLOW_ERROR"}, + {PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"}, + {PR_RANGE_ERROR, "PR_RANGE_ERROR"}, + {PR_DEADLOCK_ERROR, "PR_DEADLOCK_ERROR"}, + {PR_FILE_IS_LOCKED_ERROR, "PR_FILE_IS_LOCKED_ERROR"}, + {PR_FILE_TOO_BIG_ERROR, "PR_FILE_TOO_BIG_ERROR"}, + {PR_NO_DEVICE_SPACE_ERROR, "PR_NO_DEVICE_SPACE_ERROR"}, + {PR_PIPE_ERROR, "PR_PIPE_ERROR"}, + {PR_NO_SEEK_DEVICE_ERROR, "PR_NO_SEEK_DEVICE_ERROR"}, + {PR_IS_DIRECTORY_ERROR, "PR_IS_DIRECTORY_ERROR"}, + {PR_LOOP_ERROR, "PR_LOOP_ERROR"}, + {PR_NAME_TOO_LONG_ERROR, "PR_NAME_TOO_LONG_ERROR"}, + {PR_FILE_NOT_FOUND_ERROR, "PR_FILE_NOT_FOUND_ERROR"}, + {PR_NOT_DIRECTORY_ERROR, "PR_NOT_DIRECTORY_ERROR"}, + {PR_READ_ONLY_FILESYSTEM_ERROR, "PR_READ_ONLY_FILESYSTEM_ERROR"}, + {PR_DIRECTORY_NOT_EMPTY_ERROR, "PR_DIRECTORY_NOT_EMPTY_ERROR"}, + {PR_FILESYSTEM_MOUNTED_ERROR, "PR_FILESYSTEM_MOUNTED_ERROR"}, + {PR_NOT_SAME_DEVICE_ERROR, "PR_NOT_SAME_DEVICE_ERROR"}, + {PR_DIRECTORY_CORRUPTED_ERROR, "PR_DIRECTORY_CORRUPTED_ERROR"}, + {PR_FILE_EXISTS_ERROR, "PR_FILE_EXISTS_ERROR"}, + {PR_MAX_DIRECTORY_ENTRIES_ERROR, "PR_MAX_DIRECTORY_ENTRIES_ERROR"}, + {PR_INVALID_DEVICE_STATE_ERROR, "PR_INVALID_DEVICE_STATE_ERROR"}, + {PR_DEVICE_IS_LOCKED_ERROR, "PR_DEVICE_IS_LOCKED_ERROR"}, + {PR_NO_MORE_FILES_ERROR, "PR_NO_MORE_FILES_ERROR"}, + {PR_END_OF_FILE_ERROR, "PR_END_OF_FILE_ERROR"}, + {PR_FILE_SEEK_ERROR, "PR_FILE_SEEK_ERROR"}, + {PR_FILE_IS_BUSY_ERROR, "PR_FILE_IS_BUSY_ERROR"}, + {PR_IN_PROGRESS_ERROR, "PR_IN_PROGRESS_ERROR"}, + {PR_ALREADY_INITIATED_ERROR, "PR_ALREADY_INITIATED_ERROR"}, + {PR_GROUP_EMPTY_ERROR, "PR_GROUP_EMPTY_ERROR"}, + {PR_INVALID_STATE_ERROR, "PR_INVALID_STATE_ERROR"}, + {PR_NETWORK_DOWN_ERROR, "PR_NETWORK_DOWN_ERROR"}, + {PR_SOCKET_SHUTDOWN_ERROR, "PR_SOCKET_SHUTDOWN_ERROR"}, + {PR_CONNECT_ABORTED_ERROR, "PR_CONNECT_ABORTED_ERROR"}, + {PR_HOST_UNREACHABLE_ERROR, "PR_HOST_UNREACHABLE_ERROR"} +}; + +int main(int argc, char **argv) +{ + + int count, errnum; + + /* + * -d debug mode + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + count = sizeof(errcodes)/sizeof(errcodes[0]); + printf("\nNumber of error codes = %d\n\n",count); + for (errnum = 0; errnum < count; errnum++) { + printf("%-40s = %d\n",errcodes[errnum].errname, + errcodes[errnum].errcode); + } + + return 0; +} diff --git a/nsprpub/pr/tests/errset.c b/nsprpub/pr/tests/errset.c new file mode 100644 index 0000000000..fb76a6fa9d --- /dev/null +++ b/nsprpub/pr/tests/errset.c @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: errset.c +** +** Description: errset.c exercises the functions in prerror.c. +** This code is a unit test of the prerror.c capability. +** +** Note: There's some fluff in here. The guts of the test +** were plagerized from another test. So, sue me. +** +** +*/ +#include "prerror.h" +#include "plgetopt.h" +#include "prlog.h" + +#include <stdio.h> +#include <string.h> + +static int _debug_on = 0; + +struct errinfo { + PRErrorCode errcode; + char *errname; +}; + +struct errinfo errcodes[] = { + {PR_OUT_OF_MEMORY_ERROR, "PR_OUT_OF_MEMORY_ERROR"}, + {PR_UNKNOWN_ERROR, "An intentionally long error message text intended to force a delete of the current errorString buffer and get another one."}, + {PR_BAD_DESCRIPTOR_ERROR, "PR_BAD_DESCRIPTOR_ERROR"}, + {PR_WOULD_BLOCK_ERROR, "PR_WOULD_BLOCK_ERROR"}, + {PR_ACCESS_FAULT_ERROR, "PR_ACCESS_FAULT_ERROR"}, + {PR_INVALID_METHOD_ERROR, "PR_INVALID_METHOD_ERROR"}, + {PR_ILLEGAL_ACCESS_ERROR, "PR_ILLEGAL_ACCESS_ERROR"}, + {PR_UNKNOWN_ERROR, "PR_UNKNOWN_ERROR"}, + {PR_PENDING_INTERRUPT_ERROR, "PR_PENDING_INTERRUPT_ERROR"}, + {PR_NOT_IMPLEMENTED_ERROR, "PR_NOT_IMPLEMENTED_ERROR"}, + {PR_IO_ERROR, "PR_IO_ERROR"}, + {PR_IO_TIMEOUT_ERROR, "PR_IO_TIMEOUT_ERROR"}, + {PR_IO_PENDING_ERROR, "PR_IO_PENDING_ERROR"}, + {PR_DIRECTORY_OPEN_ERROR, "PR_DIRECTORY_OPEN_ERROR"}, + {PR_INVALID_ARGUMENT_ERROR, "PR_INVALID_ARGUMENT_ERROR"}, + {PR_ADDRESS_NOT_AVAILABLE_ERROR, "PR_ADDRESS_NOT_AVAILABLE_ERROR"}, + {PR_ADDRESS_NOT_SUPPORTED_ERROR, "PR_ADDRESS_NOT_SUPPORTED_ERROR"}, + {PR_IS_CONNECTED_ERROR, "PR_IS_CONNECTED_ERROR"}, + {PR_BAD_ADDRESS_ERROR, "PR_BAD_ADDRESS_ERROR"}, + {PR_ADDRESS_IN_USE_ERROR, "PR_ADDRESS_IN_USE_ERROR"}, + {PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"}, + {PR_NETWORK_UNREACHABLE_ERROR, "PR_NETWORK_UNREACHABLE_ERROR"}, + {PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"}, + {PR_NOT_CONNECTED_ERROR, "PR_NOT_CONNECTED_ERROR"}, + {PR_LOAD_LIBRARY_ERROR, "PR_LOAD_LIBRARY_ERROR"}, + {PR_UNLOAD_LIBRARY_ERROR, "PR_UNLOAD_LIBRARY_ERROR"}, + {PR_FIND_SYMBOL_ERROR, "PR_FIND_SYMBOL_ERROR"}, + {PR_INSUFFICIENT_RESOURCES_ERROR, "PR_INSUFFICIENT_RESOURCES_ERROR"}, + {PR_DIRECTORY_LOOKUP_ERROR, "PR_DIRECTORY_LOOKUP_ERROR"}, + {PR_TPD_RANGE_ERROR, "PR_TPD_RANGE_ERROR"}, + {PR_PROC_DESC_TABLE_FULL_ERROR, "PR_PROC_DESC_TABLE_FULL_ERROR"}, + {PR_SYS_DESC_TABLE_FULL_ERROR, "PR_SYS_DESC_TABLE_FULL_ERROR"}, + {PR_NOT_SOCKET_ERROR, "PR_NOT_SOCKET_ERROR"}, + {PR_NOT_TCP_SOCKET_ERROR, "PR_NOT_TCP_SOCKET_ERROR"}, + {PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "PR_SOCKET_ADDRESS_IS_BOUND_ERROR"}, + {PR_NO_ACCESS_RIGHTS_ERROR, "PR_NO_ACCESS_RIGHTS_ERROR"}, + {PR_OPERATION_NOT_SUPPORTED_ERROR, "PR_OPERATION_NOT_SUPPORTED_ERROR"}, + {PR_PROTOCOL_NOT_SUPPORTED_ERROR, "PR_PROTOCOL_NOT_SUPPORTED_ERROR"}, + {PR_REMOTE_FILE_ERROR, "PR_REMOTE_FILE_ERROR"}, + {PR_BUFFER_OVERFLOW_ERROR, "PR_BUFFER_OVERFLOW_ERROR"}, + {PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"}, + {PR_RANGE_ERROR, "PR_RANGE_ERROR"}, + {PR_DEADLOCK_ERROR, "PR_DEADLOCK_ERROR"}, + {PR_FILE_IS_LOCKED_ERROR, "PR_FILE_IS_LOCKED_ERROR"}, + {PR_FILE_TOO_BIG_ERROR, "PR_FILE_TOO_BIG_ERROR"}, + {PR_NO_DEVICE_SPACE_ERROR, "PR_NO_DEVICE_SPACE_ERROR"}, + {PR_PIPE_ERROR, "PR_PIPE_ERROR"}, + {PR_NO_SEEK_DEVICE_ERROR, "PR_NO_SEEK_DEVICE_ERROR"}, + {PR_IS_DIRECTORY_ERROR, "PR_IS_DIRECTORY_ERROR"}, + {PR_LOOP_ERROR, "PR_LOOP_ERROR"}, + {PR_NAME_TOO_LONG_ERROR, "PR_NAME_TOO_LONG_ERROR"}, + {PR_FILE_NOT_FOUND_ERROR, "PR_FILE_NOT_FOUND_ERROR"}, + {PR_NOT_DIRECTORY_ERROR, "PR_NOT_DIRECTORY_ERROR"}, + {PR_READ_ONLY_FILESYSTEM_ERROR, "PR_READ_ONLY_FILESYSTEM_ERROR"}, + {PR_DIRECTORY_NOT_EMPTY_ERROR, "PR_DIRECTORY_NOT_EMPTY_ERROR"}, + {PR_FILESYSTEM_MOUNTED_ERROR, "PR_FILESYSTEM_MOUNTED_ERROR"}, + {PR_NOT_SAME_DEVICE_ERROR, "PR_NOT_SAME_DEVICE_ERROR"}, + {PR_DIRECTORY_CORRUPTED_ERROR, "PR_DIRECTORY_CORRUPTED_ERROR"}, + {PR_FILE_EXISTS_ERROR, "PR_FILE_EXISTS_ERROR"}, + {PR_MAX_DIRECTORY_ENTRIES_ERROR, "PR_MAX_DIRECTORY_ENTRIES_ERROR"}, + {PR_INVALID_DEVICE_STATE_ERROR, "PR_INVALID_DEVICE_STATE_ERROR"}, + {PR_DEVICE_IS_LOCKED_ERROR, "PR_DEVICE_IS_LOCKED_ERROR"}, + {PR_NO_MORE_FILES_ERROR, "PR_NO_MORE_FILES_ERROR"}, + {PR_END_OF_FILE_ERROR, "PR_END_OF_FILE_ERROR"}, + {PR_FILE_SEEK_ERROR, "PR_FILE_SEEK_ERROR"}, + {PR_FILE_IS_BUSY_ERROR, "PR_FILE_IS_BUSY_ERROR"}, + {PR_IN_PROGRESS_ERROR, "PR_IN_PROGRESS_ERROR"}, + {PR_ALREADY_INITIATED_ERROR, "PR_ALREADY_INITIATED_ERROR"}, + {PR_GROUP_EMPTY_ERROR, "PR_GROUP_EMPTY_ERROR"}, + {PR_INVALID_STATE_ERROR, "PR_INVALID_STATE_ERROR"}, + {PR_NETWORK_DOWN_ERROR, "PR_NETWORK_DOWN_ERROR"}, + {PR_SOCKET_SHUTDOWN_ERROR, "PR_SOCKET_SHUTDOWN_ERROR"}, + {PR_CONNECT_ABORTED_ERROR, "PR_CONNECT_ABORTED_ERROR"}, + {PR_HOST_UNREACHABLE_ERROR, "PR_HOST_UNREACHABLE_ERROR"} +}; + +int main(int argc, char **argv) +{ + + int count, errnum; + + /* + * -d debug mode + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + count = sizeof(errcodes)/sizeof(errcodes[0]); + printf("\nNumber of error codes = %d\n\n",count); + for (errnum = 0; errnum < count; errnum++) { + PRInt32 len1, len2, err; + char msg[256]; + + PR_SetError( errnum, -5 ); + err = PR_GetError(); + PR_ASSERT( err == errnum ); + err = PR_GetOSError(); + PR_ASSERT( err == -5 ); + PR_SetErrorText( strlen(errcodes[errnum].errname), errcodes[errnum].errname ); + len1 = PR_GetErrorTextLength(); + len2 = PR_GetErrorText( msg ); + PR_ASSERT( len1 == len2 ); + printf("%5.5d -- %s\n", errnum, msg ); + } + + return 0; +} diff --git a/nsprpub/pr/tests/exit.c b/nsprpub/pr/tests/exit.c new file mode 100644 index 0000000000..dd3d44427e --- /dev/null +++ b/nsprpub/pr/tests/exit.c @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prprf.h" +#include "prinit.h" +#include "prthread.h" +#include "prproces.h" +#include "prinrval.h" + +#include "plgetopt.h" + +#include <stdlib.h> + +static PRInt32 dally = 0; +static PRFileDesc *err = NULL; +static PRBool verbose = PR_FALSE, force = PR_FALSE; + +static void Help(void) +{ + PR_fprintf(err, "Usage: [-t s] [-h]\n"); + PR_fprintf(err, "\t-d Verbose output (default: FALSE)\n"); + PR_fprintf(err, "\t-x Forced termination (default: FALSE)\n"); + PR_fprintf(err, "\t-t Time for thread to block (default: 10 seconds)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +static void Dull(void *arg) +{ + PR_Sleep(PR_SecondsToInterval(dally)); + if (verbose && force) { + PR_fprintf(err, "If you see this, the test failed\n"); + } +} /* Dull */ + +static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "ht:dx"); + + err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* verbosity */ + verbose = PR_TRUE; + break; + case 'x': /* force exit */ + force = PR_TRUE; + break; + case 't': /* seconds to dally in child */ + dally = atoi(opt->value); + break; + case 'h': /* user wants some guidance */ + default: + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + + if (0 == dally) { + dally = 10; + } + + /* + * Create LOCAL and GLOBAL threads + */ + (void)PR_CreateThread( + PR_USER_THREAD, Dull, NULL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); + + (void)PR_CreateThread( + PR_USER_THREAD, Dull, NULL, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); + + if (verbose) + PR_fprintf( + err, "Main is exiting now. Program should exit %s.\n", + (force) ? "immediately" : "after child dally time"); + + if (force) + { + PR_ProcessExit(0); + if (verbose) + { + PR_fprintf(err, "You should not have gotten here.\n"); + return 1; + } + } + return 0; + +} + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/fdcach.c b/nsprpub/pr/tests/fdcach.c new file mode 100644 index 0000000000..5fa51b706a --- /dev/null +++ b/nsprpub/pr/tests/fdcach.c @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: fdcach.c + * Description: + * This test verifies that the fd cache is working + * correctly. + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> + +/* + * Define ORDER_PRESERVED if the implementation of PR_SetFDCacheSize + * preserves the ordering of the fd's when moving them between the + * cache. + */ +#define ORDER_PRESERVED 1 + +/* + * NUM_FDS must be <= FD_CACHE_SIZE. + */ +#define FD_CACHE_SIZE 1024 +#define NUM_FDS 20 + +int main(int argc, char **argv) +{ + int i; + PRFileDesc *fds[NUM_FDS]; + PRFileDesc *savefds[NUM_FDS]; + int numfds = sizeof(fds)/sizeof(fds[0]); + + PR_SetFDCacheSize(0, FD_CACHE_SIZE); + + /* Add some fd's to the fd cache. */ + for (i = 0; i < numfds; i++) { + savefds[i] = PR_NewTCPSocket(); + if (NULL == savefds[i]) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + } + for (i = 0; i < numfds; i++) { + if (PR_Close(savefds[i]) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + } + + /* + * Create some fd's. These fd's should come from + * the fd cache. Verify the FIFO ordering of the fd + * cache. + */ + for (i = 0; i < numfds; i++) { + fds[i] = PR_NewTCPSocket(); + if (NULL == fds[i]) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + if (fds[i] != savefds[i]) { + fprintf(stderr, "fd cache malfunctioned\n"); + exit(1); + } + } + /* Put the fd's back to the fd cache. */ + for (i = 0; i < numfds; i++) { + if (PR_Close(savefds[i]) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + } + + /* Switch to the fd cache. */ + PR_SetFDCacheSize(0, FD_CACHE_SIZE); + + for (i = 0; i < numfds; i++) { + fds[i] = PR_NewTCPSocket(); + if (NULL == fds[i]) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } +#ifdef ORDER_PRESERVED + if (fds[i] != savefds[i]) { + fprintf(stderr, "fd cache malfunctioned\n"); + exit(1); + } +#else + savefds[i] = fds[i]; +#endif + } + for (i = 0; i < numfds; i++) { + if (PR_Close(savefds[i]) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + } + + for (i = 0; i < numfds; i++) { + fds[i] = PR_NewTCPSocket(); + if (NULL == fds[i]) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + if (fds[i] != savefds[i]) { + fprintf(stderr, "fd cache malfunctioned\n"); + exit(1); + } + } + for (i = 0; i < numfds; i++) { + if (PR_Close(savefds[i]) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + } + + PR_Cleanup(); + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/fileio.c b/nsprpub/pr/tests/fileio.c new file mode 100644 index 0000000000..a0be3bb10a --- /dev/null +++ b/nsprpub/pr/tests/fileio.c @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: fileio.c +** +** Description: Program to copy one file to another. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 Revert to return code 0 and 1, remove debug option (obsolete). +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +#include "prinit.h" +#include "prthread.h" +#include "prlock.h" +#include "prcvar.h" +#include "prmon.h" +#include "prmem.h" +#include "prio.h" +#include "prlog.h" + +#include <stdio.h> + +#include "obsolete/prsem.h" + + +#define TBSIZE 1024 + +static PRUint8 tbuf[TBSIZE]; + +static PRFileDesc *t1, *t2; + +PRIntn failed_already=0; +PRIntn debug_mode; +static void InitialSetup(void) +{ + PRUintn i; + PRInt32 nWritten, rv; + + t1 = PR_Open("t1.tmp", PR_CREATE_FILE | PR_RDWR, 0); + PR_ASSERT(t1 != NULL); + + for (i=0; i<TBSIZE; i++) { + tbuf[i] = i; + } + + nWritten = PR_Write((PRFileDesc*)t1, tbuf, TBSIZE); + PR_ASSERT(nWritten == TBSIZE); + + rv = PR_Seek(t1,0,PR_SEEK_SET); + PR_ASSERT(rv == 0); + + t2 = PR_Open("t2.tmp", PR_CREATE_FILE | PR_RDWR, 0); + PR_ASSERT(t2 != NULL); +} + + +static void VerifyAndCleanup(void) +{ + PRUintn i; + PRInt32 nRead, rv; + + for (i=0; i<TBSIZE; i++) { + tbuf[i] = 0; + } + + rv = PR_Seek(t2,0,PR_SEEK_SET); + PR_ASSERT(rv == 0); + + nRead = PR_Read((PRFileDesc*)t2, tbuf, TBSIZE); + PR_ASSERT(nRead == TBSIZE); + + for (i=0; i<TBSIZE; i++) + if (tbuf[i] != (PRUint8)i) { + if (debug_mode) { + printf("data mismatch for index= %d \n", i); + } + else { + failed_already=1; + } + } + PR_Close(t1); + PR_Close(t2); + + PR_Delete("t1.tmp"); + PR_Delete("t2.tmp"); + + if (debug_mode) { + printf("fileio test passed\n"); + } +} + + +/*------------------ Following is the real test program ---------*/ +/* + Program to copy one file to another. Two temporary files get + created. First one gets written in one write call. Then, + a reader thread reads from this file into a double buffer. + The writer thread writes from double buffer into the other + temporary file. The second temporary file gets verified + for accurate data. +*/ + +PRSemaphore *emptyBufs; /* number of empty buffers */ +PRSemaphore *fullBufs; /* number of buffers that are full */ + +#define BSIZE 100 + +struct { + char data[BSIZE]; + PRUintn nbytes; /* number of bytes in this buffer */ +} buf[2]; + +static void PR_CALLBACK reader(void *arg) +{ + PRUintn i = 0; + PRInt32 nbytes; + + do { + (void) PR_WaitSem(emptyBufs); + nbytes = PR_Read((PRFileDesc*)arg, buf[i].data, BSIZE); + if (nbytes >= 0) { + buf[i].nbytes = nbytes; + PR_PostSem(fullBufs); + i = (i + 1) % 2; + } + } while (nbytes > 0); +} + +static void PR_CALLBACK writer(void *arg) +{ + PRUintn i = 0; + PRInt32 nbytes; + + do { + (void) PR_WaitSem(fullBufs); + nbytes = buf[i].nbytes; + if (nbytes > 0) { + nbytes = PR_Write((PRFileDesc*)arg, buf[i].data, nbytes); + PR_PostSem(emptyBufs); + i = (i + 1) % 2; + } + } while (nbytes > 0); +} + +int main(int argc, char **argv) +{ + PRThread *r, *w; + + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + emptyBufs = PR_NewSem(2); /* two empty buffers */ + + fullBufs = PR_NewSem(0); /* zero full buffers */ + + /* Create initial temp file setup */ + InitialSetup(); + + /* create the reader thread */ + + r = PR_CreateThread(PR_USER_THREAD, + reader, t1, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0); + + w = PR_CreateThread(PR_USER_THREAD, + writer, t2, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0); + + /* Do the joining for both threads */ + (void) PR_JoinThread(r); + (void) PR_JoinThread(w); + + /* Do the verification and clean up */ + VerifyAndCleanup(); + + PR_DestroySem(emptyBufs); + PR_DestroySem(fullBufs); + + PR_Cleanup(); + + if(failed_already) + { + printf("Fail\n"); + return 1; + } + else + { + printf("PASS\n"); + return 0; + } + + +} diff --git a/nsprpub/pr/tests/foreign.c b/nsprpub/pr/tests/foreign.c new file mode 100644 index 0000000000..a1f5b5cbcc --- /dev/null +++ b/nsprpub/pr/tests/foreign.c @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: foreign.c +** Description: Testing various functions w/ foreign threads +** +** We create a thread and get it to call exactly one runtime function. +** The thread is allowed to be created by some other environment that +** NSPR, but it does not announce itself to the runtime prior to calling +** in. +** +** The goal: try to survive. +** +*/ + +#include "prcvar.h" +#include "prenv.h" +#include "prerror.h" +#include "prinit.h" +#include "prinrval.h" +#include "prio.h" +#include "prlock.h" +#include "prlog.h" +#include "prmem.h" +#include "prthread.h" +#include "prtypes.h" +#include "prprf.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> + +static enum { + thread_nspr, thread_pthread, thread_sproc, thread_win32 +} thread_provider; + +typedef void (*StartFn)(void*); +typedef struct StartObject +{ + StartFn start; + void *arg; +} StartObject; + +static PRFileDesc *output; + +static int _debug_on = 0; + +#define DEFAULT_THREAD_COUNT 10 + +#define DPRINTF(arg) if (_debug_on) PR_fprintf arg + +#if defined(_PR_PTHREADS) +#include <pthread.h> +#include "md/_pth.h" +static void *pthread_start(void *arg) +{ + StartFn start = ((StartObject*)arg)->start; + void *data = ((StartObject*)arg)->arg; + PR_Free(arg); + start(data); + return NULL; +} /* pthread_start */ +#endif /* defined(_PR_PTHREADS) */ + +#if defined(WIN32) +#include <windows.h> +#include <process.h> /* for _beginthreadex() */ + +static PRUintn __stdcall windows_start(void *arg) +{ + StartObject *so = (StartObject*)arg; + StartFn start = so->start; + void *data = so->arg; + PR_Free(so); + start(data); + return 0; +} /* windows_start */ +#endif /* defined(WIN32) */ + +static PRStatus NSPRPUB_TESTS_CreateThread(StartFn start, void *arg) +{ + PRStatus rv; + + switch (thread_provider) + { + case thread_nspr: + { + PRThread *thread = PR_CreateThread( + PR_USER_THREAD, start, arg, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS; + } + break; + case thread_pthread: +#if defined(_PR_PTHREADS) + { + int rv; + pthread_t id; + pthread_attr_t tattr; + StartObject *start_object; + start_object = PR_NEW(StartObject); + PR_ASSERT(NULL != start_object); + start_object->start = start; + start_object->arg = arg; + + rv = _PT_PTHREAD_ATTR_INIT(&tattr); + PR_ASSERT(0 == rv); + + rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + PR_ASSERT(0 == rv); + + rv = pthread_attr_setstacksize(&tattr, 64 * 1024); + PR_ASSERT(0 == rv); + + rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object); + (void)_PT_PTHREAD_ATTR_DESTROY(&tattr); + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; + } +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; + break; +#endif /* defined(_PR_PTHREADS) */ + + case thread_sproc: + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; + break; + case thread_win32: +#if defined(WIN32) + { + void *th; + PRUintn id; + StartObject *start_object; + start_object = PR_NEW(StartObject); + PR_ASSERT(NULL != start_object); + start_object->start = start; + start_object->arg = arg; + th = (void*)_beginthreadex( + NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */ + 0U, /* DWORD - initial thread stack size, in bytes */ + windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */ + start_object, /* LPVOID - argument for new thread */ + STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */ + &id /* LPDWORD - pointer to returned thread identifier */ ); + + rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS; + } +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; +#endif + break; + default: + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; + } + return rv; +} /* NSPRPUB_TESTS_CreateThread */ + +static void PR_CALLBACK lazyEntry(void *arg) +{ + PR_ASSERT(NULL == arg); +} /* lazyEntry */ + + +static void OneShot(void *arg) +{ + PRUintn pdkey; + PRLock *lock; + PRFileDesc *fd; + PRDir *dir; + PRFileDesc *pair[2]; + PRIntn test = (PRIntn)arg; + + for (test = 0; test < 12; ++test) { + + switch (test) + { + case 0: + lock = PR_NewLock(); + DPRINTF((output,"Thread[0x%x] called PR_NewLock\n", + PR_GetCurrentThread())); + PR_DestroyLock(lock); + break; + + case 1: + (void)PR_SecondsToInterval(1); + DPRINTF((output,"Thread[0x%x] called PR_SecondsToInterval\n", + PR_GetCurrentThread())); + break; + + case 2: (void)PR_CreateThread( + PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); + DPRINTF((output,"Thread[0x%x] called PR_CreateThread\n", + PR_GetCurrentThread())); + break; + + case 3: + fd = PR_Open("foreign.tmp", PR_CREATE_FILE | PR_RDWR, 0666); + DPRINTF((output,"Thread[0x%x] called PR_Open\n", + PR_GetCurrentThread())); + PR_Close(fd); + break; + + case 4: + fd = PR_NewUDPSocket(); + DPRINTF((output,"Thread[0x%x] called PR_NewUDPSocket\n", + PR_GetCurrentThread())); + PR_Close(fd); + break; + + case 5: + fd = PR_NewTCPSocket(); + DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocket\n", + PR_GetCurrentThread())); + PR_Close(fd); + break; + + case 6: +#define TEMP_DIR "./tmp" + PR_MkDir(TEMP_DIR, 0700); + dir = PR_OpenDir(TEMP_DIR); + DPRINTF((output,"Thread[0x%x] called PR_OpenDir\n", + PR_GetCurrentThread())); + PR_CloseDir(dir); + break; + + case 7: + (void)PR_NewThreadPrivateIndex(&pdkey, NULL); + DPRINTF((output,"Thread[0x%x] called PR_NewThreadPrivateIndex\n", + PR_GetCurrentThread())); + break; + + case 8: + (void)PR_GetEnv("PATH"); + DPRINTF((output,"Thread[0x%x] called PR_GetEnv\n", + PR_GetCurrentThread())); + break; + + case 9: + (void)PR_NewTCPSocketPair(pair); + DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocketPair\n", + PR_GetCurrentThread())); + PR_Close(pair[0]); + PR_Close(pair[1]); + break; + + case 10: + PR_SetConcurrency(2); + DPRINTF((output,"Thread[0x%x] called PR_SetConcurrency\n", + PR_GetCurrentThread())); + break; + + case 11: + PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_HIGH); + DPRINTF((output,"Thread[0x%x] called PR_SetThreadPriority\n", + PR_GetCurrentThread())); + break; + + default: + break; + } /* switch() */ + } +} /* OneShot */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PRInt32 thread_cnt = DEFAULT_THREAD_COUNT; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dt:"); + +#if defined(WIN32) + thread_provider = thread_win32; +#elif defined(_PR_PTHREADS) + thread_provider = thread_pthread; +#else + thread_provider = thread_nspr; +#endif + + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + case 't': /* thread count */ + thread_cnt = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_SetConcurrency(2); + + output = PR_GetSpecialFD(PR_StandardOutput); + + while (thread_cnt-- > 0) + { + rv = NSPRPUB_TESTS_CreateThread(OneShot, (void*)thread_cnt); + PR_ASSERT(PR_SUCCESS == rv); + PR_Sleep(PR_MillisecondsToInterval(5)); + } + PR_Sleep(PR_SecondsToInterval(3)); + return (PR_SUCCESS == PR_Cleanup()) ? 0 : 1; +} /* main */ + +/* foreign.c */ diff --git a/nsprpub/pr/tests/forktest.c b/nsprpub/pr/tests/forktest.c new file mode 100644 index 0000000000..a6d4bc4bb4 --- /dev/null +++ b/nsprpub/pr/tests/forktest.c @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: forktest.c +** +** Description: UNIX test for fork functions. +** +** Modification History: +** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** 12-June-97 AGarcic - Revert to return code 0 and 1, remove debug option (obsolete). +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +PRIntn failed_already=0; + +#ifdef XP_UNIX + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> + +static char *message = "Hello world!"; + +static void +ClientThreadFunc(void *arg) +{ + PRNetAddr addr; + PRFileDesc *sock = NULL; + PRInt32 tmp = (PRInt32)arg; + + /* + * Make sure the PR_Accept call will block + */ + + printf("Wait one second before connect\n"); + fflush(stdout); + PR_Sleep(PR_SecondsToInterval(1)); + + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = 0; + if ((sock = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "failed to create TCP socket: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + if (PR_Bind(sock, &addr) != PR_SUCCESS) { + fprintf(stderr, "PR_Bind failed: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + addr.inet.ip = PR_htonl(INADDR_LOOPBACK); + addr.inet.port = PR_htons((PRInt16)tmp); + printf("Connecting to port %hu\n", PR_ntohs(addr.inet.port)); + fflush(stdout); + if (PR_Connect(sock, &addr, PR_SecondsToInterval(5)) != + PR_SUCCESS) { + fprintf(stderr, "PR_Connect failed: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + printf("Writing message \"%s\"\n", message); + fflush(stdout); + if (PR_Send(sock, message, strlen(message) + 1, 0, PR_INTERVAL_NO_TIMEOUT) == + -1) { + fprintf(stderr, "PR_Send failed: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } +finish: + if (sock) { + PR_Close(sock); + } + return; +} + +/* + * DoIO -- + * This function creates a thread that acts as a client and itself. + * acts as a server. Then it joins the client thread. + */ +static void +DoIO(void) +{ + PRThread *clientThread; + PRFileDesc *listenSock = NULL; + PRFileDesc *sock = NULL; + PRNetAddr addr; + PRInt32 nBytes; + char buf[128]; + + listenSock = PR_NewTCPSocket(); + if (!listenSock) { + fprintf(stderr, "failed to create a TCP socket: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = 0; + if (PR_Bind(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "failed to bind socket: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "failed to get socket port number: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + clientThread = PR_CreateThread( PR_USER_THREAD, ClientThreadFunc, + (void *) PR_ntohs(addr.inet.port), PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "Cannot create client thread: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already = 1; + goto finish; + } + printf("Accepting connection at port %hu\n", PR_ntohs(addr.inet.port)); + fflush(stdout); + sock = PR_Accept(listenSock, &addr, PR_SecondsToInterval(5)); + if (!sock) { + fprintf(stderr, "PR_Accept failed: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + nBytes = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); + if (nBytes == -1) { + fprintf(stderr, "PR_Recv failed: error code %d\n", + PR_GetError()); + failed_already = 1; + goto finish; + } + + /* + * Make sure it has proper null byte to mark end of string + */ + + buf[sizeof(buf) - 1] = '\0'; + printf("Received \"%s\" from the client\n", buf); + fflush(stdout); + if (!strcmp(buf, message)) { + PR_JoinThread(clientThread); + + printf("The message is received correctly\n"); + fflush(stdout); + } else { + fprintf(stderr, "The message should be \"%s\"\n", + message); + failed_already = 1; + } + +finish: + if (listenSock) { + PR_Close(listenSock); + } + if (sock) { + PR_Close(sock); + } + return; +} + +int main(int argc, char **argv) +{ + pid_t pid; + + /* main test program */ + + DoIO(); + + pid = fork(); + + if (pid == (pid_t) -1) { + fprintf(stderr, "Fork failed: errno %d\n", errno); + failed_already=1; + return 1; + } else if (pid > 0) { + int childStatus; + + printf("Fork succeeded. Parent process continues.\n"); + DoIO(); + if (waitpid(pid, &childStatus, 0) != pid) { + { + fprintf(stderr, "waitpid failed: %d\n", errno); + failed_already = 1; + } + } else if (!WIFEXITED(childStatus) + || WEXITSTATUS(childStatus) != 0) { + failed_already = 1; + } + printf("Parent process exits.\n"); + if (!failed_already) { + printf("PASSED\n"); + } else { + printf("FAILED\n"); + } + return failed_already; + } else { + printf("Fork succeeded. Child process continues.\n"); + DoIO(); + printf("Child process exits.\n"); + return failed_already; + } +} + +#else /* XP_UNIX */ + +int main( int argc, + char *argv[] + ) +{ + + printf("The fork test is applicable to Unix only.\n"); + return 0; + +} + +#endif /* XP_UNIX */ diff --git a/nsprpub/pr/tests/formattm.c b/nsprpub/pr/tests/formattm.c new file mode 100644 index 0000000000..63355b28b7 --- /dev/null +++ b/nsprpub/pr/tests/formattm.c @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A test program for PR_FormatTime and PR_FormatTimeUSEnglish */ + +#include "prtime.h" + +#include <stdio.h> + +int main(int argc, char **argv) +{ + char buffer[256]; + char small_buffer[8]; + PRTime now; + PRExplodedTime tod; + + now = PR_Now(); + PR_ExplodeTime(now, PR_LocalTimeParameters, &tod); + + if (PR_FormatTime(buffer, sizeof(buffer), + "%a %b %d %H:%M:%S %Z %Y", &tod) != 0) { + printf("%s\n", buffer); + } else { + fprintf(stderr, "PR_FormatTime(buffer) failed\n"); + return 1; + } + + small_buffer[0] = '?'; + if (PR_FormatTime(small_buffer, sizeof(small_buffer), + "%a %b %d %H:%M:%S %Z %Y", &tod) == 0) { + if (small_buffer[0] != '\0') { + fprintf(stderr, "PR_FormatTime(small_buffer) did not output " + "an empty string on failure\n"); + return 1; + } + printf("%s\n", small_buffer); + } else { + fprintf(stderr, "PR_FormatTime(small_buffer) succeeded " + "unexpectedly\n"); + return 1; + } + + (void)PR_FormatTimeUSEnglish(buffer, sizeof(buffer), + "%a %b %d %H:%M:%S %Z %Y", &tod); + printf("%s\n", buffer); + + return 0; +} diff --git a/nsprpub/pr/tests/freeif.c b/nsprpub/pr/tests/freeif.c new file mode 100644 index 0000000000..1d38d0e31a --- /dev/null +++ b/nsprpub/pr/tests/freeif.c @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A test to see if the macros PR_DELETE and PR_FREEIF are + * properly defined. (See Bugzilla bug #39110.) + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> + +static void Noop(void) { } + +static void Fail(void) +{ + printf("FAIL\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int foo = 1; + char *ptr = NULL; + + /* this fails to compile with the old definition of PR_DELETE */ + if (foo) { + PR_DELETE(ptr); + } + else { + Noop(); + } + + /* this nests incorrectly with the old definition of PR_FREEIF */ + if (foo) { + PR_FREEIF(ptr); + } + else { + Fail(); + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/fsync.c b/nsprpub/pr/tests/fsync.c new file mode 100644 index 0000000000..3e0fb175c1 --- /dev/null +++ b/nsprpub/pr/tests/fsync.c @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prmem.h" +#include "prprf.h" +#include "prinrval.h" + +#include "plerror.h" +#include "plgetopt.h" + +static PRFileDesc *err = NULL; + +static void Help(void) +{ + PR_fprintf(err, "Usage: [-S] [-K <n>] [-h] <filename>\n"); + PR_fprintf(err, "\t-c Nuber of iterations (default: 10)\n"); + PR_fprintf(err, "\t-S Sync the file (default: FALSE)\n"); + PR_fprintf(err, "\t-K Size of file (K bytes) (default: 10)\n"); + PR_fprintf(err, "\t Name of file to write (default: ./tmp-sync.dat)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PLOptStatus os; + PRUint8 *buffer; + PRFileDesc *file = NULL; + const char *filename = "sync.dat"; + PRUint32 index, loops, iterations = 10, filesize = 10; + PRIntn flags = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE; + PLOptState *opt = PL_CreateOptState(argc, argv, "hSK:c:"); + PRIntervalTime time, total = 0, shortest = 0x7fffffff, longest = 0; + + err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: /* Name of file to create */ + filename = opt->value; + break; + case 'S': /* Use sych option on file */ + flags |= PR_SYNC; + break; + case 'K': /* Size of file to write */ + filesize = atoi(opt->value); + break; + case 'c': /* Number of iterations */ + iterations = atoi(opt->value); + break; + case 'h': /* user wants some guidance */ + default: /* user needs some guidance */ + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + + file = PR_Open(filename, flags, 0666); + if (NULL == file) + { + PL_FPrintError(err, "Failed to open file"); + return 1; + } + + buffer = (PRUint8*)PR_CALLOC(1024); + if (NULL == buffer) + { + PL_FPrintError(err, "Cannot allocate buffer"); + return 1; + } + + for (index = 0; index < sizeof(buffer); ++index) { + buffer[index] = (PRUint8)index; + } + + for (loops = 0; loops < iterations; ++loops) + { + time = PR_IntervalNow(); + for (index = 0; index < filesize; ++index) + { + PR_Write(file, buffer, 1024); + } + time = (PR_IntervalNow() - time); + + total += time; + if (time < shortest) { + shortest = time; + } + else if (time > longest) { + longest = time; + } + if (0 != PR_Seek(file, 0, PR_SEEK_SET)) + { + PL_FPrintError(err, "Rewinding file"); + return 1; + } + } + + total = total / iterations; + PR_fprintf( + err, "%u iterations over a %u kbyte %sfile: %u [%u] %u\n", + iterations, filesize, ((flags & PR_SYNC) ? "SYNCH'd " : ""), + PR_IntervalToMicroseconds(shortest), + PR_IntervalToMicroseconds(total), + PR_IntervalToMicroseconds(longest)); + + PR_DELETE(buffer); + rv = PR_Close(file); + if (PR_SUCCESS != rv) + { + PL_FPrintError(err, "Closing file failed"); + return 1; + } + rv = PR_Delete(filename); + if (PR_SUCCESS != rv) + { + PL_FPrintError(err, "Deleting file failed"); + return 1; + } + return 0; +} diff --git a/nsprpub/pr/tests/getai.c b/nsprpub/pr/tests/getai.c new file mode 100644 index 0000000000..d759ed827d --- /dev/null +++ b/nsprpub/pr/tests/getai.c @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) +{ + PRAddrInfo *ai; + void *iter; + PRNetAddr addr; + + ai = PR_GetAddrInfoByName(argv[1], PR_AF_UNSPEC, PR_AI_ADDRCONFIG); + if (ai == NULL) { + fprintf(stderr, "PR_GetAddrInfoByName failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("%s\n", PR_GetCanonNameFromAddrInfo(ai)); + iter = NULL; + while ((iter = PR_EnumerateAddrInfo(iter, ai, 0, &addr)) != NULL) { + char buf[128]; + PR_NetAddrToString(&addr, buf, sizeof buf); + printf("%s\n", buf); + } + PR_FreeAddrInfo(ai); + return 0; +} diff --git a/nsprpub/pr/tests/gethost.c b/nsprpub/pr/tests/gethost.c new file mode 100644 index 0000000000..71704a8454 --- /dev/null +++ b/nsprpub/pr/tests/gethost.c @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * 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 "mozilla.org" + +static void Help(void) +{ + fprintf(stderr, "Usage: gethost [-h] [hostname]\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, "h"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) { + 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); + 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); + idx = 0; + printf("PR_GetHostByAddr with PR_AF_INET6\n"); + 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_GetHostByAddr with PR_AF_INET6 done\n"); + + PR_StringToNetAddr("::1", &addr); + if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped) == PR_TRUE) { + fprintf(stderr, "addr should not be ipv4 mapped address\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + + PR_StringToNetAddr("127.0.0.1", &addr); + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + PR_StringToNetAddr("::FFFF:127.0.0.1", &addr); + if (PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped) == PR_FALSE) { + fprintf(stderr, "addr should be ipv4 mapped address\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + + 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); + } + { + char buf[256]; + PR_NetAddrToString(&addr, buf, 256); + printf("IPv4 INADDRANY: %s\n", buf); + } + 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); + } + { + char buf[256]; + PR_NetAddrToString(&addr, buf, 256); + printf("IPv4 LOOPBACK: %s\n", buf); + } + + 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); + } + { + char buf[256]; + PR_NetAddrToString(&addr, buf, 256); + printf("IPv6 INADDRANY: %s\n", buf); + } + 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); + } + { + char buf[256]; + PR_NetAddrToString(&addr, buf, 256); + printf("IPv6 LOOPBACK: %s\n", buf); + } + { + PRIPv6Addr v6addr; + char tmp_buf[256]; + + PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET, 0, &addr); + + PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &v6addr); + PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr); + addr.ipv6.ip = v6addr; + PR_NetAddrToString(&addr, tmp_buf, 256); + printf("IPv4-mapped IPv6 LOOPBACK: %s\n", tmp_buf); + } + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/getproto.c b/nsprpub/pr/tests/getproto.c new file mode 100644 index 0000000000..1a33b1f256 --- /dev/null +++ b/nsprpub/pr/tests/getproto.c @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + ************************************************************************* + * + * File: getproto.c + * + * A test program for PR_GetProtoByName and PR_GetProtoByNumber + * + ************************************************************************* + */ + +#include "plstr.h" +#include "plerror.h" +#include "prinit.h" +#include "prprf.h" +#include "prnetdb.h" +#include "prerror.h" + +int main(int argc, char **argv) +{ + PRFileDesc *prstderr = PR_GetSpecialFD(PR_StandardError); + PRBool failed = PR_FALSE; + PRProtoEnt proto; + char buf[2048]; + PRStatus rv; + + PR_STDIO_INIT(); + rv = PR_GetProtoByName("tcp", buf, sizeof(buf), &proto); + if (PR_FAILURE == rv) { + failed = PR_TRUE; + PL_FPrintError(prstderr, "PR_GetProtoByName failed"); + } + else if (6 != proto.p_num) { + PR_fprintf( + prstderr,"tcp is usually 6, but is %d on this machine\n", + proto.p_num); + } + else { + PR_fprintf(prstderr, "tcp is protocol number %d\n", proto.p_num); + } + + rv = PR_GetProtoByName("udp", buf, sizeof(buf), &proto); + if (PR_FAILURE == rv) { + failed = PR_TRUE; + PL_FPrintError(prstderr, "PR_GetProtoByName failed"); + } + else if (17 != proto.p_num) { + PR_fprintf( + prstderr, "udp is usually 17, but is %d on this machine\n", + proto.p_num); + } + else { + PR_fprintf(prstderr, "udp is protocol number %d\n", proto.p_num); + } + + rv = PR_GetProtoByNumber(6, buf, sizeof(buf), &proto); + if (PR_FAILURE == rv) { + failed = PR_TRUE; + PL_FPrintError(prstderr, "PR_GetProtoByNumber failed"); + } + else if (PL_strcmp("tcp", proto.p_name)) { + PR_fprintf( + prstderr, "Protocol number 6 is usually tcp, but is %s" + " on this platform\n", proto.p_name); + } + else { + PR_fprintf(prstderr, "Protocol number 6 is %s\n", proto.p_name); + } + + rv = PR_GetProtoByNumber(17, buf, sizeof(buf), &proto); + if (PR_FAILURE == rv) { + failed = PR_TRUE; + PL_FPrintError(prstderr, "PR_GetProtoByNumber failed"); + } + else if (PL_strcmp("udp", proto.p_name)) { + PR_fprintf( + prstderr, "Protocol number 17 is usually udp, but is %s" + " on this platform\n", proto.p_name); + } + else { + PR_fprintf(prstderr, "Protocol number 17 is %s\n", proto.p_name); + } + + PR_fprintf(prstderr, (failed) ? "FAILED\n" : "PASSED\n"); + return (failed) ? 1 : 0; +} diff --git a/nsprpub/pr/tests/i2l.c b/nsprpub/pr/tests/i2l.c new file mode 100644 index 0000000000..a28cd16a80 --- /dev/null +++ b/nsprpub/pr/tests/i2l.c @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdlib.h> + +#include "prio.h" +#include "prinit.h" +#include "prprf.h" +#include "prlong.h" + +#include "plerror.h" +#include "plgetopt.h" + +typedef union Overlay_i +{ + PRInt32 i; + PRInt64 l; +} Overlay_i; + +typedef union Overlay_u +{ + PRUint32 i; + PRUint64 l; +} Overlay_u; + +static PRFileDesc *err = NULL; + +static void Help(void) +{ + PR_fprintf(err, "Usage: -i n | -u n | -h\n"); + PR_fprintf(err, "\t-i n treat following number as signed integer\n"); + PR_fprintf(err, "\t-u n treat following number as unsigned integer\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv) +{ + Overlay_i si; + Overlay_u ui; + PLOptStatus os; + PRBool bsi = PR_FALSE, bui = PR_FALSE; + PLOptState *opt = PL_CreateOptState(argc, argv, "hi:u:"); + err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'i': /* signed integer */ + si.i = (PRInt32)atoi(opt->value); + bsi = PR_TRUE; + break; + case 'u': /* unsigned */ + ui.i = (PRUint32)atoi(opt->value); + bui = PR_TRUE; + break; + case 'h': /* user wants some guidance */ + default: + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + +#if defined(HAVE_LONG_LONG) + PR_fprintf(err, "We have long long\n"); +#else + PR_fprintf(err, "We don't have long long\n"); +#endif + + if (bsi) + { + PR_fprintf(err, "Converting %ld: ", si.i); + LL_I2L(si.l, si.i); + PR_fprintf(err, "%lld\n", si.l); + } + + if (bui) + { + PR_fprintf(err, "Converting %lu: ", ui.i); + LL_I2L(ui.l, ui.i); + PR_fprintf(err, "%llu\n", ui.l); + } + return 0; + +} /* main */ + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ + +/* i2l.c */ diff --git a/nsprpub/pr/tests/initclk.c b/nsprpub/pr/tests/initclk.c new file mode 100644 index 0000000000..65d8b17e71 --- /dev/null +++ b/nsprpub/pr/tests/initclk.c @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This is a regression test for the bug that the interval timer + * is not initialized when _PR_CreateCPU calls PR_IntervalNow. + * The bug would make this test program finish prematurely, + * when the SHORT_TIMEOUT period expires. The correct behavior + * is for the test to finish when the LONG_TIMEOUT period expires. + */ + +#include "prlock.h" +#include "prcvar.h" +#include "prthread.h" +#include "prinrval.h" +#include "prlog.h" +#include <stdio.h> +#include <stdlib.h> + +/* The timeouts, in milliseconds */ +#define SHORT_TIMEOUT 1000 +#define LONG_TIMEOUT 3000 + +PRLock *lock1, *lock2; +PRCondVar *cv1, *cv2; + +void ThreadFunc(void *arg) +{ + PR_Lock(lock1); + PR_WaitCondVar(cv1, PR_MillisecondsToInterval(SHORT_TIMEOUT)); + PR_Unlock(lock1); +} + +int main(int argc, char **argv) +{ + PRThread *thread; + PRIntervalTime start, end; + PRUint32 elapsed_ms; + + lock1 = PR_NewLock(); + PR_ASSERT(NULL != lock1); + cv1 = PR_NewCondVar(lock1); + PR_ASSERT(NULL != cv1); + lock2 = PR_NewLock(); + PR_ASSERT(NULL != lock2); + cv2 = PR_NewCondVar(lock2); + PR_ASSERT(NULL != cv2); + start = PR_IntervalNow(); + thread = PR_CreateThread( + PR_USER_THREAD, + ThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0); + PR_ASSERT(NULL != thread); + PR_Lock(lock2); + PR_WaitCondVar(cv2, PR_MillisecondsToInterval(LONG_TIMEOUT)); + PR_Unlock(lock2); + PR_JoinThread(thread); + end = PR_IntervalNow(); + elapsed_ms = PR_IntervalToMilliseconds((PRIntervalTime)(end - start)); + /* Allow 100ms imprecision */ + if (elapsed_ms < LONG_TIMEOUT - 100 || elapsed_ms > LONG_TIMEOUT + 100) { + printf("Elapsed time should be %u ms but is %u ms\n", + LONG_TIMEOUT, elapsed_ms); + printf("FAIL\n"); + exit(1); + } + printf("Elapsed time: %u ms, expected time: %u ms\n", + LONG_TIMEOUT, elapsed_ms); + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/inrval.c b/nsprpub/pr/tests/inrval.c new file mode 100644 index 0000000000..52f7b71da1 --- /dev/null +++ b/nsprpub/pr/tests/inrval.c @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** file: inrval.c +** description: Interval conversion test. +** Modification History: +** 15-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +**/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "obsolete/pralarm.h" + +#include "prio.h" +#include "prprf.h" +#include "prlock.h" +#include "prlong.h" +#include "prcvar.h" +#include "prinrval.h" +#include "prtime.h" + +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> + +static PRIntn debug_mode; +static PRFileDesc *output; + + +static void TestConversions(void) +{ + PRIntervalTime ticks = PR_TicksPerSecond(); + + if (debug_mode) { + PR_fprintf(output, "PR_TicksPerSecond: %ld\n\n", ticks); + PR_fprintf(output, "PR_SecondsToInterval(1): %ld\n", PR_SecondsToInterval(1)); + PR_fprintf(output, "PR_MillisecondsToInterval(1000): %ld\n", PR_MillisecondsToInterval(1000)); + PR_fprintf(output, "PR_MicrosecondsToInterval(1000000): %ld\n\n", PR_MicrosecondsToInterval(1000000)); + + PR_fprintf(output, "PR_SecondsToInterval(3): %ld\n", PR_SecondsToInterval(3)); + PR_fprintf(output, "PR_MillisecondsToInterval(3000): %ld\n", PR_MillisecondsToInterval(3000)); + PR_fprintf(output, "PR_MicrosecondsToInterval(3000000): %ld\n\n", PR_MicrosecondsToInterval(3000000)); + + PR_fprintf(output, "PR_IntervalToSeconds(%ld): %ld\n", ticks, PR_IntervalToSeconds(ticks)); + PR_fprintf(output, "PR_IntervalToMilliseconds(%ld): %ld\n", ticks, PR_IntervalToMilliseconds(ticks)); + PR_fprintf(output, "PR_IntervalToMicroseconds(%ld): %ld\n\n", ticks, PR_IntervalToMicroseconds(ticks)); + + ticks *= 3; + PR_fprintf(output, "PR_IntervalToSeconds(%ld): %ld\n", ticks, PR_IntervalToSeconds(ticks)); + PR_fprintf(output, "PR_IntervalToMilliseconds(%ld): %ld\n", ticks, PR_IntervalToMilliseconds(ticks)); + PR_fprintf(output, "PR_IntervalToMicroseconds(%ld): %ld\n\n", ticks, PR_IntervalToMicroseconds(ticks)); + } /*end debug mode */ +} /* TestConversions */ + +static void TestIntervalOverhead(void) +{ + /* Hopefully the optimizer won't delete this function */ + PRUint32 elapsed, per_call, loops = 1000000; + + PRIntervalTime timeout, timein = PR_IntervalNow(); + while (--loops > 0) { + timeout = PR_IntervalNow(); + } + + elapsed = 1000U * PR_IntervalToMicroseconds(timeout - timein); + per_call = elapsed / 1000000U; + PR_fprintf( + output, "Overhead of 'PR_IntervalNow()' is %u nsecs\n\n", per_call); +} /* TestIntervalOverhead */ + +static void TestNowOverhead(void) +{ + PRTime timeout, timein; + PRInt32 overhead, loops = 1000000; + PRInt64 elapsed, per_call, ten23rd, ten26th; + + LL_I2L(ten23rd, 1000); + LL_I2L(ten26th, 1000000); + + timein = PR_Now(); + while (--loops > 0) { + timeout = PR_Now(); + } + + LL_SUB(elapsed, timeout, timein); + LL_MUL(elapsed, elapsed, ten23rd); + LL_DIV(per_call, elapsed, ten26th); + LL_L2I(overhead, per_call); + PR_fprintf( + output, "Overhead of 'PR_Now()' is %u nsecs\n\n", overhead); +} /* TestNowOverhead */ + +static void TestIntervals(void) +{ + PRStatus rv; + PRUint32 delta; + PRInt32 seconds; + PRUint64 elapsed, thousand; + PRTime timein, timeout; + PRLock *ml = PR_NewLock(); + PRCondVar *cv = PR_NewCondVar(ml); + for (seconds = 0; seconds < 10; ++seconds) + { + PRIntervalTime ticks = PR_SecondsToInterval(seconds); + PR_Lock(ml); + timein = PR_Now(); + rv = PR_WaitCondVar(cv, ticks); + timeout = PR_Now(); + PR_Unlock(ml); + LL_SUB(elapsed, timeout, timein); + LL_I2L(thousand, 1000); + LL_DIV(elapsed, elapsed, thousand); + LL_L2UI(delta, elapsed); + if (debug_mode) PR_fprintf(output, + "TestIntervals: %swaiting %ld seconds took %ld msecs\n", + ((rv == PR_SUCCESS) ? "" : "FAILED "), seconds, delta); + } + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + if (debug_mode) { + PR_fprintf(output, "\n"); + } +} /* TestIntervals */ + +static PRIntn PR_CALLBACK RealMain(int argc, char** argv) +{ + PRUint32 vcpu, cpus = 0, loops = 1000; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + + /* main test */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dl:c:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'c': /* concurrency counter */ + cpus = atoi(opt->value); + break; + case 'l': /* loop counter */ + loops = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + output = PR_GetSpecialFD(PR_StandardOutput); + PR_fprintf(output, "inrval: Examine stdout to determine results.\n"); + + if (cpus == 0) { + cpus = 8; + } + if (loops == 0) { + loops = 1000; + } + + if (debug_mode > 0) + { + PR_fprintf(output, "Inrval: Using %d loops\n", loops); + PR_fprintf(output, "Inrval: Using 1 and %d cpu(s)\n", cpus); + } + + for (vcpu = 1; vcpu <= cpus; vcpu += cpus - 1) + { + if (debug_mode) { + PR_fprintf(output, "\nInrval: Using %d CPU(s)\n\n", vcpu); + } + PR_SetConcurrency(vcpu); + + TestNowOverhead(); + TestIntervalOverhead(); + TestConversions(); + TestIntervals(); + } + + return 0; +} + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ + diff --git a/nsprpub/pr/tests/instrumt.c b/nsprpub/pr/tests/instrumt.c new file mode 100644 index 0000000000..e1ca94d035 --- /dev/null +++ b/nsprpub/pr/tests/instrumt.c @@ -0,0 +1,478 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: instrumt.c +** Description: This test is for the NSPR debug aids defined in +** prcountr.h, prtrace.h, prolock.h +** +** The test case tests the three debug aids in NSPR: +** +** Diagnostic messages can be enabled using "instrumt -v 6" +** This sets the msgLevel to something that PR_LOG() likes. +** Also define in the environment "NSPR_LOG_MODULES=Test:6" +** +** CounterTest() tests the counter facility. This test +** creates 4 threads. Each thread either increments, decrements, +** adds to or subtracts from a counter, depending on an argument +** passed to the thread at thread-create time. Each of these threads +** does COUNT_LIMIT iterations doing its thing. When all 4 threads +** are done, the result of the counter is evaluated. If all was atomic, +** the the value of the counter should be zero. +** +** TraceTest(): +** This test mingles with the counter test. Counters trace. +** A thread to extract trace entries on the fly is started. +** A thread to dump trace entries to a file is started. +** +** OrderedLockTest(): +** +** +** +** +** +*/ + +#include <stdio.h> +#include <plstr.h> +#include <prclist.h> +#include <prmem.h> +#include <plgetopt.h> +#include <prlog.h> +#include <prmon.h> +#include <pratom.h> +#include <prtrace.h> +#include <prcountr.h> +#include <prolock.h> + +#define COUNT_LIMIT (10 * ( 1024)) + +#define SMALL_TRACE_BUFSIZE ( 60 * 1024 ) + +typedef enum +{ + CountLoop = 1, + TraceLoop = 2, + TraceFlow = 3 +} TraceTypes; + + +PRLogModuleLevel msgLevel = PR_LOG_ALWAYS; + +PRBool help = PR_FALSE; +PRBool failed = PR_FALSE; + + +PRLogModuleInfo *lm; +PRMonitor *mon; +PRInt32 activeThreads = 0; +PR_DEFINE_COUNTER( hCounter ); +PR_DEFINE_TRACE( hTrace ); + +static void Help(void) +{ + printf("Help? ... Ha!\n"); +} + +static void ListCounters(void) +{ + PR_DEFINE_COUNTER( qh ); + PR_DEFINE_COUNTER( rh ); + const char *qn, *rn, *dn; + const char **qname = &qn, **rname = &rn, **desc = &dn; + PRUint32 tCtr; + + PR_INIT_COUNTER_HANDLE( qh, NULL ); + PR_FIND_NEXT_COUNTER_QNAME(qh, qh ); + while ( qh != NULL ) + { + PR_INIT_COUNTER_HANDLE( rh, NULL ); + PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh ); + while ( rh != NULL ) + { + PR_GET_COUNTER_NAME_FROM_HANDLE( rh, qname, rname, desc ); + PR_GET_COUNTER(tCtr, rh); + PR_LOG( lm, msgLevel, + ( "QName: %s RName: %s Desc: %s Value: %ld\n", + qn, rn, dn, tCtr )); + PR_FIND_NEXT_COUNTER_RNAME(rh, rh, qh ); + } + PR_FIND_NEXT_COUNTER_QNAME(qh, qh); + } + return; +} /* end ListCounters() */ + +static void ListTraces(void) +{ + PR_DEFINE_TRACE( qh ); + PR_DEFINE_TRACE( rh ); + const char *qn, *rn, *dn; + const char **qname = &qn, **rname = &rn, **desc = &dn; + + PR_INIT_TRACE_HANDLE( qh, NULL ); + PR_FIND_NEXT_TRACE_QNAME(qh, qh ); + while ( qh != NULL ) + { + PR_INIT_TRACE_HANDLE( rh, NULL ); + PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh ); + while ( rh != NULL ) + { + PR_GET_TRACE_NAME_FROM_HANDLE( rh, qname, rname, desc ); + PR_LOG( lm, msgLevel, + ( "QName: %s RName: %s Desc: %s", + qn, rn, dn )); + PR_FIND_NEXT_TRACE_RNAME(rh, rh, qh ); + } + PR_FIND_NEXT_TRACE_QNAME(qh, qh); + } + return; +} /* end ListCounters() */ + + +static PRInt32 one = 1; +static PRInt32 two = 2; +static PRInt32 three = 3; +static PRInt32 four = 4; + +/* +** Thread to iteratively count something. +*/ +static void PR_CALLBACK CountSomething( void *arg ) +{ + PRInt32 switchVar = *((PRInt32 *)arg); + PRInt32 i; + + PR_LOG( lm, msgLevel, + ("CountSomething: begin thread %ld", switchVar )); + + for ( i = 0; i < COUNT_LIMIT ; i++) + { + switch ( switchVar ) + { + case 1 : + PR_INCREMENT_COUNTER( hCounter ); + break; + case 2 : + PR_DECREMENT_COUNTER( hCounter ); + break; + case 3 : + PR_ADD_TO_COUNTER( hCounter, 1 ); + break; + case 4 : + PR_SUBTRACT_FROM_COUNTER( hCounter, 1 ); + break; + default : + PR_ASSERT( 0 ); + break; + } + PR_TRACE( hTrace, CountLoop, switchVar, i, 0, 0, 0, 0, 0 ); + } /* end for() */ + + PR_LOG( lm, msgLevel, + ("CounterSomething: end thread %ld", switchVar )); + + PR_EnterMonitor(mon); + --activeThreads; + PR_Notify( mon ); + PR_ExitMonitor(mon); + + return; +} /* end CountSomething() */ + +/* +** Create the counter threads. +*/ +static void CounterTest( void ) +{ + PRThread *t1, *t2, *t3, *t4; + PRIntn i = 0; + PR_DEFINE_COUNTER( tc ); + PR_DEFINE_COUNTER( zCounter ); + + PR_LOG( lm, msgLevel, + ("Begin CounterTest")); + + /* + ** Test Get and Set of a counter. + ** + */ + PR_CREATE_COUNTER( zCounter, "Atomic", "get/set test", "test get and set of counter" ); + PR_SET_COUNTER( zCounter, 9 ); + PR_GET_COUNTER( i, zCounter ); + if ( i != 9 ) + { + failed = PR_TRUE; + PR_LOG( lm, msgLevel, + ("Counter set/get failed")); + } + + activeThreads += 4; + PR_CREATE_COUNTER( hCounter, "Atomic", "SMP Tests", "test atomic nature of counter" ); + + PR_GET_COUNTER_HANDLE_FROM_NAME( tc, "Atomic", "SMP Tests" ); + PR_ASSERT( tc == hCounter ); + + t1 = PR_CreateThread(PR_USER_THREAD, + CountSomething, &one, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t1); + + t2 = PR_CreateThread(PR_USER_THREAD, + CountSomething, &two, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t2); + + t3 = PR_CreateThread(PR_USER_THREAD, + CountSomething, &three, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t3); + + t4 = PR_CreateThread(PR_USER_THREAD, + CountSomething, &four, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t4); + + PR_LOG( lm, msgLevel, + ("Counter Threads started")); + + ListCounters(); + return; +} /* end CounterTest() */ + +/* +** Thread to dump trace buffer to a file. +*/ +static void PR_CALLBACK RecordTrace(void *arg ) +{ + PR_RECORD_TRACE_ENTRIES(); + + PR_EnterMonitor(mon); + --activeThreads; + PR_Notify( mon ); + PR_ExitMonitor(mon); + + return; +} /* end RecordTrace() */ + + + +#define NUM_TRACE_RECORDS ( 10000 ) +/* +** Thread to extract and print trace entries from the buffer. +*/ +static void PR_CALLBACK SampleTrace( void *arg ) +{ +#if defined(DEBUG) || defined(FORCE_NSPR_TRACE) + PRInt32 found, rc; + PRTraceEntry *foundEntries; + PRInt32 i; + + foundEntries = (PRTraceEntry *)PR_Malloc( NUM_TRACE_RECORDS * sizeof(PRTraceEntry)); + PR_ASSERT(foundEntries != NULL ); + + do + { + rc = PR_GetTraceEntries( foundEntries, NUM_TRACE_RECORDS, &found); + PR_LOG( lm, msgLevel, + ("SampleTrace: Lost Data: %ld found: %ld", rc, found )); + + if ( found != 0) + { + for ( i = 0 ; i < found; i++ ) + { + PR_LOG( lm, msgLevel, + ("SampleTrace, detail: Thread: %p, Time: %llX, UD0: %ld, UD1: %ld, UD2: %8.8ld", + (foundEntries +i)->thread, + (foundEntries +i)->time, + (foundEntries +i)->userData[0], + (foundEntries +i)->userData[1], + (foundEntries +i)->userData[2] )); + } + } + PR_Sleep(PR_MillisecondsToInterval(50)); + } + while( found != 0 && activeThreads >= 1 ); + + PR_Free( foundEntries ); + + PR_EnterMonitor(mon); + --activeThreads; + PR_Notify( mon ); + PR_ExitMonitor(mon); + + PR_LOG( lm, msgLevel, + ("SampleTrace(): exiting")); + +#endif + return; +} /* end RecordTrace() */ + +/* +** Basic trace test. +*/ +static void TraceTest( void ) +{ + PRInt32 i; + PRInt32 size; + PR_DEFINE_TRACE( th ); + PRThread *t1, *t2; + + PR_LOG( lm, msgLevel, + ("Begin TraceTest")); + + size = SMALL_TRACE_BUFSIZE; + PR_SET_TRACE_OPTION( PRTraceBufSize, &size ); + PR_GET_TRACE_OPTION( PRTraceBufSize, &i ); + + PR_CREATE_TRACE( th, "TraceTest", "tt2", "A description for the trace test" ); + PR_CREATE_TRACE( th, "TraceTest", "tt3", "A description for the trace test" ); + PR_CREATE_TRACE( th, "TraceTest", "tt4", "A description for the trace test" ); + PR_CREATE_TRACE( th, "TraceTest", "tt5", "A description for the trace test" ); + PR_CREATE_TRACE( th, "TraceTest", "tt6", "A description for the trace test" ); + PR_CREATE_TRACE( th, "TraceTest", "tt7", "A description for the trace test" ); + PR_CREATE_TRACE( th, "TraceTest", "tt8", "A description for the trace test" ); + + PR_CREATE_TRACE( th, "Trace Test", "tt0", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt1", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt2", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt3", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt4", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt5", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt6", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt7", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt8", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt9", "QName is Trace Test, not TraceTest" ); + PR_CREATE_TRACE( th, "Trace Test", "tt10", "QName is Trace Test, not TraceTest" ); + + + + activeThreads += 2; + t1 = PR_CreateThread(PR_USER_THREAD, + RecordTrace, NULL, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t1); + + t2 = PR_CreateThread(PR_USER_THREAD, + SampleTrace, 0, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + PR_ASSERT(t2); + + ListTraces(); + + PR_GET_TRACE_HANDLE_FROM_NAME( th, "TraceTest","tt1" ); + PR_ASSERT( th == hTrace ); + + PR_LOG( lm, msgLevel, + ("End TraceTest")); + return; +} /* end TraceTest() */ + + +/* +** Ordered lock test. +*/ +static void OrderedLockTest( void ) +{ + PR_LOG( lm, msgLevel, + ("Begin OrderedLockTest")); + + +} /* end OrderedLockTest() */ + + +int main(int argc, char **argv) +{ +#if defined(DEBUG) || defined(FORCE_NSPR_TRACE) + PRUint32 counter; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdv:"); + lm = PR_NewLogModule("Test"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'v': /* verbose mode */ + msgLevel = (PRLogModuleLevel)atol( opt->value); + break; + case 'h': /* help message */ + Help(); + help = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_CREATE_TRACE( hTrace, "TraceTest", "tt1", "A description for the trace test" ); + mon = PR_NewMonitor(); + PR_EnterMonitor( mon ); + + TraceTest(); + CounterTest(); + OrderedLockTest(); + + /* Wait for all threads to exit */ + while ( activeThreads > 0 ) { + if ( activeThreads == 1 ) { + PR_SET_TRACE_OPTION( PRTraceStopRecording, NULL ); + } + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + PR_GET_COUNTER( counter, hCounter ); + } + PR_ExitMonitor( mon ); + + /* + ** Evaluate results + */ + PR_GET_COUNTER( counter, hCounter ); + if ( counter != 0 ) + { + failed = PR_TRUE; + PR_LOG( lm, msgLevel, + ("Expected counter == 0, found: %ld", counter)); + printf("FAIL\n"); + } + else + { + printf("PASS\n"); + } + + + PR_DESTROY_COUNTER( hCounter ); + + PR_DestroyMonitor( mon ); + + PR_TRACE( hTrace, TraceFlow, 0xfff,0,0,0,0,0,0); + PR_DESTROY_TRACE( hTrace ); +#else + printf("Test not defined\n"); +#endif + return 0; +} /* main() */ +/* end instrumt.c */ + diff --git a/nsprpub/pr/tests/intrio.c b/nsprpub/pr/tests/intrio.c new file mode 100644 index 0000000000..23d834d667 --- /dev/null +++ b/nsprpub/pr/tests/intrio.c @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: intrio.c + * Purpose: testing i/o interrupts (see Bugzilla bug #31120) + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* for synchronization between the main thread and iothread */ +static PRLock *lock; +static PRCondVar *cvar; +static PRBool iothread_ready; + +static void PR_CALLBACK AbortIO(void *arg) +{ + PRStatus rv; + PR_Sleep(PR_SecondsToInterval(2)); + rv = PR_Interrupt((PRThread*)arg); + PR_ASSERT(PR_SUCCESS == rv); +} /* AbortIO */ + +static void PR_CALLBACK IOThread(void *arg) +{ + PRFileDesc *sock, *newsock; + PRNetAddr addr; + + sock = PR_OpenTCPSocket(PR_AF_INET6); + if (sock == NULL) { + fprintf(stderr, "PR_OpenTCPSocket failed\n"); + exit(1); + } + memset(&addr, 0, sizeof(addr)); + if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + 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); + } + /* tell the main thread that we are ready */ + PR_Lock(lock); + iothread_ready = PR_TRUE; + PR_NotifyCondVar(cvar); + PR_Unlock(lock); + newsock = PR_Accept(sock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (newsock != NULL) { + fprintf(stderr, "PR_Accept shouldn't have succeeded\n"); + exit(1); + } + if (PR_GetError() != PR_PENDING_INTERRUPT_ERROR) { + fprintf(stderr, "PR_Accept failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("PR_Accept() is interrupted as expected\n"); + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +static void Test(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *iothread, *abortio; + + printf("A %s thread will be interrupted by a %s thread\n", + (scope1 == PR_LOCAL_THREAD ? "local" : "global"), + (scope2 == PR_LOCAL_THREAD ? "local" : "global")); + iothread_ready = PR_FALSE; + iothread = PR_CreateThread( + PR_USER_THREAD, IOThread, NULL, PR_PRIORITY_NORMAL, + scope1, PR_JOINABLE_THREAD, 0); + if (iothread == NULL) { + fprintf(stderr, "cannot create thread\n"); + exit(1); + } + PR_Lock(lock); + while (!iothread_ready) { + PR_WaitCondVar(cvar, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(lock); + abortio = PR_CreateThread( + PR_USER_THREAD, AbortIO, iothread, PR_PRIORITY_NORMAL, + scope2, PR_JOINABLE_THREAD, 0); + if (abortio == NULL) { + fprintf(stderr, "cannot create thread\n"); + exit(1); + } + if (PR_JoinThread(iothread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + if (PR_JoinThread(abortio) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + lock = PR_NewLock(); + if (lock == NULL) { + fprintf(stderr, "PR_NewLock failed\n"); + exit(1); + } + cvar = PR_NewCondVar(lock); + if (cvar == NULL) { + fprintf(stderr, "PR_NewCondVar failed\n"); + exit(1); + } + /* test all four combinations */ + Test(PR_LOCAL_THREAD, PR_LOCAL_THREAD); + Test(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); + Test(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); + Test(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); + printf("PASSED\n"); + return 0; +} /* main */ diff --git a/nsprpub/pr/tests/intrupt.c b/nsprpub/pr/tests/intrupt.c new file mode 100644 index 0000000000..950d30d469 --- /dev/null +++ b/nsprpub/pr/tests/intrupt.c @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: intrupt.c + * Purpose: testing thread interrupts + */ + +#include "plgetopt.h" +#include "prcvar.h" +#include "prerror.h" +#include "prinit.h" +#include "prinrval.h" +#include "prio.h" +#include "prlock.h" +#include "prlog.h" +#include "prthread.h" +#include "prtypes.h" +#include "prnetdb.h" + +#include <stdio.h> +#include <string.h> + +#define DEFAULT_TCP_PORT 12500 + +static PRLock *ml = NULL; +static PRCondVar *cv = NULL; + +static PRBool passed = PR_TRUE; +static PRBool debug_mode = PR_FALSE; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +static void PR_CALLBACK AbortCV(void *arg) +{ + PRStatus rv; + PRThread *me = PR_GetCurrentThread(); + + /* some other thread (main) is doing the interrupt */ + PR_Lock(ml); + rv = PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); + if (debug_mode) { + printf( "Expected interrupt on wait CV and "); + } + if (PR_FAILURE == rv) + { + if (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) + { + if (debug_mode) { + printf("got it\n"); + } + } + else + { + if (debug_mode) { + printf("got random error\n"); + } + passed = PR_FALSE; + } + } + else + { + if (debug_mode) { + printf("got a successful completion\n"); + } + passed = PR_FALSE; + } + + rv = PR_WaitCondVar(cv, 10); + if (debug_mode) + { + printf( + "Expected success on wait CV and %s\n", + (PR_SUCCESS == rv) ? "got it" : "failed"); + } + passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE; + + /* interrupt myself, then clear */ + PR_Interrupt(me); + PR_ClearInterrupt(); + rv = PR_WaitCondVar(cv, 10); + if (debug_mode) + { + printf("Expected success on wait CV and "); + if (PR_FAILURE == rv) + { + printf( + "%s\n", (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) ? + "got interrupted" : "a random failure"); + } + printf("got it\n"); + } + passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE; + + /* set, then wait - interrupt - then wait again */ + PR_Interrupt(me); + rv = PR_WaitCondVar(cv, 10); + if (debug_mode) { + printf( "Expected interrupt on wait CV and "); + } + if (PR_FAILURE == rv) + { + if (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) + { + if (debug_mode) { + printf("got it\n"); + } + } + else + { + if (debug_mode) { + printf("failed\n"); + } + passed = PR_FALSE; + } + } + else + { + if (debug_mode) { + printf("got a successful completion\n"); + } + passed = PR_FALSE; + } + + rv = PR_WaitCondVar(cv, 10); + if (debug_mode) + { + printf( + "Expected success on wait CV and %s\n", + (PR_SUCCESS == rv) ? "got it" : "failed"); + } + passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE; + + PR_Unlock(ml); + +} /* AbortCV */ + +static void PR_CALLBACK AbortIO(void *arg) +{ + PRStatus rv; + PR_Sleep(PR_SecondsToInterval(2)); + rv = PR_Interrupt((PRThread*)arg); + PR_ASSERT(PR_SUCCESS == rv); +} /* AbortIO */ + +static void PR_CALLBACK AbortJoin(void *arg) +{ +} /* AbortJoin */ + +static void setup_listen_socket(PRFileDesc **listner, PRNetAddr *netaddr) +{ + PRStatus rv; + PRInt16 port = DEFAULT_TCP_PORT; + + *listner = PR_NewTCPSocket(); + PR_ASSERT(*listner != NULL); + memset(netaddr, 0, sizeof(*netaddr)); + (*netaddr).inet.ip = PR_htonl(PR_INADDR_ANY); + (*netaddr).inet.family = PR_AF_INET; + do + { + (*netaddr).inet.port = PR_htons(port); + rv = PR_Bind(*listner, netaddr); + port += 1; + PR_ASSERT(port < (DEFAULT_TCP_PORT + 10)); + } while (PR_FAILURE == rv); + + rv = PR_Listen(*listner, 5); + + if (PR_GetSockName(*listner, netaddr) < 0) { + if (debug_mode) { + printf("intrupt: ERROR - PR_GetSockName failed\n"); + } + passed = PR_FALSE; + return; + } + +} + +static void PR_CALLBACK IntrBlock(void *arg) +{ + PRStatus rv; + PRNetAddr netaddr; + PRFileDesc *listner; + + /* some other thread (main) is doing the interrupt */ + /* block the interrupt */ + PR_BlockInterrupt(); + PR_Lock(ml); + rv = PR_WaitCondVar(cv, PR_SecondsToInterval(4)); + PR_Unlock(ml); + if (debug_mode) + { + printf("Expected success on wait CV and "); + if (PR_FAILURE == rv) + { + printf( + "%s\n", (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) ? + "got interrupted" : "got a random failure"); + } else { + printf("got it\n"); + } + } + passed = ((PR_TRUE == passed) && (PR_SUCCESS == rv)) ? PR_TRUE : PR_FALSE; + + setup_listen_socket(&listner, &netaddr); + PR_UnblockInterrupt(); + if (PR_Accept(listner, &netaddr, PR_INTERVAL_NO_TIMEOUT) == NULL) + { + PRInt32 error = PR_GetError(); + if (debug_mode) { + printf("Expected interrupt on PR_Accept() and "); + } + if (PR_PENDING_INTERRUPT_ERROR == error) + { + if (debug_mode) { + printf("got it\n"); + } + } + else + { + if (debug_mode) { + printf("failed\n"); + } + passed = PR_FALSE; + } + } + else + { + if (debug_mode) { + printf("Failed to interrupt PR_Accept()\n"); + } + passed = PR_FALSE; + } + + (void)PR_Close(listner); listner = NULL; +} /* TestIntrBlock */ + +void PR_CALLBACK Intrupt(void *arg) +{ + PRStatus rv; + PRNetAddr netaddr; + PRFileDesc *listner; + PRThread *abortCV, *abortIO, *abortJoin, *intrBlock; + + ml = PR_NewLock(); + cv = PR_NewCondVar(ml); + + /* Part I */ + if (debug_mode) { + printf("Part I\n"); + } + abortCV = PR_CreateThread( + PR_USER_THREAD, AbortCV, 0, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + + PR_Sleep(PR_SecondsToInterval(2)); + rv = PR_Interrupt(abortCV); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(abortCV); + PR_ASSERT(PR_SUCCESS == rv); + + /* Part II */ + if (debug_mode) { + printf("Part II\n"); + } + abortJoin = PR_CreateThread( + PR_USER_THREAD, AbortJoin, 0, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + PR_Sleep(PR_SecondsToInterval(2)); + if (debug_mode) { + printf("Expecting to interrupt an exited thread "); + } + rv = PR_Interrupt(abortJoin); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(abortJoin); + PR_ASSERT(PR_SUCCESS == rv); + if (debug_mode) { + printf("and succeeded\n"); + } + + /* Part III */ + if (debug_mode) { + printf("Part III\n"); + } + setup_listen_socket(&listner, &netaddr); + abortIO = PR_CreateThread( + PR_USER_THREAD, AbortIO, PR_GetCurrentThread(), PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + + if (PR_Accept(listner, &netaddr, PR_INTERVAL_NO_TIMEOUT) == NULL) + { + PRInt32 error = PR_GetError(); + if (debug_mode) { + printf("Expected interrupt on PR_Accept() and "); + } + if (PR_PENDING_INTERRUPT_ERROR == error) + { + if (debug_mode) { + printf("got it\n"); + } + } + else + { + if (debug_mode) { + printf("failed\n"); + } + passed = PR_FALSE; + } + } + else + { + if (debug_mode) { + printf("Failed to interrupt PR_Accept()\n"); + } + passed = PR_FALSE; + } + + (void)PR_Close(listner); listner = NULL; + + rv = PR_JoinThread(abortIO); + PR_ASSERT(PR_SUCCESS == rv); + /* Part VI */ + if (debug_mode) { + printf("Part VI\n"); + } + intrBlock = PR_CreateThread( + PR_USER_THREAD, IntrBlock, 0, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + + PR_Sleep(PR_SecondsToInterval(2)); + rv = PR_Interrupt(intrBlock); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(intrBlock); + PR_ASSERT(PR_SUCCESS == rv); + + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); +} /* Intrupt */ + +int main(int argc, char **argv) +{ + PRThread *intrupt; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dG"); + 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 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + } + } + PL_DestroyOptState(opt); + PR_STDIO_INIT(); + intrupt = PR_CreateThread( + PR_USER_THREAD, Intrupt, NULL, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + if (intrupt == NULL) { + fprintf(stderr, "cannot create thread\n"); + passed = PR_FALSE; + } else { + PRStatus rv; + rv = PR_JoinThread(intrupt); + PR_ASSERT(rv == PR_SUCCESS); + } + printf("%s\n", ((passed) ? "PASSED" : "FAILED")); + return ((passed) ? 0 : 1); +} /* main */ + +/* intrupt.c */ diff --git a/nsprpub/pr/tests/io_timeout.c b/nsprpub/pr/tests/io_timeout.c new file mode 100644 index 0000000000..19d3daeccf --- /dev/null +++ b/nsprpub/pr/tests/io_timeout.c @@ -0,0 +1,269 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** Test socket IO timeouts +** +** +** +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include <stdio.h> +#include "nspr.h" + +#define NUM_THREADS 1 +#define BASE_PORT 8000 +#define DEFAULT_ACCEPT_TIMEOUT 2 + +typedef struct threadInfo { + PRInt16 id; + PRInt16 accept_timeout; + PRLock *dead_lock; + PRCondVar *dead_cv; + PRInt32 *alive; +} threadInfo; + +PRIntn failed_already = 0; +PRIntn debug_mode = 0; + +#define LOCAL_SCOPE_STRING "LOCAL scope" +#define GLOBAL_SCOPE_STRING "GLOBAL scope" +#define GLOBAL_BOUND_SCOPE_STRING "GLOBAL_BOUND scope" + +void +thread_main(void *_info) +{ + threadInfo *info = (threadInfo *)_info; + PRNetAddr listenAddr; + PRNetAddr clientAddr; + PRFileDesc *listenSock = NULL; + PRFileDesc *clientSock; + PRStatus rv; + PRThreadScope tscope; + char *scope_str; + + + if (debug_mode) { + printf("thread %d is alive\n", info->id); + } + tscope = PR_GetThreadScope(PR_GetCurrentThread()); + + switch(tscope) { + case PR_LOCAL_THREAD: + scope_str = LOCAL_SCOPE_STRING; + break; + case PR_GLOBAL_THREAD: + scope_str = GLOBAL_SCOPE_STRING; + break; + case PR_GLOBAL_BOUND_THREAD: + scope_str = GLOBAL_BOUND_SCOPE_STRING; + break; + default: + PR_NOT_REACHED("Invalid thread scope"); + break; + } + printf("thread id %d, scope %s\n", info->id, scope_str); + + listenSock = PR_NewTCPSocket(); + if (!listenSock) { + if (debug_mode) { + printf("unable to create listen socket\n"); + } + failed_already=1; + goto dead; + } + + listenAddr.inet.family = PR_AF_INET; + listenAddr.inet.port = PR_htons(BASE_PORT + info->id); + listenAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + rv = PR_Bind(listenSock, &listenAddr); + if (rv == PR_FAILURE) { + if (debug_mode) { + printf("unable to bind\n"); + } + failed_already=1; + goto dead; + } + + rv = PR_Listen(listenSock, 4); + if (rv == PR_FAILURE) { + if (debug_mode) { + printf("unable to listen\n"); + } + failed_already=1; + goto dead; + } + + if (debug_mode) + printf("thread %d going into accept for %d seconds\n", + info->id, info->accept_timeout + info->id); + + clientSock = PR_Accept(listenSock, &clientAddr, PR_SecondsToInterval(info->accept_timeout +info->id)); + + if (clientSock == NULL) { + if (PR_GetError() == PR_IO_TIMEOUT_ERROR) { + if (debug_mode) { + printf("PR_Accept() timeout worked!\n"); + printf("TEST PASSED! PR_Accept() returned error %d\n", + PR_IO_TIMEOUT_ERROR); + } + } else { + if (debug_mode) + printf("TEST FAILED! PR_Accept() returned error %d\n", + PR_GetError()); + failed_already=1; + } + } else { + if (debug_mode) { + printf ("TEST FAILED! PR_Accept() succeeded?\n"); + } + failed_already=1; + PR_Close(clientSock); + } + +dead: + if (listenSock) { + PR_Close(listenSock); + } + PR_Lock(info->dead_lock); + (*info->alive)--; + PR_NotifyCondVar(info->dead_cv); + PR_Unlock(info->dead_lock); + + if (debug_mode) { + printf("thread %d is dead\n", info->id); + } + + PR_Free(info); +} + +void +thread_test(PRThreadScope scope, PRInt32 num_threads) +{ + PRInt32 index; + PRThread *thr; + PRLock *dead_lock; + PRCondVar *dead_cv; + PRInt32 alive; + + if (debug_mode) { + printf("IO Timeout test started with %d threads\n", num_threads); + } + + dead_lock = PR_NewLock(); + dead_cv = PR_NewCondVar(dead_lock); + alive = num_threads; + + for (index = 0; index < num_threads; index++) { + threadInfo *info = (threadInfo *)PR_Malloc(sizeof(threadInfo)); + + info->id = index; + info->dead_lock = dead_lock; + info->dead_cv = dead_cv; + info->alive = &alive; + info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT; + + thr = PR_CreateThread( PR_USER_THREAD, + thread_main, + (void *)info, + PR_PRIORITY_NORMAL, + scope, + PR_UNJOINABLE_THREAD, + 0); + + if (!thr) { + printf("Failed to create thread, error = %d(%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + + PR_Lock(dead_lock); + alive--; + PR_Unlock(dead_lock); + } + } + + PR_Lock(dead_lock); + while(alive) { + if (debug_mode) { + printf("main loop awake; alive = %d\n", alive); + } + 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) +{ + PRInt32 num_threads = 0; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name [-d] [-t <threads>] + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dt:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 't': /* threads to involve */ + num_threads = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + if (0 == num_threads) { + num_threads = NUM_THREADS; + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0); + PR_STDIO_INIT(); + + printf("test with global bound thread\n"); + thread_test(PR_GLOBAL_BOUND_THREAD, num_threads); + + printf("test with local thread\n"); + thread_test(PR_LOCAL_THREAD, num_threads); + + printf("test with global thread\n"); + thread_test(PR_GLOBAL_THREAD, num_threads); + + PR_Cleanup(); + + if (failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/io_timeoutk.c b/nsprpub/pr/tests/io_timeoutk.c new file mode 100644 index 0000000000..a058655fdb --- /dev/null +++ b/nsprpub/pr/tests/io_timeoutk.c @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** name io_timeoutk.c +** Description:Test socket IO timeouts (kernel level) +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include <stdio.h> +#include "nspr.h" + +#define NUM_THREADS 1 +#define BASE_PORT 8000 +#define DEFAULT_ACCEPT_TIMEOUT 2 + +typedef struct threadInfo { + PRInt16 id; + PRInt16 accept_timeout; + PRLock *dead_lock; + PRCondVar *dead_cv; + PRInt32 *alive; +} threadInfo; + +PRIntn failed_already=0; +PRIntn debug_mode; + + +void +thread_main(void *_info) +{ + threadInfo *info = (threadInfo *)_info; + PRNetAddr listenAddr; + PRNetAddr clientAddr; + PRFileDesc *listenSock = NULL; + PRFileDesc *clientSock; + PRStatus rv; + + if (debug_mode) { + printf("thread %d is alive\n", info->id); + } + + listenSock = PR_NewTCPSocket(); + if (!listenSock) { + if (debug_mode) { + printf("unable to create listen socket\n"); + } + goto dead; + } + + listenAddr.inet.family = AF_INET; + listenAddr.inet.port = PR_htons(BASE_PORT + info->id); + listenAddr.inet.ip = PR_htonl(INADDR_ANY); + rv = PR_Bind(listenSock, &listenAddr); + if (rv == PR_FAILURE) { + if (debug_mode) { + printf("unable to bind\n"); + } + goto dead; + } + + rv = PR_Listen(listenSock, 4); + if (rv == PR_FAILURE) { + if (debug_mode) { + printf("unable to listen\n"); + } + goto dead; + } + + if (debug_mode) printf("thread %d going into accept for %d seconds\n", + info->id, info->accept_timeout + info->id); + + clientSock = PR_Accept(listenSock, &clientAddr, PR_SecondsToInterval(info->accept_timeout +info->id)); + + if (clientSock == NULL) { + if (PR_GetError() == PR_IO_TIMEOUT_ERROR) + if (debug_mode) { + printf("PR_Accept() timeout worked!\n"); + printf("TEST FAILED! PR_Accept() returned error %d\n", + PR_GetError()); + } + else { + failed_already=1; + } + } else { + if (debug_mode) { + printf ("TEST FAILED! PR_Accept() succeeded?\n"); + } + else { + failed_already=1; + } + PR_Close(clientSock); + } + +dead: + if (listenSock) { + PR_Close(listenSock); + } + PR_Lock(info->dead_lock); + (*info->alive)--; + PR_NotifyCondVar(info->dead_cv); + PR_Unlock(info->dead_lock); + + if (debug_mode) { + printf("thread %d is dead\n", info->id); + } +} + +void +thread_test(PRInt32 scope, PRInt32 num_threads) +{ + PRInt32 index; + PRThread *thr; + PRLock *dead_lock; + PRCondVar *dead_cv; + PRInt32 alive; + + if (debug_mode) { + printf("IO Timeout test started with %d threads\n", num_threads); + } + + dead_lock = PR_NewLock(); + dead_cv = PR_NewCondVar(dead_lock); + alive = num_threads; + + for (index = 0; index < num_threads; index++) { + threadInfo *info = (threadInfo *)malloc(sizeof(threadInfo)); + + info->id = index; + info->dead_lock = dead_lock; + info->dead_cv = dead_cv; + info->alive = &alive; + info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT; + + thr = PR_CreateThread( PR_USER_THREAD, + thread_main, + (void *)info, + PR_PRIORITY_NORMAL, + scope, + PR_UNJOINABLE_THREAD, + 0); + + if (!thr) { + PR_Lock(dead_lock); + alive--; + PR_Unlock(dead_lock); + } + } + + PR_Lock(dead_lock); + while(alive) { + if (debug_mode) { + printf("main loop awake; alive = %d\n", alive); + } + PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(dead_lock); +} + +int main(int argc, char **argv) +{ + PRInt32 num_threads; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + if (argc > 2) { + num_threads = atoi(argv[2]); + } + else { + num_threads = NUM_THREADS; + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("kernel level test\n"); + } + thread_test(PR_GLOBAL_THREAD, num_threads); + + PR_Cleanup(); + + if(failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/io_timeoutu.c b/nsprpub/pr/tests/io_timeoutu.c new file mode 100644 index 0000000000..b6d9ccfa36 --- /dev/null +++ b/nsprpub/pr/tests/io_timeoutu.c @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* +** name io_timeoutu.c +** Description: Test socket IO timeouts (user level) +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include <stdio.h> +#include "nspr.h" + +#define NUM_THREADS 1 +#define BASE_PORT 8000 +#define DEFAULT_ACCEPT_TIMEOUT 2 + +typedef struct threadInfo { + PRInt16 id; + PRInt16 accept_timeout; + PRLock *dead_lock; + PRCondVar *dead_cv; + PRInt32 *alive; +} threadInfo; +PRIntn failed_already=0; +PRIntn debug_mode; + +void +thread_main(void *_info) +{ + threadInfo *info = (threadInfo *)_info; + PRNetAddr listenAddr; + PRNetAddr clientAddr; + PRFileDesc *listenSock = NULL; + PRFileDesc *clientSock; + PRStatus rv; + + if (debug_mode) { + printf("thread %d is alive\n", info->id); + } + + listenSock = PR_NewTCPSocket(); + if (!listenSock) { + if (debug_mode) { + printf("unable to create listen socket\n"); + } + goto dead; + } + + listenAddr.inet.family = AF_INET; + listenAddr.inet.port = PR_htons(BASE_PORT + info->id); + listenAddr.inet.ip = PR_htonl(INADDR_ANY); + rv = PR_Bind(listenSock, &listenAddr); + if (rv == PR_FAILURE) { + if (debug_mode) { + printf("unable to bind\n"); + } + goto dead; + } + + rv = PR_Listen(listenSock, 4); + if (rv == PR_FAILURE) { + if (debug_mode) { + printf("unable to listen\n"); + } + goto dead; + } + + if (debug_mode) printf("thread %d going into accept for %d seconds\n", + info->id, info->accept_timeout + info->id); + + clientSock = PR_Accept( + listenSock, &clientAddr, PR_SecondsToInterval( + info->accept_timeout + info->id)); + + if (clientSock == NULL) { + if (PR_GetError() == PR_IO_TIMEOUT_ERROR) + if (debug_mode) { + printf("PR_Accept() timeout worked!\n"); + printf("TEST FAILED! PR_Accept() returned error %d\n", + } + PR_GetError()); + else { + failed_already=1; + } + } else { + if (debug_mode) { + printf ("TEST FAILED! PR_Accept() succeeded?\n"); + } + else { + failed_already=1; + } + PR_Close(clientSock); + } + +dead: + if (listenSock) { + PR_Close(listenSock); + } + PR_Lock(info->dead_lock); + (*info->alive)--; + PR_NotifyCondVar(info->dead_cv); + PR_Unlock(info->dead_lock); + + if (debug_mode) { + printf("thread %d is dead\n", info->id); + } +} + +void +thread_test(PRInt32 scope, PRInt32 num_threads) +{ + PRInt32 index; + PRThread *thr; + PRLock *dead_lock; + PRCondVar *dead_cv; + PRInt32 alive; + + if (debug_mode) { + printf("IO Timeout test started with %d threads\n", num_threads); + } + + dead_lock = PR_NewLock(); + dead_cv = PR_NewCondVar(dead_lock); + alive = num_threads; + + for (index = 0; index < num_threads; index++) { + threadInfo *info = (threadInfo *)malloc(sizeof(threadInfo)); + + info->id = index; + info->dead_lock = dead_lock; + info->dead_cv = dead_cv; + info->alive = &alive; + info->accept_timeout = DEFAULT_ACCEPT_TIMEOUT; + + thr = PR_CreateThread( PR_USER_THREAD, + thread_main, + (void *)info, + PR_PRIORITY_NORMAL, + scope, + PR_UNJOINABLE_THREAD, + 0); + + if (!thr) { + PR_Lock(dead_lock); + alive--; + PR_Unlock(dead_lock); + } + } + + PR_Lock(dead_lock); + while(alive) { + if (debug_mode) { + printf("main loop awake; alive = %d\n", alive); + } + PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(dead_lock); +} + +int main(int argc, char **argv) +{ + PRInt32 num_threads; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + if (argc > 2) { + num_threads = atoi(argv[2]); + } + else { + num_threads = NUM_THREADS; + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_LOW, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("user level test\n"); + } + thread_test(PR_LOCAL_THREAD, num_threads); + + PR_Cleanup(); + if(failed_already) { + return 1; + } + else { + return 0; + } + + +} diff --git a/nsprpub/pr/tests/ioconthr.c b/nsprpub/pr/tests/ioconthr.c new file mode 100644 index 0000000000..3f5adcd572 --- /dev/null +++ b/nsprpub/pr/tests/ioconthr.c @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This is a test for the io continuation thread machinery + * in pthreads. + */ + +#include "nspr.h" +#include <stdio.h> + +int num_threads = 10; /* must be an even number */ +PRThreadScope thread_scope = PR_GLOBAL_THREAD; + +void ThreadFunc(void *arg) +{ + PRFileDesc *fd = (PRFileDesc *) arg; + char buf[1024]; + PRInt32 nbytes; + PRErrorCode err; + + nbytes = PR_Recv(fd, buf, sizeof(buf), 0, PR_SecondsToInterval(20)); + if (nbytes == -1) { + err = PR_GetError(); + if (err != PR_PENDING_INTERRUPT_ERROR) { + fprintf(stderr, "PR_Recv failed: (%d, %d)\n", + 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); + } + } else { + fprintf(stderr, "PR_Recv received %d bytes!?\n", nbytes); + PR_ProcessExit(1); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc **fds; + PRThread **threads; + PRIntervalTime start, elapsed; + int index; + + 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; index++) { + if (PR_NewTCPSocketPair(&fds[2 * index]) == PR_FAILURE) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + PR_ProcessExit(1); + } + threads[index] = PR_CreateThread( + PR_USER_THREAD, ThreadFunc, fds[2 * index], + PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0); + if (NULL == threads[index]) { + fprintf(stderr, "PR_CreateThread failed\n"); + PR_ProcessExit(1); + } + } + + /* Let the threads block in PR_Recv */ + PR_Sleep(PR_SecondsToInterval(2)); + + printf("Interrupting the threads\n"); + fflush(stdout); + start = PR_IntervalNow(); + for (index = 0; index < num_threads; index++) { + if (PR_Interrupt(threads[index]) == PR_FAILURE) { + fprintf(stderr, "PR_Interrupt failed\n"); + PR_ProcessExit(1); + } + } + for (index = 0; index < num_threads; index++) { + if (PR_JoinThread(threads[index]) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + PR_ProcessExit(1); + } + } + elapsed = (PRIntervalTime)(PR_IntervalNow() - start); + printf("Threads terminated in %d milliseconds\n", + PR_IntervalToMilliseconds(elapsed)); + fflush(stdout); + + /* We are being very generous and allow 10 seconds. */ + if (elapsed >= PR_SecondsToInterval(10)) { + fprintf(stderr, "Interrupting threads took longer than 10 seconds!!\n"); + 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"); + PR_Cleanup(); + return 0; +} diff --git a/nsprpub/pr/tests/ipv6.c b/nsprpub/pr/tests/ipv6.c new file mode 100644 index 0000000000..cc323e1e6c --- /dev/null +++ b/nsprpub/pr/tests/ipv6.c @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prenv.h" +#include "prmem.h" +#include "prlink.h" +#include "prsystem.h" +#include "prnetdb.h" +#include "prprf.h" +#include "prvrsion.h" + +#include "plerror.h" +#include "plgetopt.h" +#include "obsolete/probslet.h" + +#include <string.h> + +#define DNS_BUFFER 100 +#define ADDR_BUFFER 100 +#define HOST_BUFFER 1024 +#define PROTO_BUFFER 1500 + +#define NETADDR_SIZE(addr) \ + (PR_AF_INET == (addr)->raw.family ? \ + sizeof((addr)->inet) : sizeof((addr)->ipv6)) + +static PRFileDesc *err = NULL; + +static void Help(void) +{ + PR_fprintf(err, "Usage: [-V] [-h]\n"); + PR_fprintf(err, "\t<nul> Name of host to lookup (default: self)\n"); + PR_fprintf(err, "\t-V Display runtime version info (default: FALSE)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +static void DumpAddr(const PRNetAddr* address, const char *msg) +{ + PRUint32 *word = (PRUint32*)address; + PRUint32 addr_len = sizeof(PRNetAddr); + PR_fprintf(err, "%s[%d]\t", msg, NETADDR_SIZE(address)); + while (addr_len > 0) + { + PR_fprintf(err, " %08x", *word++); + addr_len -= sizeof(PRUint32); + } + PR_fprintf(err, "\n"); +} /* DumpAddr */ + +static PRStatus PrintAddress(const PRNetAddr* address) +{ + PRNetAddr translation; + char buffer[ADDR_BUFFER]; + PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer)); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_NetAddrToString"); + } + else + { + PR_fprintf(err, "\t%s\n", buffer); + memset(&translation, 0, sizeof(translation)); + rv = PR_StringToNetAddr(buffer, &translation); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_StringToNetAddr"); + } + else + { + PRSize addr_len = NETADDR_SIZE(address); + if (0 != memcmp(address, &translation, addr_len)) + { + PR_fprintf(err, "Address translations do not match\n"); + DumpAddr(address, "original"); + DumpAddr(&translation, "translate"); + rv = PR_FAILURE; + } + } + } + return rv; +} /* PrintAddress */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PLOptStatus os; + PRHostEnt host; + PRProtoEnt proto; + const char *name = NULL; + PRBool failed = PR_FALSE, version = PR_FALSE; + PLOptState *opt = PL_CreateOptState(argc, argv, "Vh"); + + err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: /* Name of host to lookup */ + name = opt->value; + break; + case 'V': /* Do version discovery */ + version = PR_TRUE; + break; + case 'h': /* user wants some guidance */ + default: + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + + if (version) + { +#if defined(WINNT) +#define NSPR_LIB "libnspr4" +#else +#define NSPR_LIB "nspr4" +#endif + const PRVersionDescription *version_info; + char *nspr_path = PR_GetEnv("LD_LIBRARY_PATH"); + char *nspr_name = PR_GetLibraryName(nspr_path, NSPR_LIB); + PRLibrary *runtime = PR_LoadLibrary(nspr_name); + if (NULL == runtime) { + PL_FPrintError(err, "PR_LoadLibrary"); + } + else + { + versionEntryPointType versionPoint = (versionEntryPointType) + PR_FindSymbol(runtime, "libVersionPoint"); + if (NULL == versionPoint) { + PL_FPrintError(err, "PR_FindSymbol"); + } + else + { + char buffer[100]; + PRExplodedTime exploded; + version_info = versionPoint(); + (void)PR_fprintf(err, "Runtime library version information\n"); + PR_ExplodeTime( + version_info->buildTime, PR_GMTParameters, &exploded); + (void)PR_FormatTime( + buffer, sizeof(buffer), "%d %b %Y %H:%M:%S", &exploded); + (void)PR_fprintf(err, " Build time: %s GMT\n", buffer); + (void)PR_fprintf( + err, " Build time: %s\n", version_info->buildTimeString); + (void)PR_fprintf( + err, " %s V%u.%u.%u (%s%s%s)\n", + version_info->description, + version_info->vMajor, + version_info->vMinor, + version_info->vPatch, + (version_info->beta ? " beta " : ""), + (version_info->debug ? " debug " : ""), + (version_info->special ? " special" : "")); + (void)PR_fprintf(err, " filename: %s\n", version_info->filename); + (void)PR_fprintf(err, " security: %s\n", version_info->security); + (void)PR_fprintf(err, " copyright: %s\n", version_info->copyright); + (void)PR_fprintf(err, " comment: %s\n", version_info->comment); + } + } + if (NULL != nspr_name) { + PR_FreeLibraryName(nspr_name); + } + } + + { + if (NULL == name) + { + char *me = (char*)PR_MALLOC(DNS_BUFFER); + rv = PR_GetSystemInfo(PR_SI_HOSTNAME, me, DNS_BUFFER); + if (PR_FAILURE == rv) + { + failed = PR_TRUE; + PL_FPrintError(err, "PR_GetSystemInfo"); + return 2; + } + name = me; /* just leak the storage */ + } + } + + { + char buffer[HOST_BUFFER]; + PR_fprintf(err, "Translating the name %s ...", name); + + rv = PR_GetHostByName(name, buffer, sizeof(buffer), &host); + if (PR_FAILURE == rv) + { + failed = PR_TRUE; + PL_FPrintError(err, "PR_GetHostByName"); + } + else + { + PRIntn index = 0; + PRNetAddr address; + memset(&address, 0, sizeof(PRNetAddr)); + PR_fprintf(err, "success .. enumerating results\n"); + do + { + index = PR_EnumerateHostEnt(index, &host, 0, &address); + if (index > 0) { + PrintAddress(&address); + } + else if (-1 == index) + { + failed = PR_TRUE; + PL_FPrintError(err, "PR_EnumerateHostEnt"); + } + } while (index > 0); + } + } + + + { + char buffer[PROTO_BUFFER]; + /* + ** Get Proto by name/number + */ + rv = PR_GetProtoByName("tcp", &buffer[1], sizeof(buffer) - 1, &proto); + rv = PR_GetProtoByNumber(6, &buffer[3], sizeof(buffer) - 3, &proto); + } + + return (failed) ? 1 : 0; +} diff --git a/nsprpub/pr/tests/join.c b/nsprpub/pr/tests/join.c new file mode 100644 index 0000000000..24238e6e05 --- /dev/null +++ b/nsprpub/pr/tests/join.c @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dbmalloc1.c +** +** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions. +** +** Modification History: +** +** 19-May-97 AGarcia - separate the four join tests into different unit test modules. +** AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" +#include "prttools.h" + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/*********************************************************************** +** PRIVATE FUNCTION: Test_Result +** DESCRIPTION: Used in conjunction with the regress tool, prints out the +** status of the test case. +** INPUTS: PASS/FAIL +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: Determine what the status is and print accordingly. +** +***********************************************************************/ + + +static void Test_Result (int result) +{ + if (result == PASS) { + printf ("PASS\n"); + } + else { + printf ("FAIL\n"); + } + exit (1); +} + + +/* + Program to test joining of threads. Two threads are created. One + to be waited upon until it has started. The other to join after it has + completed. +*/ + + +static void PR_CALLBACK lowPriority(void *arg) +{ +} + +static void PR_CALLBACK highPriority(void *arg) +{ +} + +static void PR_CALLBACK unjoinable(void *arg) +{ + PR_Sleep(PR_INTERVAL_NO_TIMEOUT); +} + +void runTest(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *low,*high; + + /* create the low and high priority threads */ + + low = PR_CreateThread(PR_USER_THREAD, + lowPriority, 0, + PR_PRIORITY_LOW, + scope1, + PR_JOINABLE_THREAD, + 0); + if (!low) { + if (debug_mode) { + printf("\tcannot create low priority thread\n"); + } + else { + Test_Result(FAIL); + } + return; + } + + high = PR_CreateThread(PR_USER_THREAD, + highPriority, 0, + PR_PRIORITY_HIGH, + scope2, + PR_JOINABLE_THREAD, + 0); + if (!high) { + if (debug_mode) { + printf("\tcannot create high priority thread\n"); + } + else { + Test_Result(FAIL); + } + return; + } + + /* Do the joining for both threads */ + if (PR_JoinThread(low) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join low priority thread\n"); + } + else { + Test_Result (FAIL); + } + return; + } else { + if (debug_mode) { + printf("\tjoined low priority thread\n"); + } + } + if (PR_JoinThread(high) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join high priority thread\n"); + } + else { + Test_Result(FAIL); + } + return; + } else { + if (debug_mode) { + printf("\tjoined high priority thread\n"); + } + } +} + +void joinWithUnjoinable(void) +{ + PRThread *thread; + + /* create the unjoinable thread */ + + thread = PR_CreateThread(PR_USER_THREAD, + unjoinable, 0, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (!thread) { + if (debug_mode) { + printf("\tcannot create unjoinable thread\n"); + } + else { + Test_Result(FAIL); + } + return; + } + + if (PR_JoinThread(thread) == PR_SUCCESS) { + if (debug_mode) { + printf("\tsuccessfully joined with unjoinable thread?!\n"); + } + else { + Test_Result(FAIL); + } + return; + } else { + if (debug_mode) { + printf("\tcannot join with unjoinable thread, as expected\n"); + } + if (PR_GetError() != PR_INVALID_ARGUMENT_ERROR) { + if (debug_mode) { + printf("\tWrong error code\n"); + } + else { + Test_Result(FAIL); + } + return; + } + } + if (PR_Interrupt(thread) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot interrupt unjoinable thread\n"); + } + else { + Test_Result(FAIL); + } + return; + } else { + if (debug_mode) { + printf("\tinterrupted unjoinable thread\n"); + } + } +} + +static PRIntn PR_CALLBACK RealMain(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + printf("User-User test\n"); + runTest(PR_LOCAL_THREAD, PR_LOCAL_THREAD); + printf("User-Kernel test\n"); + runTest(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); + printf("Kernel-User test\n"); + runTest(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); + printf("Kernel-Kernel test\n"); + runTest(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); + printf("Join with unjoinable thread\n"); + joinWithUnjoinable(); + + printf("PASSED\n"); + + return 0; +} + + + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/joinkk.c b/nsprpub/pr/tests/joinkk.c new file mode 100644 index 0000000000..4e7b2b5e14 --- /dev/null +++ b/nsprpub/pr/tests/joinkk.c @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dbmalloc1.c +** +** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions. +** +** Modification History: +** +** 19-May-97 AGarcia - separate the four join tests into different unit test modules. +** AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn failed_already=0; +PRIntn debug_mode; +/* + Program to test joining of threads. Two threads are created. One + to be waited upon until it has started. The other to join after it has + completed. +*/ + + +static void lowPriority(void *arg) +{ +} + +static void highPriority(void *arg) +{ +} + +void runTest(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *low,*high; + + /* create the low and high priority threads */ + + low = PR_CreateThread(PR_USER_THREAD, + lowPriority, 0, + PR_PRIORITY_LOW, + scope1, + PR_JOINABLE_THREAD, + 0); + if (!low) { + if (debug_mode) { + printf("\tcannot create low priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + high = PR_CreateThread(PR_USER_THREAD, + highPriority, 0, + PR_PRIORITY_HIGH, + scope2, + PR_JOINABLE_THREAD, + 0); + if (!high) { + if (debug_mode) { + printf("\tcannot create high priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + /* Do the joining for both threads */ + if (PR_JoinThread(low) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join low priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined low priority thread\n"); + } + } + if (PR_JoinThread(high) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join high priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined high priority thread\n"); + } + } +} + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + if (debug_mode) { + printf("Kernel-Kernel test\n"); + } + runTest(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); + + if(failed_already) + { + printf("FAIL\n"); + return 1; + } + else + { + printf("PASS\n"); + return 0; + } + +} + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/joinku.c b/nsprpub/pr/tests/joinku.c new file mode 100644 index 0000000000..cd83b6cb42 --- /dev/null +++ b/nsprpub/pr/tests/joinku.c @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dbmalloc1.c +** +** Description: Tests PR_SetMallocCountdown PR_ClearMallocCountdown functions. +** +** Modification History: +** +** 19-May-97 AGarcia - separate the four join tests into different unit test modules. +** AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + + +/* + Program to test joining of threads. Two threads are created. One + to be waited upon until it has started. The other to join after it has + completed. +*/ + + +static void lowPriority(void *arg) +{ +} + +static void highPriority(void *arg) +{ +} + +void runTest(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *low,*high; + + /* create the low and high priority threads */ + + low = PR_CreateThread(PR_USER_THREAD, + lowPriority, 0, + PR_PRIORITY_LOW, + scope1, + PR_JOINABLE_THREAD, + 0); + if (!low) { + if (debug_mode) { + printf("\tcannot create low priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + high = PR_CreateThread(PR_USER_THREAD, + highPriority, 0, + PR_PRIORITY_HIGH, + scope2, + PR_JOINABLE_THREAD, + 0); + if (!high) { + if (debug_mode) { + printf("\tcannot create high priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + /* Do the joining for both threads */ + if (PR_JoinThread(low) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join low priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined low priority thread\n"); + } + } + if (PR_JoinThread(high) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join high priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined high priority thread\n"); + } + } +} + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + /* main test */ + + if (debug_mode) { + printf("Kernel-User test\n"); + } + runTest(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); + + + if(failed_already) + { + printf("FAIL\n"); + return 1; + } + else + { + printf("PASS\n"); + return 0; + } + +} + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/joinuk.c b/nsprpub/pr/tests/joinuk.c new file mode 100644 index 0000000000..418c5e0e91 --- /dev/null +++ b/nsprpub/pr/tests/joinuk.c @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: joinuk.c +** +** Description: Join kernel - user +** +** Modification History: +** +** 19-May-97 AGarcia - separate the four join tests into different unit test modules. +** AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn failed_already=0; +PRIntn debug_mode; +/* + Program to test joining of threads. Two threads are created. One + to be waited upon until it has started. The other to join after it has + completed. +*/ + + +static void lowPriority(void *arg) +{ +} + +static void highPriority(void *arg) +{ +} + +void runTest(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *low,*high; + + /* create the low and high priority threads */ + + low = PR_CreateThread(PR_USER_THREAD, + lowPriority, 0, + PR_PRIORITY_LOW, + scope1, + PR_JOINABLE_THREAD, + 0); + if (!low) { + if (debug_mode) { + printf("\tcannot create low priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + high = PR_CreateThread(PR_USER_THREAD, + highPriority, 0, + PR_PRIORITY_HIGH, + scope2, + PR_JOINABLE_THREAD, + 0); + if (!high) { + if (debug_mode) { + printf("\tcannot create high priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + /* Do the joining for both threads */ + if (PR_JoinThread(low) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join low priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined low priority thread\n"); + } + } + if (PR_JoinThread(high) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join high priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined high priority thread\n"); + } + } +} + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + /* main test */ + + if (debug_mode) { + printf("User-Kernel test\n"); + } + runTest(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); + + + if(failed_already) + { + printf("FAIL\n"); + return 1; + } else + { + printf("PASS\n"); + return 0; + } +} + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/joinuu.c b/nsprpub/pr/tests/joinuu.c new file mode 100644 index 0000000000..4d30bfa7ce --- /dev/null +++ b/nsprpub/pr/tests/joinuu.c @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: dbmalloc1.c +** +** Description: Join tests user - user +** +** Modification History: +** +** 19-May-97 AGarcia - separate the four join tests into different unit test modules. +** AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + + +/* + Program to test joining of threads. Two threads are created. One + to be waited upon until it has started. The other to join after it has + completed. +*/ + + +static void lowPriority(void *arg) +{ +} + +static void highPriority(void *arg) +{ +} + +void runTest(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *low,*high; + + /* create the low and high priority threads */ + + low = PR_CreateThread(PR_USER_THREAD, + lowPriority, 0, + PR_PRIORITY_LOW, + scope1, + PR_JOINABLE_THREAD, + 0); + if (!low) { + if (debug_mode) { + printf("\tcannot create low priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + high = PR_CreateThread(PR_USER_THREAD, + highPriority, 0, + PR_PRIORITY_HIGH, + scope2, + PR_JOINABLE_THREAD, + 0); + if (!high) { + if (debug_mode) { + printf("\tcannot create high priority thread\n"); + } + else { + failed_already=1; + } + return; + } + + /* Do the joining for both threads */ + if (PR_JoinThread(low) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join low priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined low priority thread\n"); + } + } + if (PR_JoinThread(high) == PR_FAILURE) { + if (debug_mode) { + printf("\tcannot join high priority thread\n"); + } + else { + failed_already=1; + } + return; + } else { + if (debug_mode) { + printf("\tjoined high priority thread\n"); + } + } +} + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + /* main test */ + if (debug_mode) { + printf("User-User test\n"); + } + runTest(PR_LOCAL_THREAD, PR_LOCAL_THREAD); + + if(failed_already) + { + printf("FAIL\n"); + return 1; + } else + { + printf("PASS\n"); + return 0; + } + + +} + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/layer.c b/nsprpub/pr/tests/layer.c new file mode 100644 index 0000000000..8ad32eeaa8 --- /dev/null +++ b/nsprpub/pr/tests/layer.c @@ -0,0 +1,471 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prprf.h" +#include "prlog.h" +#include "prnetdb.h" +#include "prthread.h" + +#include "plerror.h" +#include "plgetopt.h" +#include "prwin16.h" + +#include <stdlib.h> +#include <string.h> + +/* +** Testing layering of I/O +** +** The layered server +** A thread that acts as a server. It creates a TCP listener with a dummy +** layer pushed on top. Then listens for incoming connections. Each connection +** request for connection will be layered as well, accept one request, echo +** it back and close. +** +** The layered client +** Pretty much what you'd expect. +*/ + +static PRFileDesc *logFile; +static PRDescIdentity identity; +static PRNetAddr server_address; + +static PRIOMethods myMethods; + +typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity; + +static PRIntn minor_iterations = 5; +static PRIntn major_iterations = 1; +static Verbosity verbosity = quiet; + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264; + +static PRFileDesc *PushLayer(PRFileDesc *stack) +{ + PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods); + PRStatus rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); + if (verbosity > quiet) { + PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack); + } + PR_ASSERT(PR_SUCCESS == rv); + return stack; +} /* PushLayer */ + +static PRFileDesc *PushNewLayers(PRFileDesc *stack) +{ + PRDescIdentity tmp_identity; + PRFileDesc *layer; + PRStatus rv; + + /* push a dummy layer */ + tmp_identity = PR_GetUniqueIdentity("Dummy 1"); + layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods()); + rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); + if (verbosity > quiet) + PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, + stack); + PR_ASSERT(PR_SUCCESS == rv); + + /* push a data processing layer */ + layer = PR_CreateIOLayerStub(identity, &myMethods); + rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); + if (verbosity > quiet) + PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, + stack); + PR_ASSERT(PR_SUCCESS == rv); + + /* push another dummy layer */ + tmp_identity = PR_GetUniqueIdentity("Dummy 2"); + layer = PR_CreateIOLayerStub(tmp_identity, PR_GetDefaultIOMethods()); + rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); + if (verbosity > quiet) + PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, + stack); + PR_ASSERT(PR_SUCCESS == rv); + return stack; +} /* PushLayer */ + +#if 0 +static PRFileDesc *PopLayer(PRFileDesc *stack) +{ + PRFileDesc *popped = PR_PopIOLayer(stack, identity); + if (verbosity > quiet) { + PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack); + } + popped->dtor(popped); + + return stack; +} /* PopLayer */ +#endif + +static void PR_CALLBACK Client(void *arg) +{ + PRStatus rv; + PRUint8 buffer[100]; + PRIntn empty_flags = 0; + PRIntn bytes_read, bytes_sent; + PRFileDesc *stack = (PRFileDesc*)arg; + + /* Initialize the buffer so that Purify won't complain */ + memset(buffer, 0, sizeof(buffer)); + + rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(PR_SUCCESS == rv); + while (minor_iterations-- > 0) + { + bytes_sent = PR_Send( + stack, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(sizeof(buffer) == bytes_sent); + if (verbosity > chatty) { + PR_fprintf(logFile, "Client sending %d bytes\n", bytes_sent); + } + bytes_read = PR_Recv( + stack, buffer, bytes_sent, empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) { + PR_fprintf(logFile, "Client receiving %d bytes\n", bytes_read); + } + PR_ASSERT(bytes_read == bytes_sent); + } + + if (verbosity > quiet) { + PR_fprintf(logFile, "Client shutting down stack\n"); + } + + rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); +} /* Client */ + +static void PR_CALLBACK Server(void *arg) +{ + PRStatus rv; + PRUint8 buffer[100]; + PRFileDesc *service; + PRUintn empty_flags = 0; + PRIntn bytes_read, bytes_sent; + PRFileDesc *stack = (PRFileDesc*)arg; + PRNetAddr client_address; + + service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > quiet) { + PR_fprintf(logFile, "Server accepting connection\n"); + } + + do + { + bytes_read = PR_Recv( + service, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (0 != bytes_read) + { + if (verbosity > chatty) { + PR_fprintf(logFile, "Server receiving %d bytes\n", bytes_read); + } + PR_ASSERT(bytes_read > 0); + bytes_sent = PR_Send( + service, buffer, bytes_read, empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) { + PR_fprintf(logFile, "Server sending %d bytes\n", bytes_sent); + } + PR_ASSERT(bytes_read == bytes_sent); + } + + } while (0 != bytes_read); + + if (verbosity > quiet) { + PR_fprintf(logFile, "Server shutting down and closing stack\n"); + } + rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); + +} /* Server */ + +static PRInt32 PR_CALLBACK MyRecv( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + char *b = (char*)buf; + PRFileDesc *lo = fd->lower; + PRInt32 rv, readin = 0, request = 0; + rv = lo->methods->recv(lo, &request, sizeof(request), flags, timeout); + if (verbosity > chatty) PR_fprintf( + logFile, "MyRecv sending permission for %d bytes\n", request); + if (0 < rv) + { + if (verbosity > chatty) PR_fprintf( + logFile, "MyRecv received permission request for %d bytes\n", request); + rv = lo->methods->send( + lo, &request, sizeof(request), flags, timeout); + if (0 < rv) + { + if (verbosity > chatty) PR_fprintf( + logFile, "MyRecv sending permission for %d bytes\n", request); + while (readin < request) + { + rv = lo->methods->recv( + lo, b + readin, amount - readin, flags, timeout); + if (rv <= 0) { + break; + } + if (verbosity > chatty) PR_fprintf( + logFile, "MyRecv received %d bytes\n", rv); + readin += rv; + } + rv = readin; + } + } + return rv; +} /* MyRecv */ + +static PRInt32 PR_CALLBACK MySend( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRFileDesc *lo = fd->lower; + const char *b = (const char*)buf; + PRInt32 rv, wroteout = 0, request; + if (verbosity > chatty) PR_fprintf( + logFile, "MySend asking permission to send %d bytes\n", amount); + rv = lo->methods->send(lo, &amount, sizeof(amount), flags, timeout); + if (0 < rv) + { + rv = lo->methods->recv( + lo, &request, sizeof(request), flags, timeout); + if (0 < rv) + { + PR_ASSERT(request == amount); + if (verbosity > chatty) PR_fprintf( + logFile, "MySend got permission to send %d bytes\n", request); + while (wroteout < request) + { + rv = lo->methods->send( + lo, b + wroteout, request - wroteout, flags, timeout); + if (rv <= 0) { + break; + } + if (verbosity > chatty) PR_fprintf( + logFile, "MySend wrote %d bytes\n", rv); + wroteout += rv; + } + rv = amount; + } + } + return rv; +} /* MySend */ + +static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta) +{ + PRIntn verbage = (PRIntn)verbosity + delta; + if (verbage < (PRIntn)silent) { + verbage = (PRIntn)silent; + } + else if (verbage > (PRIntn)noisy) { + verbage = (PRIntn)noisy; + } + return (Verbosity)verbage; +} /* ChangeVerbosity */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PRIntn mits; + PLOptStatus os; + PRFileDesc *client, *service; + PRFileDesc *client_stack, *service_stack; + PRNetAddr any_address; + const char *server_name = NULL; + const PRIOMethods *stubMethods; + PRThread *client_thread, *server_thread; + PRThreadScope thread_scope = PR_LOCAL_THREAD; + PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: + server_name = opt->value; + break; + case 'd': /* debug mode */ + if (verbosity < noisy) { + verbosity = ChangeVerbosity(verbosity, 1); + } + break; + case 'q': /* debug mode */ + if (verbosity > silent) { + verbosity = ChangeVerbosity(verbosity, -1); + } + break; + case 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'C': /* number of threads waiting */ + major_iterations = atoi(opt->value); + break; + case 'c': /* number of client threads */ + minor_iterations = atoi(opt->value); + break; + case 'p': /* default port */ + default_port = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + PR_STDIO_INIT(); + + logFile = PR_GetSpecialFD(PR_StandardError); + + identity = PR_GetUniqueIdentity("Dummy"); + stubMethods = PR_GetDefaultIOMethods(); + + /* + ** The protocol we're going to implement is one where in order to initiate + ** a send, the sender must first solicit permission. Therefore, every + ** send is really a send - receive - send sequence. + */ + myMethods = *stubMethods; /* first get the entire batch */ + myMethods.recv = MyRecv; /* then override the ones we care about */ + myMethods.send = MySend; /* then override the ones we care about */ + + if (NULL == server_name) + rv = PR_InitializeNetAddr( + PR_IpAddrLoopback, default_port, &server_address); + else + { + rv = PR_StringToNetAddr(server_name, &server_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_InitializeNetAddr( + PR_IpAddrNull, default_port, &server_address); + } + PR_ASSERT(PR_SUCCESS == rv); + + /* one type w/o layering */ + + mits = minor_iterations; + while (major_iterations-- > 0) + { + if (verbosity > silent) { + PR_fprintf(logFile, "Beginning non-layered test\n"); + } + client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); + service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); + rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); + + minor_iterations = mits; + server_thread = PR_CreateThread( + PR_USER_THREAD, Server, service, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != server_thread); + + client_thread = PR_CreateThread( + PR_USER_THREAD, Client, client, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != client_thread); + + rv = PR_JoinThread(client_thread); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(server_thread); + PR_ASSERT(PR_SUCCESS == rv); + + rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); + if (verbosity > silent) { + PR_fprintf(logFile, "Ending non-layered test\n"); + } + + /* with layering */ + if (verbosity > silent) { + PR_fprintf(logFile, "Beginning layered test\n"); + } + client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); + PushLayer(client); + service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); + PushLayer(service); + rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); + + minor_iterations = mits; + server_thread = PR_CreateThread( + PR_USER_THREAD, Server, service, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != server_thread); + + client_thread = PR_CreateThread( + PR_USER_THREAD, Client, client, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != client_thread); + + rv = PR_JoinThread(client_thread); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(server_thread); + PR_ASSERT(PR_SUCCESS == rv); + + rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); + /* with layering, using new style stack */ + if (verbosity > silent) + PR_fprintf(logFile, + "Beginning layered test with new style stack\n"); + client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); + client_stack = PR_CreateIOLayer(client); + PushNewLayers(client_stack); + service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); + service_stack = PR_CreateIOLayer(service); + PushNewLayers(service_stack); + rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); + + minor_iterations = mits; + server_thread = PR_CreateThread( + PR_USER_THREAD, Server, service_stack, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != server_thread); + + client_thread = PR_CreateThread( + PR_USER_THREAD, Client, client_stack, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != client_thread); + + rv = PR_JoinThread(client_thread); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(server_thread); + PR_ASSERT(PR_SUCCESS == rv); + + rv = PR_Close(client_stack); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(service_stack); PR_ASSERT(PR_SUCCESS == rv); + if (verbosity > silent) { + PR_fprintf(logFile, "Ending layered test\n"); + } + } + return 0; +} /* main */ + +/* layer.c */ diff --git a/nsprpub/pr/tests/lazyinit.c b/nsprpub/pr/tests/lazyinit.c new file mode 100644 index 0000000000..2a910f2051 --- /dev/null +++ b/nsprpub/pr/tests/lazyinit.c @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: lazyinit.c +** Description: Testing lazy initialization +** +** Since you only get to initialize once, you have to rerun the test +** for each test case. The test cases are numbered. If you want to +** add more tests, take the next number and add it to the switch +** statement. +** +** This test is problematic on systems that don't support the notion +** of console output. The workarounds to emulate that feature include +** initializations themselves, which defeats the purpose here. +*/ + +#include "prcvar.h" +#include "prenv.h" +#include "prinit.h" +#include "prinrval.h" +#include "prio.h" +#include "prlock.h" +#include "prlog.h" +#include "prthread.h" +#include "prtypes.h" + +#include <stdio.h> +#include <stdlib.h> + +static void PR_CALLBACK lazyEntry(void *arg) +{ + PR_ASSERT(NULL == arg); +} /* lazyEntry */ + + +int main(int argc, char **argv) +{ + PRUintn pdkey; + PRStatus status; + char *path = NULL; + PRDir *dir = NULL; + PRLock *ml = NULL; + PRCondVar *cv = NULL; + PRThread *thread = NULL; + PRIntervalTime interval = 0; + PRFileDesc *file, *udp, *tcp, *pair[2]; + PRIntn test; + + if ( argc < 2) + { + test = 0; + } + else { + test = atoi(argv[1]); + } + + switch (test) + { + case 0: ml = PR_NewLock(); + break; + + case 1: interval = PR_SecondsToInterval(1); + break; + + case 2: thread = PR_CreateThread( + PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + break; + + case 3: file = PR_Open("./tmp-", PR_RDONLY, 0); + break; + + case 4: udp = PR_NewUDPSocket(); + break; + + case 5: tcp = PR_NewTCPSocket(); + break; + + case 6: dir = PR_OpenDir("./tmp-"); + break; + + case 7: (void)PR_NewThreadPrivateIndex(&pdkey, NULL); + break; + + case 8: path = PR_GetEnv("PATH"); + break; + + case 9: status = PR_NewTCPSocketPair(pair); + break; + + case 10: PR_SetConcurrency(2); + break; + + default: + printf( + "lazyinit: unrecognized command line argument: %s\n", + argv[1] ); + printf( "FAIL\n" ); + exit( 1 ); + break; + } /* switch() */ + return 0; +} /* Lazy */ + +/* lazyinit.c */ diff --git a/nsprpub/pr/tests/libfilename.c b/nsprpub/pr/tests/libfilename.c new file mode 100644 index 0000000000..09d0980de3 --- /dev/null +++ b/nsprpub/pr/tests/libfilename.c @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: libfilename.c +** +** Description: test PR_GetLibraryFilePathname. +** +***********************************************************************/ + +#include "nspr.h" +#include "pprio.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRBool debug_mode = PR_FALSE; + +static PRStatus RunTest(const char *name, PRFuncPtr addr) +{ + char *pathname; + PRFileDesc *fd; + + pathname = PR_GetLibraryFilePathname(name, addr); + if (pathname == NULL) { + fprintf(stderr, "PR_GetLibraryFilePathname failed\n"); + /* we let this test pass if this function is not implemented */ + if (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR) { + return PR_SUCCESS; + } + return PR_FAILURE; + } + + if (debug_mode) { + printf("Pathname is %s\n", pathname); + } + fd = PR_OpenFile(pathname, PR_RDONLY, 0); + if (fd == NULL) { + fprintf(stderr, "PR_Open failed: %d\n", (int)PR_GetError()); + return PR_FAILURE; + } + if (PR_Close(fd) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed: %d\n", (int)PR_GetError()); + return PR_FAILURE; + } + PR_Free(pathname); + return PR_SUCCESS; +} + +int main(int argc, char **argv) +{ + char *name; + PRFuncPtr addr; + PRLibrary *lib; + PRBool failed = PR_FALSE; + + if (argc >= 2 && strcmp(argv[1], "-d") == 0) { + debug_mode = PR_TRUE; + } + + /* First test a library that is implicitly linked. */ +#ifdef WINNT + name = PR_Malloc(strlen("libnspr4.dll")+1); + strcpy(name, "libnspr4.dll"); +#else + name = PR_GetLibraryName(NULL, "nspr4"); +#endif + addr = (PRFuncPtr)PR_GetTCPMethods()->close; + if (RunTest(name, addr) == PR_FAILURE) { + failed = PR_TRUE; + } + PR_FreeLibraryName(name); + + /* Next test a library that is dynamically loaded. */ + name = PR_GetLibraryName("dll", "my"); + if (debug_mode) { + printf("Loading library %s\n", name); + } + lib = PR_LoadLibrary(name); + if (!lib) { + fprintf(stderr, "PR_LoadLibrary failed\n"); + exit(1); + } + PR_FreeLibraryName(name); + name = PR_GetLibraryName(NULL, "my"); + addr = PR_FindFunctionSymbol(lib, "My_GetValue"); + if (RunTest(name, addr) == PR_FAILURE) { + failed = PR_TRUE; + } + PR_FreeLibraryName(name); + PR_UnloadLibrary(lib); + if (failed) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/lltest.c b/nsprpub/pr/tests/lltest.c new file mode 100644 index 0000000000..26f7d8eadf --- /dev/null +++ b/nsprpub/pr/tests/lltest.c @@ -0,0 +1,969 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** testll.c -- test suite for 64bit integer (longlong) operations +** +** Summary: testll [-d] | [-h] +** +** Where: +** -d set debug mode on; displays individual test failures +** -v verbose mode; displays progress in test, plus -d +** -h gives usage message. +** +** Description: +** lltest.c tests the functions defined in NSPR 2.0's prlong.h. +** +** Successive tests begin to depend on other LL functions working +** correctly. So, ... Do not change the order of the tests as run +** from main(). +** +** Caveats: +** Do not even begin to think that this is an exhaustive test! +** +** These tests try a little of everything, but not all boundary +** conditions and limits are tested. +** You want better coverage? ... Add it. +** +** --- +** Author: Lawrence Hardiman <larryh@netscape.com>. +** --- +** Revision History: +** 01-Oct-1997. Original implementation. +** +*/ + +#include "nspr.h" +#include "plgetopt.h" + +/* --- Local Definitions --- */ +#define ReportProgress(m) if (verboseMode) PR_fprintf(output, (m)); + + +/* --- Global variables --- */ +static PRIntn failedAlready = 0; +static PRFileDesc* output = NULL; +static PRBool debugMode = PR_FALSE; +static PRBool verboseMode = PR_FALSE; + +/* +** Constants used in tests. +*/ +const PRInt64 bigZero = LL_INIT( 0, 0 ); +const PRInt64 bigOne = LL_INIT( 0, 1 ); +const PRInt64 bigTwo = LL_INIT( 0, 2 ); +const PRInt64 bigSixTeen = LL_INIT( 0, 16 ); +const PRInt64 bigThirtyTwo = LL_INIT( 0, 32 ); +const PRInt64 bigMinusOne = LL_INIT( 0xffffffff, 0xffffffff ); +const PRInt64 bigMinusTwo = LL_INIT( 0xffffffff, 0xfffffffe ); +const PRInt64 bigNumber = LL_INIT( 0x7fffffff, 0xffffffff ); +const PRInt64 bigMinusNumber = LL_INIT( 0x80000000, 0x00000001 ); +const PRInt64 bigMaxInt32 = LL_INIT( 0x00000000, 0x7fffffff ); +const PRInt64 big2To31 = LL_INIT( 0x00000000, 0x80000000 ); +const PRUint64 bigZeroFox = LL_INIT( 0x00000000, 0xffffffff ); +const PRUint64 bigFoxFox = LL_INIT( 0xffffffff, 0xffffffff ); +const PRUint64 bigFoxZero = LL_INIT( 0xffffffff, 0x00000000 ); +const PRUint64 bigEightZero = LL_INIT( 0x80000000, 0x00000000 ); +const PRUint64 big64K = LL_INIT( 0x00000000, 0x00010000 ); +const PRInt64 bigInt0 = LL_INIT( 0x01a00000, 0x00001000 ); +const PRInt64 bigInt1 = LL_INIT( 0x01a00000, 0x00001100 ); +const PRInt64 bigInt2 = LL_INIT( 0x01a00000, 0x00000100 ); +const PRInt64 bigInt3 = LL_INIT( 0x01a00001, 0x00001000 ); +const PRInt64 bigInt4 = LL_INIT( 0x01a00001, 0x00001100 ); +const PRInt64 bigInt5 = LL_INIT( 0x01a00001, 0x00000100 ); +const PRInt64 bigInt6 = LL_INIT( 0xb1a00000, 0x00001000 ); +const PRInt64 bigInt7 = LL_INIT( 0xb1a00000, 0x00001100 ); +const PRInt64 bigInt8 = LL_INIT( 0xb1a00000, 0x00000100 ); +const PRInt64 bigInt9 = LL_INIT( 0xb1a00001, 0x00001000 ); +const PRInt64 bigInt10 = LL_INIT( 0xb1a00001, 0x00001100 ); +const PRInt64 bigInt11 = LL_INIT( 0xb1a00001, 0x00000100 ); +const PRInt32 one = 1l; +const PRInt32 minusOne = -1l; +const PRInt32 sixteen = 16l; +const PRInt32 thirtyTwo = 32l; +const PRInt32 sixtyThree = 63l; + +/* +** SetFailed() -- Report individual test failure +** +*/ +static void +SetFailed( char *what, char *how ) +{ + failedAlready = 1; + if ( debugMode ) { + PR_fprintf(output, "%s: failed: %s\n", what, how ); + } + return; +} + +static void +ResultFailed( char *what, char *how, PRInt64 expected, PRInt64 got) +{ + if ( debugMode) + { + SetFailed( what, how ); + PR_fprintf(output, "Expected: 0x%llx Got: 0x%llx\n", expected, got ); + } + return; +} + + +/* +** TestAssignment() -- Test the assignment +*/ +static void TestAssignment( void ) +{ + PRInt64 zero = LL_Zero(); + PRInt64 min = LL_MinInt(); + PRInt64 max = LL_MaxInt(); + if (!LL_EQ(zero, bigZero)) { + SetFailed("LL_EQ(zero, bigZero)", "!="); + } + if (!LL_CMP(max, >, min)) { + SetFailed("LL_CMP(max, >, min)", "!>"); + } +} + +/* +** TestComparisons() -- Test the longlong comparison operations +*/ +static void +TestComparisons( void ) +{ + ReportProgress("Testing Comparisons Operations\n"); + + /* test for zero */ + if ( !LL_IS_ZERO( bigZero )) { + SetFailed( "LL_IS_ZERO", "Zero is not zero" ); + } + + if ( LL_IS_ZERO( bigOne )) { + SetFailed( "LL_IS_ZERO", "One tests as zero" ); + } + + if ( LL_IS_ZERO( bigMinusOne )) { + SetFailed( "LL_IS_ZERO", "Minus One tests as zero" ); + } + + /* test equal */ + if ( !LL_EQ( bigZero, bigZero )) { + SetFailed( "LL_EQ", "zero EQ zero"); + } + + if ( !LL_EQ( bigOne, bigOne )) { + SetFailed( "LL_EQ", "one EQ one" ); + } + + if ( !LL_EQ( bigNumber, bigNumber )) { + SetFailed( "LL_EQ", "bigNumber EQ bigNumber" ); + } + + if ( !LL_EQ( bigMinusOne, bigMinusOne )) { + SetFailed( "LL_EQ", "minus one EQ minus one"); + } + + if ( LL_EQ( bigZero, bigOne )) { + SetFailed( "LL_EQ", "zero EQ one"); + } + + if ( LL_EQ( bigOne, bigZero )) { + SetFailed( "LL_EQ", "one EQ zero" ); + } + + if ( LL_EQ( bigMinusOne, bigOne )) { + SetFailed( "LL_EQ", "minus one EQ one"); + } + + if ( LL_EQ( bigNumber, bigOne )) { + SetFailed( "LL_EQ", "bigNumber EQ one"); + } + + /* test not equal */ + if ( LL_NE( bigZero, bigZero )) { + SetFailed( "LL_NE", "0 NE 0"); + } + + if ( LL_NE( bigOne, bigOne )) { + SetFailed( "LL_NE", "1 NE 1"); + } + + if ( LL_NE( bigMinusOne, bigMinusOne )) { + SetFailed( "LL_NE", "-1 NE -1"); + } + + if ( LL_NE( bigNumber, bigNumber )) { + SetFailed( "LL_NE", "n NE n"); + } + + if ( LL_NE( bigMinusNumber, bigMinusNumber )) { + SetFailed( "LL_NE", "-n NE -n"); + } + + if ( !LL_NE( bigZero, bigOne)) { + SetFailed( "LL_NE", "0 NE 1"); + } + + if ( !LL_NE( bigOne, bigMinusNumber)) { + SetFailed( "LL_NE", "1 NE -n"); + } + + /* Greater than or equal to zero */ + if ( !LL_GE_ZERO( bigZero )) { + SetFailed( "LL_GE_ZERO", "0"); + } + + if ( !LL_GE_ZERO( bigOne )) { + SetFailed( "LL_GE_ZERO", "1"); + } + + if ( !LL_GE_ZERO( bigNumber )) { + SetFailed( "LL_GE_ZERO", "n"); + } + + if ( LL_GE_ZERO( bigMinusOne )) { + SetFailed( "LL_GE_ZERO", "-1"); + } + + if ( LL_GE_ZERO( bigMinusNumber )) { + SetFailed( "LL_GE_ZERO", "-n"); + } + + /* Algebraic Compare two values */ + if ( !LL_CMP( bigZero, ==, bigZero )) { + SetFailed( "LL_CMP", "0 == 0"); + } + + if ( LL_CMP( bigZero, >, bigZero )) { + SetFailed( "LL_CMP", "0 > 0"); + } + + if ( LL_CMP( bigZero, <, bigZero )) { + SetFailed( "LL_CMP", "0 < 0"); + } + + if ( LL_CMP( bigNumber, <, bigOne )) { + SetFailed( "LL_CMP", "n < 1"); + } + + if ( !LL_CMP( bigNumber, >, bigOne )) { + SetFailed( "LL_CMP", "n <= 1"); + } + + if ( LL_CMP( bigOne, >, bigNumber )) { + SetFailed( "LL_CMP", "1 > n"); + } + + if ( LL_CMP( bigMinusNumber, >, bigNumber )) { + SetFailed( "LL_CMP", "-n > n"); + } + + if ( LL_CMP( bigNumber, !=, bigNumber)) { + SetFailed( "LL_CMP", "n != n"); + } + + if ( !LL_CMP( bigMinusOne, >, bigMinusTwo )) { + SetFailed( "LL_CMP", "-1 <= -2"); + } + + if ( !LL_CMP( bigMaxInt32, <, big2To31 )) { + SetFailed( "LL_CMP", "Max 32-bit signed int >= 2^31"); + } + + /* Two positive numbers */ + if ( !LL_CMP( bigInt0, <=, bigInt0 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt0, <=, bigInt1 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( LL_CMP( bigInt0, <=, bigInt2 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt0, <=, bigInt3 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt0, <=, bigInt4 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt0, <=, bigInt5 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + /* Two negative numbers */ + if ( !LL_CMP( bigInt6, <=, bigInt6 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt6, <=, bigInt7 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( LL_CMP( bigInt6, <=, bigInt8 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt6, <=, bigInt9 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt6, <=, bigInt10 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( !LL_CMP( bigInt6, <=, bigInt11 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + /* One positive, one negative */ + if ( LL_CMP( bigInt0, <=, bigInt6 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( LL_CMP( bigInt0, <=, bigInt7 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + if ( LL_CMP( bigInt0, <=, bigInt8 )) { + SetFailed( "LL_CMP", "LL_CMP(<=) failed"); + } + + /* Bitwise Compare two numbers */ + if ( !LL_UCMP( bigZero, ==, bigZero )) { + SetFailed( "LL_UCMP", "0 == 0"); + } + + if ( LL_UCMP( bigZero, >, bigZero )) { + SetFailed( "LL_UCMP", "0 > 0"); + } + + if ( LL_UCMP( bigZero, <, bigZero )) { + SetFailed( "LL_UCMP", "0 < 0"); + } + + if ( LL_UCMP( bigNumber, <, bigOne )) { + SetFailed( "LL_UCMP", "n < 1"); + } + + if ( !LL_UCMP( bigNumber, >, bigOne )) { + SetFailed( "LL_UCMP", "n < 1"); + } + + if ( LL_UCMP( bigOne, >, bigNumber )) { + SetFailed( "LL_UCMP", "1 > n"); + } + + if ( LL_UCMP( bigMinusNumber, <, bigNumber )) { + SetFailed( "LL_UCMP", "-n < n"); + } + + /* Two positive numbers */ + if ( !LL_UCMP( bigInt0, <=, bigInt0 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt0, <=, bigInt1 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( LL_UCMP( bigInt0, <=, bigInt2 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt0, <=, bigInt3 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt0, <=, bigInt4 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt0, <=, bigInt5 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + /* Two negative numbers */ + if ( !LL_UCMP( bigInt6, <=, bigInt6 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt6, <=, bigInt7 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( LL_UCMP( bigInt6, <=, bigInt8 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt6, <=, bigInt9 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt6, <=, bigInt10 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt6, <=, bigInt11 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + /* One positive, one negative */ + if ( !LL_UCMP( bigInt0, <=, bigInt6 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt0, <=, bigInt7 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + if ( !LL_UCMP( bigInt0, <=, bigInt8 )) { + SetFailed( "LL_UCMP", "LL_UCMP(<=) failed"); + } + + return; +} + +/* +** TestLogicalOperations() -- Tests for AND, OR, ... +** +*/ +static void +TestLogicalOperations( void ) +{ + PRUint64 result, result2; + + ReportProgress("Testing Logical Operations\n"); + + /* Test AND */ + LL_AND( result, bigZero, bigZero ); + if ( !LL_IS_ZERO( result )) { + ResultFailed( "LL_AND", "0 & 0", bigZero, result ); + } + + LL_AND( result, bigOne, bigOne ); + if ( LL_IS_ZERO( result )) { + ResultFailed( "LL_AND", "1 & 1", bigOne, result ); + } + + LL_AND( result, bigZero, bigOne ); + if ( !LL_IS_ZERO( result )) { + ResultFailed( "LL_AND", "1 & 1", bigZero, result ); + } + + LL_AND( result, bigMinusOne, bigMinusOne ); + if ( !LL_UCMP( result, ==, bigMinusOne )) { + ResultFailed( "LL_AND", "-1 & -1", bigMinusOne, result ); + } + + /* test OR */ + LL_OR( result, bigZero, bigZero ); + if ( !LL_IS_ZERO( result )) { + ResultFailed( "LL_OR", "0 | 1", bigZero, result); + } + + LL_OR( result, bigZero, bigOne ); + if ( LL_IS_ZERO( result )) { + ResultFailed( "LL_OR", "0 | 1", bigOne, result ); + } + + LL_OR( result, bigZero, bigMinusNumber ); + if ( !LL_UCMP( result, ==, bigMinusNumber )) { + ResultFailed( "LL_OR", "0 | -n", bigMinusNumber, result); + } + + LL_OR( result, bigMinusNumber, bigZero ); + if ( !LL_UCMP( result, ==, bigMinusNumber )) { + ResultFailed( "LL_OR", "-n | 0", bigMinusNumber, result ); + } + + /* test XOR */ + LL_XOR( result, bigZero, bigZero ); + if ( LL_UCMP( result, !=, bigZero )) { + ResultFailed( "LL_XOR", "0 ^ 0", bigZero, result); + } + + LL_XOR( result, bigOne, bigZero ); + if ( LL_UCMP( result, !=, bigOne )) { + ResultFailed( "LL_XOR", "1 ^ 0", bigZero, result ); + } + + LL_XOR( result, bigMinusNumber, bigZero ); + if ( LL_UCMP( result, !=, bigMinusNumber )) { + ResultFailed( "LL_XOR", "-n ^ 0", bigMinusNumber, result ); + } + + LL_XOR( result, bigMinusNumber, bigMinusNumber ); + if ( LL_UCMP( result, !=, bigZero )) { + ResultFailed( "LL_XOR", "-n ^ -n", bigMinusNumber, result); + } + + /* test OR2. */ + result = bigZero; + LL_OR2( result, bigOne ); + if ( LL_UCMP( result, !=, bigOne )) { + ResultFailed( "LL_OR2", "(r=0) |= 1", bigOne, result); + } + + result = bigOne; + LL_OR2( result, bigNumber ); + if ( LL_UCMP( result, !=, bigNumber )) { + ResultFailed( "LL_OR2", "(r=1) |= n", bigNumber, result); + } + + result = bigMinusNumber; + LL_OR2( result, bigMinusNumber ); + if ( LL_UCMP( result, !=, bigMinusNumber )) { + ResultFailed( "LL_OR2", "(r=-n) |= -n", bigMinusNumber, result); + } + + /* test NOT */ + LL_NOT( result, bigMinusNumber); + LL_NOT( result2, result); + if ( LL_UCMP( result2, !=, bigMinusNumber )) { + ResultFailed( "LL_NOT", "r != ~(~-n)", bigMinusNumber, result); + } + + /* test Negation */ + LL_NEG( result, bigMinusNumber ); + LL_NEG( result2, result ); + if ( LL_CMP( result2, !=, bigMinusNumber )) { + ResultFailed( "LL_NEG", "r != -(-(-n))", bigMinusNumber, result); + } + + return; +} + + + +/* +** TestConversion() -- Test Conversion Operations +** +*/ +static void +TestConversion( void ) +{ + PRInt64 result; + PRInt64 resultU; + PRInt32 result32; + PRUint32 resultU32; + float resultF; + PRFloat64 resultD; + + ReportProgress("Testing Conversion Operations\n"); + + /* LL_L2I -- Convert to signed 32bit */ + LL_L2I(result32, bigOne ); + if ( result32 != one ) { + SetFailed( "LL_L2I", "r != 1"); + } + + LL_L2I(result32, bigMinusOne ); + if ( result32 != minusOne ) { + SetFailed( "LL_L2I", "r != -1"); + } + + /* LL_L2UI -- Convert 64bit to unsigned 32bit */ + LL_L2UI( resultU32, bigMinusOne ); + if ( resultU32 != (PRUint32) minusOne ) { + SetFailed( "LL_L2UI", "r != -1"); + } + + LL_L2UI( resultU32, bigOne ); + if ( resultU32 != (PRUint32) one ) { + SetFailed( "LL_L2UI", "r != 1"); + } + + /* LL_L2F -- Convert to 32bit floating point */ + LL_L2F( resultF, bigOne ); + if ( resultF != 1.0 ) { + SetFailed( "LL_L2F", "r != 1.0"); + } + + LL_L2F( resultF, bigMinusOne ); + if ( resultF != -1.0 ) { + SetFailed( "LL_L2F", "r != 1.0"); + } + + /* LL_L2D -- Convert to 64bit floating point */ + LL_L2D( resultD, bigOne ); + if ( resultD != 1.0L ) { + SetFailed( "LL_L2D", "r != 1.0"); + } + + LL_L2D( resultD, bigMinusOne ); + if ( resultD != -1.0L ) { + SetFailed( "LL_L2D", "r != -1.0"); + } + + /* LL_I2L -- Convert 32bit signed to 64bit signed */ + LL_I2L( result, one ); + if ( LL_CMP(result, !=, bigOne )) { + SetFailed( "LL_I2L", "r != 1"); + } + + LL_I2L( result, minusOne ); + if ( LL_CMP(result, !=, bigMinusOne )) { + SetFailed( "LL_I2L", "r != -1"); + } + + /* LL_UI2L -- Convert 32bit unsigned to 64bit unsigned */ + LL_UI2L( resultU, (PRUint32) one ); + if ( LL_CMP(resultU, !=, bigOne )) { + SetFailed( "LL_UI2L", "r != 1"); + } + + /* [lth.] This did not behave as expected, but it is correct + */ + LL_UI2L( resultU, (PRUint32) minusOne ); + if ( LL_CMP(resultU, !=, bigZeroFox )) { + ResultFailed( "LL_UI2L", "r != -1", bigZeroFox, resultU); + } + + /* LL_F2L -- Convert 32bit float to 64bit signed */ + LL_F2L( result, 1.0 ); + if ( LL_CMP(result, !=, bigOne )) { + SetFailed( "LL_F2L", "r != 1"); + } + + LL_F2L( result, -1.0 ); + if ( LL_CMP(result, !=, bigMinusOne )) { + SetFailed( "LL_F2L", "r != -1"); + } + + /* LL_D2L -- Convert 64bit Float to 64bit signed */ + LL_D2L( result, 1.0L ); + if ( LL_CMP(result, !=, bigOne )) { + SetFailed( "LL_D2L", "r != 1"); + } + + LL_D2L( result, -1.0L ); + if ( LL_CMP(result, !=, bigMinusOne )) { + SetFailed( "LL_D2L", "r != -1"); + } + + return; +} + +static void ShiftCompileOnly() +{ + /* + ** This function is only compiled, never called. + ** The real test is to see if it compiles w/o + ** warnings. This is no small feat, by the way. + */ + PRInt64 ia, ib; + PRUint64 ua, ub; + LL_SHR(ia, ib, 32); + LL_SHL(ia, ib, 32); + + LL_USHR(ua, ub, 32); + LL_ISHL(ia, 49, 32); + +} /* ShiftCompileOnly */ + + +/* +** TestShift() -- Test Shifting Operations +** +*/ +static void +TestShift( void ) +{ + static const PRInt64 largeTwoZero = LL_INIT( 0x00000002, 0x00000000 ); + PRInt64 result; + PRUint64 resultU; + + ReportProgress("Testing Shifting Operations\n"); + + /* LL_SHL -- Shift left algebraic */ + LL_SHL( result, bigOne, one ); + if ( LL_CMP( result, !=, bigTwo )) { + ResultFailed( "LL_SHL", "r != 2", bigOne, result ); + } + + LL_SHL( result, bigTwo, thirtyTwo ); + if ( LL_CMP( result, !=, largeTwoZero )) { + ResultFailed( "LL_SHL", "r != twoZero", largeTwoZero, result); + } + + /* LL_SHR -- Shift right algebraic */ + LL_SHR( result, bigFoxZero, thirtyTwo ); + if ( LL_CMP( result, !=, bigMinusOne )) { + ResultFailed( "LL_SHR", "r != -1", bigMinusOne, result); + } + + LL_SHR( result, bigTwo, one ); + if ( LL_CMP( result, !=, bigOne )) { + ResultFailed( "LL_SHR", "r != 1", bigOne, result); + } + + LL_SHR( result, bigFoxFox, thirtyTwo ); + if ( LL_CMP( result, !=, bigMinusOne )) { + ResultFailed( "LL_SHR", "r != -1 (was ff,ff)", bigMinusOne, result); + } + + /* LL_USHR -- Logical shift right */ + LL_USHR( resultU, bigZeroFox, thirtyTwo ); + if ( LL_UCMP( resultU, !=, bigZero )) { + ResultFailed( "LL_USHR", "r != 0 ", bigZero, result); + } + + LL_USHR( resultU, bigFoxFox, thirtyTwo ); + if ( LL_UCMP( resultU, !=, bigZeroFox )) { + ResultFailed( "LL_USHR", "r != 0 ", bigZeroFox, result); + } + + /* LL_ISHL -- Shift a 32bit integer into a 64bit result */ + LL_ISHL( resultU, minusOne, thirtyTwo ); + if ( LL_UCMP( resultU, !=, bigFoxZero )) { + ResultFailed( "LL_ISHL", "r != ff,00 ", bigFoxZero, result); + } + + LL_ISHL( resultU, one, sixtyThree ); + if ( LL_UCMP( resultU, !=, bigEightZero )) { + ResultFailed( "LL_ISHL", "r != 80,00 ", bigEightZero, result); + } + + LL_ISHL( resultU, one, sixteen ); + if ( LL_UCMP( resultU, !=, big64K )) { + ResultFailed( "LL_ISHL", "r != 64K ", big64K, resultU); + } + + return; +} + + +/* +** TestArithmetic() -- Test arithmetic operations. +** +*/ +static void +TestArithmetic( void ) +{ + PRInt64 largeVal = LL_INIT( 0x00000001, 0xffffffff ); + PRInt64 largeValPlusOne = LL_INIT( 0x00000002, 0x00000000 ); + PRInt64 largeValTimesTwo = LL_INIT( 0x00000003, 0xfffffffe ); + PRInt64 largeMultCand = LL_INIT( 0x00000000, 0x7fffffff ); + PRInt64 largeMinusMultCand = LL_INIT( 0xffffffff, 0x10000001 ); + PRInt64 largeMultCandx64K = LL_INIT( 0x00007fff, 0xffff0000 ); + PRInt64 largeNumSHL5 = LL_INIT( 0x0000001f, 0xffffffe0 ); + PRInt64 result, result2; + + /* Addition */ + LL_ADD( result, bigOne, bigOne ); + if ( LL_CMP( result, !=, bigTwo )) { + ResultFailed( "LL_ADD", "r != 1 + 1", bigTwo, result); + } + + LL_ADD( result, bigMinusOne, bigOne ); + if ( LL_CMP( result, !=, bigZero )) { + ResultFailed( "LL_ADD", "r != -1 + 1", bigOne, result); + } + + LL_ADD( result, largeVal, bigOne ); + if ( LL_CMP( result, !=, largeValPlusOne )) { + ResultFailed( "LL_ADD", "lVP1 != lV + 1", largeValPlusOne, result); + } + + /* Subtraction */ + LL_SUB( result, bigOne, bigOne ); + if ( LL_CMP( result, !=, bigZero )) { + ResultFailed( "LL_SUB", "r != 1 - 1", bigZero, result); + } + + LL_SUB( result, bigTwo, bigOne ); + if ( LL_CMP( result, !=, bigOne )) { + ResultFailed( "LL_SUB", "r != 2 - 1", bigOne, result); + } + + LL_SUB( result, largeValPlusOne, bigOne ); + if ( LL_CMP( result, !=, largeVal )) { + ResultFailed( "LL_SUB", "r != lVP1 - 1", largeVal, result); + } + + + /* Multiply */ + LL_MUL( result, largeVal, bigTwo ); + if ( LL_CMP( result, !=, largeValTimesTwo )) { + ResultFailed( "LL_MUL", "r != lV*2", largeValTimesTwo, result); + } + + LL_MUL( result, largeMultCand, big64K ); + if ( LL_CMP( result, !=, largeMultCandx64K )) { + ResultFailed( "LL_MUL", "r != lV*64K", largeMultCandx64K, result); + } + + LL_NEG( result2, largeMultCand ); + LL_MUL( result, largeMultCand, bigMinusOne ); + if ( LL_CMP( result, !=, result2 )) { + ResultFailed( "LL_MUL", "r != -lMC", result2, result); + } + + LL_SHL( result2, bigZeroFox, 5); + LL_MUL( result, bigZeroFox, bigThirtyTwo ); + if ( LL_CMP( result, !=, largeNumSHL5 )) { + ResultFailed( "LL_MUL", "r != 0f<<5", largeNumSHL5, result ); + } + + + + /* LL_DIV() Division */ + LL_DIV( result, bigOne, bigOne); + if ( LL_CMP( result, !=, bigOne )) { + ResultFailed( "LL_DIV", "1 != 1", bigOne, result); + } + + LL_DIV( result, bigNumber, bigOne ); + if ( LL_CMP( result, !=, bigNumber )) { + ResultFailed( "LL_DIV", "r != n / 1", bigNumber, result); + } + + LL_DIV( result, bigNumber, bigMinusOne ); + if ( LL_CMP( result, !=, bigMinusNumber )) { + ResultFailed( "LL_DIV", "r != n / -1", bigMinusNumber, result); + } + + LL_DIV( result, bigMinusNumber, bigMinusOne ); + if ( LL_CMP( result, !=, bigNumber )) { + ResultFailed( "LL_DIV", "r != -n / -1", bigNumber, result); + } + + LL_SHL( result2, bigZeroFox, 5 ); + LL_DIV( result, result2, bigOne ); + if ( LL_CMP( result, !=, result2 )) { + ResultFailed( "LL_DIV", "0f<<5 != 0f<<5", result2, result); + } + + LL_SHL( result2, bigZeroFox, 5 ); + LL_NEG( result2, result2 ); + LL_DIV( result, result2, bigOne ); + if ( LL_CMP( result, !=, result2 )) { + ResultFailed( "LL_DIV", "-0f<<5 != -0f<<5", result2, result); + } + + LL_SHL( result2, bigZeroFox, 17 ); + LL_DIV( result, result2, bigMinusOne ); + LL_NEG( result2, result2 ); + if ( LL_CMP( result, !=, result2 )) { + ResultFailed( "LL_DIV", "-0f<<17 != -0f<<17", result2, result); + } + + + /* LL_MOD() Modulo Division */ + LL_ADD( result2, bigThirtyTwo, bigOne ); + LL_MOD( result, result2, bigSixTeen ); + if ( LL_CMP( result, !=, bigOne )) { + ResultFailed( "LL_MOD", "r != 1", bigSixTeen, result); + } + + + LL_MUL( result2, bigZeroFox, bigThirtyTwo ); + LL_ADD( result2, result2, bigSixTeen); + LL_MOD( result, result2, bigThirtyTwo ); + if ( LL_CMP( result, !=, bigSixTeen )) { + ResultFailed( "LL_MOD", "r != 16", bigSixTeen, result); + } + + /* LL_UDIVMOD */ + LL_DIV( result, bigOne, bigOne); + if ( LL_CMP( result, !=, bigOne )) { + ResultFailed( "LL_DIV", "r != 16", bigSixTeen, result); + } + + + return; +} + +static void TestWellknowns(void) +{ + PRInt64 max = LL_MAXINT, min = LL_MININT, zero = LL_ZERO; + PRInt64 mmax = LL_MaxInt(), mmin = LL_MinInt(), mzero = LL_Zero(); + if (LL_NE(max, mmax)) { + ResultFailed( "max, mmax", "max != mmax", max, mmax); + } + if (LL_NE(min, mmin)) { + ResultFailed( "min, mmin", "min != mmin", max, mmin); + } + if (LL_NE(zero, mzero)) { + ResultFailed( "zero, mzero", "zero != mzero", zero, mzero); + } +} /* TestWellknowns */ + +/* +** Initialize() -- Initialize the test case +** +** Parse command line options +** +*/ +static PRIntn +Initialize( PRIntn argc, char **argv ) +{ + PLOptState *opt = PL_CreateOptState(argc, argv, "dvh"); + PLOptStatus os; + + /* + ** Parse command line options + */ + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* set debug mode */ + debugMode = PR_TRUE; + break; + + case 'v': /* set verbose mode */ + verboseMode = PR_TRUE; + debugMode = PR_TRUE; + break; + + case 'h': /* user wants some guidance */ + default: + PR_fprintf(output, "You get help.\n"); + return(1); + } + } + PL_DestroyOptState(opt); + return(0); +} + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + output = PR_GetSpecialFD(PR_StandardError); + + if ( Initialize( argc, argv )) { + return(1); + } + + TestAssignment(); + TestComparisons(); + TestLogicalOperations(); + TestConversion(); + TestShift(); + TestArithmetic(); + TestWellknowns(); + + /* + ** That's all folks! + */ + if ( failedAlready ) + { + PR_fprintf(output, "FAIL\n"); \ + } + else + { + PR_fprintf(output, "PASS\n"); \ + } + return failedAlready; +} /* end main() */ diff --git a/nsprpub/pr/tests/lock.c b/nsprpub/pr/tests/lock.c new file mode 100644 index 0000000000..83a4f41801 --- /dev/null +++ b/nsprpub/pr/tests/lock.c @@ -0,0 +1,548 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: lock.c +** Purpose: test basic locking functions +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +** +** 11-Aug-97 LarryH. Win16 port of NSPR. +** - Added "PASS", "FAIL" messages on completion. +** - Change stack variables to static scope variables +** because of shadow-stack use by Win16 +** - Added PR_CALLBACK attribute to functions called by NSPR +** - Added command line arguments: +** - l <num> to control the number of loops +** - c <num> to control the number of CPUs. +** (was positional argv). +** +** +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prio.h" +#include "prcmon.h" +#include "prinit.h" +#include "prinrval.h" +#include "prprf.h" +#include "prlock.h" +#include "prlog.h" +#include "prmon.h" +#include "prmem.h" +#include "prthread.h" +#include "prtypes.h" + +#include "plstr.h" + +#include <stdlib.h> + +#if defined(XP_UNIX) +#include <string.h> +#endif + +static PRIntn failed_already=0; +static PRFileDesc *std_err = NULL; +static PRBool verbosity = PR_FALSE; +static PRBool debug_mode = PR_FALSE; + +const static PRIntervalTime contention_interval = 50; + +typedef struct LockContentious_s { + PRLock *ml; + PRInt32 loops; + PRUint32 contender; + PRUint32 contentious; + PRIntervalTime overhead; + PRIntervalTime interval; +} LockContentious_t; + +typedef struct MonitorContentious_s { + PRMonitor *ml; + PRInt32 loops; + PRUint32 contender; + PRUint32 contentious; + PRIntervalTime overhead; + PRIntervalTime interval; +} MonitorContentious_t; + + +static PRIntervalTime Sleeper(PRUint32 loops) +{ + PRIntervalTime predicted = 0; + while (loops-- > 0) + { + predicted += contention_interval; + (void)PR_Sleep(contention_interval); + } + return predicted; +} /* Sleeper */ + +/* +** BASIC LOCKS +*/ +static PRIntervalTime MakeLock(PRUint32 loops) +{ + PRLock *ml = NULL; + while (loops-- > 0) + { + ml = PR_NewLock(); + PR_DestroyLock(ml); + ml = NULL; + } + return 0; +} /* MakeLock */ + +static PRIntervalTime NonContentiousLock(PRUint32 loops) +{ + PRLock *ml = NULL; + ml = PR_NewLock(); + while (loops-- > 0) + { + PR_Lock(ml); + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml); + PR_Unlock(ml); + } + PR_DestroyLock(ml); + return 0; +} /* NonContentiousLock */ + +static void PR_CALLBACK LockContender(void *arg) +{ + LockContentious_t *contention = (LockContentious_t*)arg; + while (contention->loops-- > 0) + { + PR_Lock(contention->ml); + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); + contention->contender+= 1; + contention->overhead += contention->interval; + PR_Sleep(contention->interval); + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); + PR_Unlock(contention->ml); + } +} /* LockContender */ + +static PRIntervalTime ContentiousLock(PRUint32 loops) +{ + PRStatus status; + PRThread *thread = NULL; + LockContentious_t * contention; + PRIntervalTime rv, overhead, timein = PR_IntervalNow(); + + contention = PR_NEWZAP(LockContentious_t); + contention->loops = loops; + contention->overhead = 0; + contention->ml = PR_NewLock(); + contention->interval = contention_interval; + thread = PR_CreateThread( + PR_USER_THREAD, LockContender, contention, + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_ASSERT(thread != NULL); + + overhead = PR_IntervalNow() - timein; + + while (contention->loops-- > 0) + { + PR_Lock(contention->ml); + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); + contention->contentious+= 1; + contention->overhead += contention->interval; + PR_Sleep(contention->interval); + PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml); + PR_Unlock(contention->ml); + } + + timein = PR_IntervalNow(); + status = PR_JoinThread(thread); + PR_DestroyLock(contention->ml); + overhead += (PR_IntervalNow() - timein); + rv = overhead + contention->overhead; + if (verbosity) + PR_fprintf( + std_err, "Access ratio: %u to %u\n", + contention->contentious, contention->contender); + PR_Free(contention); + return rv; +} /* ContentiousLock */ + +/* +** MONITORS +*/ +static PRIntervalTime MakeMonitor(PRUint32 loops) +{ + PRMonitor *ml = NULL; + while (loops-- > 0) + { + ml = PR_NewMonitor(); + PR_DestroyMonitor(ml); + ml = NULL; + } + return 0; +} /* MakeMonitor */ + +static PRIntervalTime NonContentiousMonitor(PRUint32 loops) +{ + PRMonitor *ml = NULL; + ml = PR_NewMonitor(); + while (loops-- > 0) + { + PR_EnterMonitor(ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); + PR_ExitMonitor(ml); + } + PR_DestroyMonitor(ml); + return 0; +} /* NonContentiousMonitor */ + +static void PR_CALLBACK TryEntry(void *arg) +{ + PRMonitor *ml = (PRMonitor*)arg; + if (debug_mode) { + PR_fprintf(std_err, "Reentrant thread created\n"); + } + PR_EnterMonitor(ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); + if (debug_mode) { + PR_fprintf(std_err, "Reentrant thread acquired monitor\n"); + } + PR_ExitMonitor(ml); + if (debug_mode) { + PR_fprintf(std_err, "Reentrant thread released monitor\n"); + } +} /* TryEntry */ + +static PRIntervalTime ReentrantMonitor(PRUint32 loops) +{ + PRStatus status; + PRThread *thread; + PRMonitor *ml = PR_NewMonitor(); + if (debug_mode) { + PR_fprintf(std_err, "\nMonitor created for reentrant test\n"); + } + + PR_EnterMonitor(ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); + PR_EnterMonitor(ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); + if (debug_mode) { + PR_fprintf(std_err, "Monitor acquired twice\n"); + } + + thread = PR_CreateThread( + PR_USER_THREAD, TryEntry, ml, + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_ASSERT(thread != NULL); + PR_Sleep(PR_SecondsToInterval(1)); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); + + PR_ExitMonitor(ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml); + if (debug_mode) { + PR_fprintf(std_err, "Monitor released first time\n"); + } + + PR_ExitMonitor(ml); + if (debug_mode) { + PR_fprintf(std_err, "Monitor released second time\n"); + } + + status = PR_JoinThread(thread); + if (debug_mode) PR_fprintf(std_err, + "Reentrant thread joined %s\n", + (status == PR_SUCCESS) ? "successfully" : "in error"); + + PR_DestroyMonitor(ml); + return 0; +} /* ReentrantMonitor */ + +static void PR_CALLBACK MonitorContender(void *arg) +{ + MonitorContentious_t *contention = (MonitorContentious_t*)arg; + while (contention->loops-- > 0) + { + PR_EnterMonitor(contention->ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); + contention->contender+= 1; + contention->overhead += contention->interval; + PR_Sleep(contention->interval); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); + PR_ExitMonitor(contention->ml); + } +} /* MonitorContender */ + +static PRUint32 ContentiousMonitor(PRUint32 loops) +{ + PRStatus status; + PRThread *thread = NULL; + MonitorContentious_t * contention; + PRIntervalTime rv, overhead, timein = PR_IntervalNow(); + + contention = PR_NEWZAP(MonitorContentious_t); + contention->loops = loops; + contention->overhead = 0; + contention->ml = PR_NewMonitor(); + contention->interval = contention_interval; + thread = PR_CreateThread( + PR_USER_THREAD, MonitorContender, contention, + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_ASSERT(thread != NULL); + + overhead = PR_IntervalNow() - timein; + + while (contention->loops-- > 0) + { + PR_EnterMonitor(contention->ml); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); + contention->contentious+= 1; + contention->overhead += contention->interval; + PR_Sleep(contention->interval); + PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml); + PR_ExitMonitor(contention->ml); + } + + timein = PR_IntervalNow(); + status = PR_JoinThread(thread); + PR_DestroyMonitor(contention->ml); + overhead += (PR_IntervalNow() - timein); + rv = overhead + contention->overhead; + if (verbosity) + PR_fprintf( + std_err, "Access ratio: %u to %u\n", + contention->contentious, contention->contender); + PR_Free(contention); + return rv; +} /* ContentiousMonitor */ + +/* +** CACHED MONITORS +*/ +static PRIntervalTime NonContentiousCMonitor(PRUint32 loops) +{ + MonitorContentious_t contention; + while (loops-- > 0) + { + PR_CEnterMonitor(&contention); + PR_CExitMonitor(&contention); + } + return 0; +} /* NonContentiousCMonitor */ + +static void PR_CALLBACK Contender(void *arg) +{ + MonitorContentious_t *contention = (MonitorContentious_t*)arg; + while (contention->loops-- > 0) + { + PR_CEnterMonitor(contention); + contention->contender+= 1; + contention->overhead += contention->interval; + PR_Sleep(contention->interval); + PR_CExitMonitor(contention); + } +} /* Contender */ + +static PRIntervalTime ContentiousCMonitor(PRUint32 loops) +{ + PRStatus status; + PRThread *thread = NULL; + MonitorContentious_t * contention; + PRIntervalTime overhead, timein = PR_IntervalNow(); + + contention = PR_NEWZAP(MonitorContentious_t); + contention->ml = NULL; + contention->loops = loops; + contention->interval = contention_interval; + thread = PR_CreateThread( + PR_USER_THREAD, Contender, contention, + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_ASSERT(thread != NULL); + + overhead = PR_IntervalNow() - timein; + + while (contention->loops-- > 0) + { + PR_CEnterMonitor(contention); + contention->contentious+= 1; + contention->overhead += contention->interval; + PR_Sleep(contention->interval); + PR_CExitMonitor(contention); + } + + timein = PR_IntervalNow(); + status = PR_JoinThread(thread); + overhead += (PR_IntervalNow() - timein); + overhead += overhead + contention->overhead; + if (verbosity) + PR_fprintf( + std_err, "Access ratio: %u to %u\n", + contention->contentious, contention->contender); + PR_Free(contention); + return overhead; +} /* ContentiousCMonitor */ + +static PRIntervalTime Test( + const char* msg, PRUint32 (*test)(PRUint32 loops), + PRUint32 loops, PRIntervalTime overhead) +{ + /* + * overhead - overhead not measured by the test. + * duration - wall clock time it took to perform test. + * predicted - extra time test says should not be counted + * + * Time accountable to the test is duration - overhead - predicted + * All times are Intervals and accumulated for all iterations. + */ + PRFloat64 elapsed; + PRIntervalTime accountable, duration; + PRUintn spaces = PL_strlen(msg); + PRIntervalTime timeout, timein = PR_IntervalNow(); + PRIntervalTime predicted = test(loops); + timeout = PR_IntervalNow(); + duration = timeout - timein; + + if (debug_mode) + { + accountable = duration - predicted; + accountable -= overhead; + elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable); + PR_fprintf(PR_STDOUT, "%s:", msg); + while (spaces++ < 50) { + PR_fprintf(PR_STDOUT, " "); + } + if ((PRInt32)accountable < 0) { + PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n"); + } + else { + PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops); + } + } + return duration; +} /* Test */ + +int main(int argc, char **argv) +{ + PRBool rv = PR_TRUE; + PRIntervalTime duration; + PRUint32 cpu, cpus = 2, loops = 100; + + + PR_STDIO_INIT(); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + { + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Command line argument -l <num> sets the number of loops. + Command line argument -c <num> sets the number of cpus. + Usage: lock [-d] [-l <num>] [-c <num>] + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:"); + 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 'v': /* debug mode */ + verbosity = PR_TRUE; + break; + case 'l': /* number of loops */ + loops = atoi(opt->value); + break; + case 'c': /* number of cpus */ + cpus = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } + + /* main test */ + PR_SetConcurrency(8); + + if (loops == 0) { + loops = 100; + } + if (debug_mode) + { + std_err = PR_STDERR; + PR_fprintf(std_err, "Lock: Using %d loops\n", loops); + } + + if (cpus == 0) { + cpus = 2; + } + if (debug_mode) { + PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus); + } + + (void)Sleeper(10); /* try filling in the caches */ + + for (cpu = 1; cpu <= cpus; ++cpu) + { + if (debug_mode) { + PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu); + } + PR_SetConcurrency(cpu); + + duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0); + duration = 0; + + (void)Test("Lock creation/deletion", MakeLock, loops, 0); + (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0); + (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration); + (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0); + (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0); + (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration); + + (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0); + (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration); + + (void)ReentrantMonitor(loops); + } + + if (debug_mode) + PR_fprintf( + std_err, "%s: test %s\n", "Lock(mutex) test", + ((rv) ? "passed" : "failed")); + else { + if (!rv) { + failed_already=1; + } + } + + if(failed_already) + { + PR_fprintf(PR_STDOUT, "FAIL\n"); + return 1; + } + else + { + PR_fprintf(PR_STDOUT, "PASS\n"); + return 0; + } + +} /* main */ + +/* testlock.c */ diff --git a/nsprpub/pr/tests/lockfile.c b/nsprpub/pr/tests/lockfile.c new file mode 100644 index 0000000000..883ef0d615 --- /dev/null +++ b/nsprpub/pr/tests/lockfile.c @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: lockfile.c +** Purpose: test basic locking functions +** Just because this times stuff, don't think its a perforamnce +** test!!! +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prcmon.h" +#include "prerror.h" +#include "prinit.h" +#include "prinrval.h" +#include "prlock.h" +#include "prlog.h" +#include "prmon.h" +#include "prthread.h" +#include "prtypes.h" + +#include "private/pprio.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +const static PRIntervalTime contention_interval = 50; + +typedef struct LockContentious_s { + PRLock *ml; + PRInt32 loops; + PRIntervalTime overhead; + PRIntervalTime interval; +} LockContentious_t; + +#define LOCKFILE "prlock.fil" + + + +static PRIntervalTime NonContentiousLock(PRInt32 loops) +{ + PRFileDesc *_lockfile; + while (loops-- > 0) + { + _lockfile = PR_Open(LOCKFILE, PR_CREATE_FILE|PR_RDWR, 0666); + if (!_lockfile) { + if (debug_mode) printf( + "could not create lockfile: %d [%d]\n", + PR_GetError(), PR_GetOSError()); + return PR_INTERVAL_NO_TIMEOUT; + } + PR_LockFile(_lockfile); + PR_UnlockFile(_lockfile); + PR_Close(_lockfile); + } + return 0; +} /* NonContentiousLock */ + +static void PR_CALLBACK LockContender(void *arg) +{ + LockContentious_t *contention = (LockContentious_t*)arg; + PRFileDesc *_lockfile; + while (contention->loops-- > 0) + { + _lockfile = PR_Open(LOCKFILE, PR_CREATE_FILE|PR_RDWR, 0666); + if (!_lockfile) { + if (debug_mode) printf( + "could not create lockfile: %d [%d]\n", + PR_GetError(), PR_GetOSError()); + break; + } + PR_LockFile(_lockfile); + PR_Sleep(contention->interval); + PR_UnlockFile(_lockfile); + PR_Close(_lockfile); + } + +} /* LockContender */ + +/* +** Win16 requires things passed to Threads not be on the stack +*/ +static LockContentious_t contention; + +static PRIntervalTime ContentiousLock(PRInt32 loops) +{ + PRStatus status; + PRThread *thread = NULL; + PRIntervalTime overhead, timein = PR_IntervalNow(); + + contention.loops = loops; + contention.overhead = 0; + contention.ml = PR_NewLock(); + contention.interval = contention_interval; + thread = PR_CreateThread( + PR_USER_THREAD, LockContender, &contention, + PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_ASSERT(thread != NULL); + + overhead = PR_IntervalNow() - timein; + + while (contention.loops > 0) + { + PR_Lock(contention.ml); + contention.overhead += contention.interval; + PR_Sleep(contention.interval); + PR_Unlock(contention.ml); + } + + timein = PR_IntervalNow(); + status = PR_JoinThread(thread); + PR_DestroyLock(contention.ml); + overhead += (PR_IntervalNow() - timein); + return overhead + contention.overhead; +} /* ContentiousLock */ + +static PRIntervalTime Test( + const char* msg, PRIntervalTime (*test)(PRInt32 loops), + PRInt32 loops, PRIntervalTime overhead) +{ + /* + * overhead - overhead not measured by the test. + * duration - wall clock time it took to perform test. + * predicted - extra time test says should not be counted + * + * Time accountable to the test is duration - overhead - predicted + * All times are Intervals and accumulated for all iterations. + */ + PRFloat64 elapsed; + PRIntervalTime accountable, duration; + PRUintn spaces = strlen(msg); + PRIntervalTime timeout, timein = PR_IntervalNow(); + PRIntervalTime predicted = test(loops); + timeout = PR_IntervalNow(); + duration = timeout - timein; + accountable = duration - predicted; + accountable -= overhead; + elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable); + if (debug_mode) { + printf("%s:", msg); + } + while (spaces++ < 50) if (debug_mode) { + printf(" "); + } + if ((PRInt32)accountable < 0) { + if (debug_mode) { + printf("*****.** usecs/iteration\n"); + } + } else { + if (debug_mode) { + printf("%8.2f usecs/iteration\n", elapsed/loops); + } + } + return duration; +} /* Test */ + +int main(int argc, char **argv) +{ + PRIntervalTime duration; + PRUint32 cpu, cpus = 2; + PRInt32 loops = 100; + + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (argc > 1) { + loops = atoi(argv[1]); + } + if (loops == 0) { + loops = 100; + } + if (debug_mode) { + printf("Lock: Using %d loops\n", loops); + } + + cpus = (argc < 3) ? 2 : atoi(argv[2]); + if (cpus == 0) { + cpus = 2; + } + if (debug_mode) { + printf("Lock: Using %d cpu(s)\n", cpus); + } + + + for (cpu = 1; cpu <= cpus; ++cpu) + { + if (debug_mode) { + printf("\nLockFile: Using %d CPU(s)\n", cpu); + } + PR_SetConcurrency(cpu); + + duration = Test("LockFile non-contentious locking/unlocking", NonContentiousLock, loops, 0); + (void)Test("LockFile contentious locking/unlocking", ContentiousLock, loops, duration); + } + + PR_Delete(LOCKFILE); /* try to get rid of evidence */ + + if (debug_mode) { + printf("%s: test %s\n", "Lock(mutex) test", ((failed_already) ? "failed" : "passed")); + } + if(failed_already) { + return 1; + } + else { + return 0; + } +} /* main */ + +/* testlock.c */ diff --git a/nsprpub/pr/tests/logfile.c b/nsprpub/pr/tests/logfile.c new file mode 100644 index 0000000000..d6997be1fe --- /dev/null +++ b/nsprpub/pr/tests/logfile.c @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A regression test for bug 491441. NSPR should not crash on startup in + * PR_SetLogFile when the NSPR_LOG_MODULES and NSPR_LOG_FILE environment + * variables are set. + * + * This test could be extended to be a full-blown test for NSPR_LOG_FILE. + */ + +#include "prinit.h" +#include "prlog.h" + +#include <stdio.h> +#include <stdlib.h> + +int main() +{ + PRLogModuleInfo *test_lm; + + if (putenv("NSPR_LOG_MODULES=all:5") != 0) { + fprintf(stderr, "putenv failed\n"); + exit(1); + } + if (putenv("NSPR_LOG_FILE=logfile.log") != 0) { + fprintf(stderr, "putenv failed\n"); + exit(1); + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + test_lm = PR_NewLogModule("test"); + PR_LOG(test_lm, PR_LOG_MIN, ("logfile: test log message")); + PR_Cleanup(); + return 0; +} diff --git a/nsprpub/pr/tests/logger.c b/nsprpub/pr/tests/logger.c new file mode 100644 index 0000000000..ee28d41d7c --- /dev/null +++ b/nsprpub/pr/tests/logger.c @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: logger.c + * Description: test program for logging's basic functions + */ + +#include "prinit.h" +#include "prlog.h" +#include "prlock.h" +#include "prcvar.h" +#include "prthread.h" +#include "prinrval.h" + +#include <stdio.h> + +/* lth. re-define PR_LOG() */ +#if 0 +#undef PR_LOG_TEST +#undef PR_LOG +#define PR_LOG_TEST(_module,_level) ((_module)->level <= (_level)) +#define PR_LOG(_module,_level,_args) \ + { \ + if (PR_LOG_TEST(_module,_level)) \ + PR_LogPrint _args ; \ + } +#endif + + +static void Error(const char* msg) +{ + printf("\t%s\n", msg); +} /* Error */ + +static void PR_CALLBACK forked(void *arg) +{ + PRIntn i; + PRLock *ml; + PRCondVar *cv; + + PR_LogPrint("%s logging creating mutex\n", (const char*)arg); + ml = PR_NewLock(); + PR_LogPrint("%s logging creating condition variable\n", (const char*)arg); + cv = PR_NewCondVar(ml); + + PR_LogPrint("%s waiting on condition timeout 10 times\n", (const char*)arg); + for (i = 0; i < 10; ++i) + { + PR_Lock(ml); + PR_WaitCondVar(cv, PR_SecondsToInterval(1)); + PR_Unlock(ml); + } + + PR_LogPrint("%s logging destroying condition variable\n", (const char*)arg); + PR_DestroyCondVar(cv); + PR_LogPrint("%s logging destroying mutex\n", (const char*)arg); + PR_DestroyLock(ml); + PR_LogPrint("%s forked thread exiting\n", (const char*)arg); +} + +static void UserLogStuff( void ) +{ + PRLogModuleInfo *myLM; + PRIntn i; + + myLM = PR_NewLogModule( "userStuff" ); + if (! myLM ) + { + printf("UserLogStuff(): can't create new log module\n" ); + return; + } + + PR_LOG( myLM, PR_LOG_NOTICE, ("Log a Notice %d\n", 1 )); + + for (i = 0; i < 10 ; i++ ) + { + PR_LOG( myLM, PR_LOG_DEBUG, ("Log Debug number: %d\n", i)); + PR_Sleep( 300 ); + } + +} /* end UserLogStuff() */ + +int main(int argc, char **argv) +{ + PRThread *thread; + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (argc > 1) + { + if (!PR_SetLogFile(argv[1])) + { + Error("Access: Cannot create log file"); + goto exit; + } + } + + /* Start logging something here */ + PR_LogPrint("%s logging into %s\n", argv[0], argv[1]); + + PR_LogPrint("%s creating new thread\n", argv[0]); + + /* + ** Now change buffering. + */ + PR_SetLogBuffering( 65500 ); + thread = PR_CreateThread( + PR_USER_THREAD, forked, (void*)argv[0], PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_LogPrint("%s joining thread\n", argv[0]); + + UserLogStuff(); + + PR_JoinThread(thread); + + PR_LogFlush(); + return 0; + +exit: + return -1; +} + +/* logger.c */ diff --git a/nsprpub/pr/tests/makedir.c b/nsprpub/pr/tests/makedir.c new file mode 100644 index 0000000000..21b42c5a6f --- /dev/null +++ b/nsprpub/pr/tests/makedir.c @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This test calls PR_MakeDir to create a bunch of directories + * with various mode bits. + */ + +#include "prio.h" + +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) +{ + if (PR_MakeDir("tdir0400", 0400) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0200", 0200) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0100", 0100) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0500", 0500) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0600", 0600) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0300", 0300) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0700", 0700) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0640", 0640) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0660", 0660) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0644", 0644) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0664", 0664) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + if (PR_MakeDir("tdir0666", 0666) == PR_FAILURE) { + fprintf(stderr, "PR_MakeDir failed\n"); + exit(1); + } + return 0; +} diff --git a/nsprpub/pr/tests/many_cv.c b/nsprpub/pr/tests/many_cv.c new file mode 100644 index 0000000000..8444c99576 --- /dev/null +++ b/nsprpub/pr/tests/many_cv.c @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prinit.h" +#include "prprf.h" +#include "prthread.h" +#include "prcvar.h" +#include "prlock.h" +#include "prlog.h" +#include "prmem.h" + +#include "primpl.h" + +#include "plgetopt.h" + +#include <stdlib.h> + +static PRInt32 RandomNum(void) +{ + PRInt32 ran = rand() >> 16; + return ran; +} /* RandomNum */ + +static void Help(void) +{ + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + PR_fprintf(err, "many_cv usage: [-c n] [-l n] [-h]\n"); + PR_fprintf(err, "\t-c n Number of conditions per lock (default: 10)\n"); + PR_fprintf(err, "\t-l n Number of times to loop the test (default: 1)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + PLOptStatus os; + PRIntn index, nl; + PRLock *ml = NULL; + PRCondVar **cv = NULL; + PRBool stats = PR_FALSE; + PRIntn nc, loops = 1, cvs = 10; + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + PLOptState *opt = PL_CreateOptState(argc, argv, "hsc:l:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 's': /* number of CVs to association with lock */ + stats = PR_TRUE; + break; + case 'c': /* number of CVs to association with lock */ + cvs = atoi(opt->value); + break; + case 'l': /* number of times to run the tests */ + loops = atoi(opt->value); + break; + case 'h': /* user wants some guidance */ + default: + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + + PR_fprintf(err, "Settings\n"); + PR_fprintf(err, "\tConditions / lock: %d\n", cvs); + PR_fprintf(err, "\tLoops to run test: %d\n", loops); + + ml = PR_NewLock(); + PR_ASSERT(NULL != ml); + + cv = (PRCondVar**)PR_CALLOC(sizeof(PRCondVar*) * cvs); + PR_ASSERT(NULL != cv); + + for (index = 0; index < cvs; ++index) + { + cv[index] = PR_NewCondVar(ml); + PR_ASSERT(NULL != cv[index]); + } + + for (index = 0; index < loops; ++index) + { + PR_Lock(ml); + for (nl = 0; nl < cvs; ++nl) + { + PRInt32 ran = RandomNum() % 8; + if (0 == ran) { + PR_NotifyAllCondVar(cv[nl]); + } + else for (nc = 0; nc < ran; ++nc) { + PR_NotifyCondVar(cv[nl]); + } + } + PR_Unlock(ml); + } + + for (index = 0; index < cvs; ++index) { + PR_DestroyCondVar(cv[index]); + } + + PR_DELETE(cv); + + PR_DestroyLock(ml); + + printf("PASS\n"); + + PT_FPrintStats(err, "\nPThread Statistics\n"); + return 0; +} + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/mbcs.c b/nsprpub/pr/tests/mbcs.c new file mode 100644 index 0000000000..1a0d08f840 --- /dev/null +++ b/nsprpub/pr/tests/mbcs.c @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: mbcs.c +** +** Synopsis: mbcs {dirName} +** +** where dirName is the directory to be traversed. dirName is required. +** +** Description: +** mbcs.c tests use of multi-byte characters, as would be passed to +** NSPR funtions by internationalized applications. +** +** mbcs.c, when run on any single-byte platform, should run correctly. +** In truth, running the mbcs test on a single-byte platform is +** really meaningless. mbcs.c, nor any NSPR library or test is not +** intended for use with any wide character set, including Unicode. +** mbcs.c should not be included in runtests.ksh because it requires +** extensive user intervention to set-up and run. +** +** mbcs.c should be run on a platform using some form of multi-byte +** characters. The initial platform for this test is a Japanese +** language Windows NT 4.0 machine. ... Thank you Noriko Hoshi. +** +** To run mbcs.c, the tester should create a directory tree containing +** some files in the same directory from which the test is run; i.e. +** the current working directory. The directory and files should be +** named such that when represented in the local multi-byte character +** set, one or more characters of the name is longer than a single +** byte. +** +*/ + +#include <plgetopt.h> +#include <nspr.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* +** Test harness infrastructure +*/ +PRLogModuleInfo *lm; +PRLogModuleLevel msgLevel = PR_LOG_NONE; +PRIntn debug = 0; +PRUint32 failed_already = 0; +/* end Test harness infrastructure */ + +char *dirName = NULL; /* directory name to traverse */ + +/* +** Traverse directory +*/ +static void TraverseDirectory( unsigned char *dir ) +{ + PRDir *cwd; + PRDirEntry *dirEntry; + PRFileInfo info; + PRStatus rc; + PRInt32 err; + PRFileDesc *fd; + char nextDir[256]; + char file[256]; + + printf("Directory: %s\n", dir ); + cwd = PR_OpenDir( dir ); + if ( NULL == cwd ) { + printf("PR_OpenDir() failed on directory: %s, with error: %d, %d\n", + dir, PR_GetError(), PR_GetOSError()); + exit(1); + } + while( NULL != (dirEntry = PR_ReadDir( cwd, PR_SKIP_BOTH | PR_SKIP_HIDDEN ))) { + sprintf( file, "%s/%s", dir, dirEntry->name ); + rc = PR_GetFileInfo( file, &info ); + if ( PR_FAILURE == rc ) { + printf("PR_GetFileInfo() failed on file: %s, with error: %d, %d\n", + dirEntry->name, PR_GetError(), PR_GetOSError()); + exit(1); + } + if ( PR_FILE_FILE == info.type ) { + printf("File: %s \tsize: %ld\n", dirEntry->name, info.size ); + fd = PR_Open( file, PR_RDONLY, 0 ); + if ( NULL == fd ) { + printf("PR_Open() failed. Error: %ld, OSError: %ld\n", + PR_GetError(), PR_GetOSError()); + } + rc = PR_Close( fd ); + if ( PR_FAILURE == rc ) { + printf("PR_Close() failed. Error: %ld, OSError: %ld\n", + PR_GetError(), PR_GetOSError()); + } + } else if ( PR_FILE_DIRECTORY == info.type ) { + sprintf( nextDir, "%s/%s", dir, dirEntry->name ); + TraverseDirectory(nextDir); + } else { + printf("type is not interesting for file: %s\n", dirEntry->name ); + /* keep going */ + } + } + /* assume end-of-file, actually could be error */ + + rc = PR_CloseDir( cwd ); + if ( PR_FAILURE == rc ) { + printf("PR_CloseDir() failed on directory: %s, with error: %d, %d\n", + dir, PR_GetError(), PR_GetOSError()); + } + +} /* end TraverseDirectory() */ + +int main(int argc, char **argv) +{ + { /* get command line options */ + /* + ** Get command line options + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dv"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug */ + debug = 1; + msgLevel = PR_LOG_ERROR; + break; + case 'v': /* verbose mode */ + msgLevel = PR_LOG_DEBUG; + break; + default: + dirName = strdup(opt->value); + break; + } + } + PL_DestroyOptState(opt); + } /* end get command line options */ + + lm = PR_NewLogModule("Test"); /* Initialize logging */ + + + if ( dirName == NULL ) { + printf("you gotta specify a directory as an operand!\n"); + exit(1); + } + + TraverseDirectory( dirName ); + + if (debug) { + printf("%s\n", (failed_already)? "FAIL" : "PASS"); + } + return( (failed_already == PR_TRUE )? 1 : 0 ); +} /* main() */ +/* end template.c */ diff --git a/nsprpub/pr/tests/monref.c b/nsprpub/pr/tests/monref.c new file mode 100644 index 0000000000..3e2ae637bf --- /dev/null +++ b/nsprpub/pr/tests/monref.c @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This test program demonstrates that PR_ExitMonitor needs to add a + * reference to the PRMonitor object before unlocking the internal + * mutex. + */ + +#include "prlog.h" +#include "prmon.h" +#include "prthread.h" + +#include <stdio.h> +#include <stdlib.h> + +/* Protected by the PRMonitor 'mon' in the main function. */ +static PRBool done = PR_FALSE; + +static void ThreadFunc(void *arg) +{ + PRMonitor *mon = (PRMonitor *)arg; + PRStatus rv; + + PR_EnterMonitor(mon); + done = PR_TRUE; + rv = PR_Notify(mon); + PR_ASSERT(rv == PR_SUCCESS); + rv = PR_ExitMonitor(mon); + PR_ASSERT(rv == PR_SUCCESS); +} + +int main() +{ + PRMonitor *mon; + PRThread *thread; + PRStatus rv; + + mon = PR_NewMonitor(); + if (!mon) { + fprintf(stderr, "PR_NewMonitor failed\n"); + exit(1); + } + + thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, mon, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + if (!thread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + + PR_EnterMonitor(mon); + while (!done) { + rv = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(rv == PR_SUCCESS); + } + rv = PR_ExitMonitor(mon); + PR_ASSERT(rv == PR_SUCCESS); + + /* + * Do you agree it should be safe to destroy 'mon' now? + * See bug 844784 comment 27. + */ + PR_DestroyMonitor(mon); + + rv = PR_JoinThread(thread); + PR_ASSERT(rv == PR_SUCCESS); + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/multiacc.c b/nsprpub/pr/tests/multiacc.c new file mode 100644 index 0000000000..ff8452d3da --- /dev/null +++ b/nsprpub/pr/tests/multiacc.c @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: multiacc.c + * + * Description: + * This test creates multiple threads that accept on the + * same listening socket. + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define NUM_SERVER_THREADS 10 + +static int num_server_threads = NUM_SERVER_THREADS; +static PRThreadScope thread_scope = PR_GLOBAL_THREAD; +static PRBool exit_flag = PR_FALSE; + +static void ServerThreadFunc(void *arg) +{ + PRFileDesc *listenSock = (PRFileDesc *) arg; + PRFileDesc *acceptSock; + PRErrorCode err; + PRStatus status; + + while (!exit_flag) { + acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (NULL == acceptSock) { + err = PR_GetError(); + if (PR_PENDING_INTERRUPT_ERROR == err) { + printf("server thread is interrupted\n"); + fflush(stdout); + continue; + } + fprintf(stderr, "PR_Accept failed: %d\n", err); + exit(1); + } + status = PR_Close(acceptSock); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + } +} + +int main(int argc, char **argv) +{ + PRNetAddr serverAddr; + PRFileDesc *dummySock; + PRFileDesc *listenSock; + PRFileDesc *clientSock; + PRThread *dummyThread; + PRThread **serverThreads; + PRStatus status; + PRUint16 port; + int idx; + PRInt32 nbytes; + char buf[1024]; + + serverThreads = (PRThread **) + PR_Malloc(num_server_threads * sizeof(PRThread *)); + if (NULL == serverThreads) { + fprintf(stderr, "PR_Malloc failed\n"); + exit(1); + } + + /* + * Create a dummy listening socket and have the first + * (dummy) thread listen on it. This is to ensure that + * the first thread becomes the I/O continuation thread + * in the pthreads implementation (see ptio.c) and remains + * so throughout the test, so that we never have to + * recycle the I/O continuation thread. + */ + dummySock = PR_NewTCPSocket(); + if (NULL == dummySock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + memset(&serverAddr, 0, sizeof(serverAddr)); + status = PR_InitializeNetAddr(PR_IpAddrAny, 0, &serverAddr); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + status = PR_Bind(dummySock, &serverAddr); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + status = PR_Listen(dummySock, 5); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + + listenSock = PR_NewTCPSocket(); + if (NULL == listenSock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + memset(&serverAddr, 0, sizeof(serverAddr)); + status = PR_InitializeNetAddr(PR_IpAddrAny, 0, &serverAddr); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + status = PR_Bind(listenSock, &serverAddr); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + status = PR_GetSockName(listenSock, &serverAddr); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + port = PR_ntohs(serverAddr.inet.port); + status = PR_Listen(listenSock, 5); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + + printf("creating dummy thread\n"); + fflush(stdout); + dummyThread = PR_CreateThread(PR_USER_THREAD, + ServerThreadFunc, dummySock, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + if (NULL == dummyThread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + printf("sleeping one second before creating server threads\n"); + fflush(stdout); + PR_Sleep(PR_SecondsToInterval(1)); + for (idx = 0; idx < num_server_threads; idx++) { + serverThreads[idx] = PR_CreateThread(PR_USER_THREAD, + ServerThreadFunc, listenSock, PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + if (NULL == serverThreads[idx]) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + } + + memset(&serverAddr, 0, sizeof(serverAddr)); + PR_InitializeNetAddr(PR_IpAddrLoopback, port, &serverAddr); + clientSock = PR_NewTCPSocket(); + if (NULL == clientSock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + printf("sleeping one second before connecting\n"); + fflush(stdout); + PR_Sleep(PR_SecondsToInterval(1)); + status = PR_Connect(clientSock, &serverAddr, PR_INTERVAL_NO_TIMEOUT); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Connect failed\n"); + exit(1); + } + nbytes = PR_Read(clientSock, buf, sizeof(buf)); + if (nbytes != 0) { + fprintf(stderr, "expected 0 bytes but got %d bytes\n", nbytes); + exit(1); + } + status = PR_Close(clientSock); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + printf("sleeping one second before shutting down server threads\n"); + fflush(stdout); + PR_Sleep(PR_SecondsToInterval(1)); + + exit_flag = PR_TRUE; + status = PR_Interrupt(dummyThread); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Interrupt failed\n"); + exit(1); + } + status = PR_JoinThread(dummyThread); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + for (idx = 0; idx < num_server_threads; idx++) { + status = PR_Interrupt(serverThreads[idx]); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Interrupt failed\n"); + exit(1); + } + status = PR_JoinThread(serverThreads[idx]); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + } + PR_Free(serverThreads); + status = PR_Close(dummySock); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(listenSock); + if (PR_FAILURE == status) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/multiwait.c b/nsprpub/pr/tests/multiwait.c new file mode 100644 index 0000000000..62a07f0428 --- /dev/null +++ b/nsprpub/pr/tests/multiwait.c @@ -0,0 +1,785 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prprf.h" +#include "prlog.h" +#include "prmem.h" +#include "pratom.h" +#include "prlock.h" +#include "prmwait.h" +#include "prclist.h" +#include "prerror.h" +#include "prinrval.h" +#include "prnetdb.h" +#include "prthread.h" + +#include "plstr.h" +#include "plerror.h" +#include "plgetopt.h" + +#include <string.h> + +typedef struct Shared +{ + const char *title; + PRLock *list_lock; + PRWaitGroup *group; + PRIntervalTime timeout; +} Shared; + +typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity; + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +static PRFileDesc *debug = NULL; +static PRInt32 desc_allocated = 0; +static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264; +static enum Verbosity verbosity = quiet; +static PRInt32 ops_required = 1000, ops_done = 0; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; +static PRIntn client_threads = 20, worker_threads = 2, wait_objects = 50; + +#if defined(DEBUG) +#define MW_ASSERT(_expr) \ + ((_expr)?((void)0):_MW_Assert(# _expr,__FILE__,__LINE__)) +static void _MW_Assert(const char *s, const char *file, PRIntn ln) +{ + if (NULL != debug) { + PL_FPrintError(debug, NULL); + } + PR_Assert(s, file, ln); +} /* _MW_Assert */ +#else +#define MW_ASSERT(_expr) +#endif + +static void PrintRecvDesc(PRRecvWait *desc, const char *msg) +{ + const char *tag[] = { + "PR_MW_INTERRUPT", "PR_MW_TIMEOUT", + "PR_MW_FAILURE", "PR_MW_SUCCESS", "PR_MW_PENDING" + }; + PR_fprintf( + debug, "%s: PRRecvWait(@0x%x): {fd: 0x%x, outcome: %s, tmo: %u}\n", + msg, desc, desc->fd, tag[desc->outcome + 3], desc->timeout); +} /* PrintRecvDesc */ + +static Shared *MakeShared(const char *title) +{ + Shared *shared = PR_NEWZAP(Shared); + shared->group = PR_CreateWaitGroup(1); + shared->timeout = PR_SecondsToInterval(1); + shared->list_lock = PR_NewLock(); + shared->title = title; + return shared; +} /* MakeShared */ + +static void DestroyShared(Shared *shared) +{ + PRStatus rv; + if (verbosity > quiet) { + PR_fprintf(debug, "%s: destroying group\n", shared->title); + } + rv = PR_DestroyWaitGroup(shared->group); + MW_ASSERT(PR_SUCCESS == rv); + PR_DestroyLock(shared->list_lock); + PR_DELETE(shared); +} /* DestroyShared */ + +static PRRecvWait *CreateRecvWait(PRFileDesc *fd, PRIntervalTime timeout) +{ + PRRecvWait *desc_out = PR_NEWZAP(PRRecvWait); + MW_ASSERT(NULL != desc_out); + + MW_ASSERT(NULL != fd); + desc_out->fd = fd; + desc_out->timeout = timeout; + desc_out->buffer.length = 120; + desc_out->buffer.start = PR_CALLOC(120); + + PR_AtomicIncrement(&desc_allocated); + + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Allocated"); + } + return desc_out; +} /* CreateRecvWait */ + +static void DestroyRecvWait(PRRecvWait *desc_out) +{ + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Destroying"); + } + PR_Close(desc_out->fd); + if (NULL != desc_out->buffer.start) { + PR_DELETE(desc_out->buffer.start); + } + PR_Free(desc_out); + (void)PR_AtomicDecrement(&desc_allocated); +} /* DestroyRecvWait */ + +static void CancelGroup(Shared *shared) +{ + PRRecvWait *desc_out; + + if (verbosity > quiet) { + PR_fprintf(debug, "%s Reclaiming wait descriptors\n", shared->title); + } + + do + { + desc_out = PR_CancelWaitGroup(shared->group); + if (NULL != desc_out) { + DestroyRecvWait(desc_out); + } + } while (NULL != desc_out); + + MW_ASSERT(0 == desc_allocated); + MW_ASSERT(PR_GROUP_EMPTY_ERROR == PR_GetError()); +} /* CancelGroup */ + +static void PR_CALLBACK ClientThread(void* arg) +{ + PRStatus rv; + PRInt32 bytes; + PRIntn empty_flags = 0; + PRNetAddr server_address; + unsigned char buffer[100]; + Shared *shared = (Shared*)arg; + PRFileDesc *server = PR_NewTCPSocket(); + if ((NULL == server) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + return; + } + MW_ASSERT(NULL != server); + + if (verbosity > chatty) { + PR_fprintf(debug, "%s: Server socket @0x%x\n", shared->title, server); + } + + /* Initialize the buffer so that Purify won't complain */ + memset(buffer, 0, sizeof(buffer)); + + rv = PR_InitializeNetAddr(PR_IpAddrLoopback, default_port, &server_address); + MW_ASSERT(PR_SUCCESS == rv); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: Client opening connection\n", shared->title); + } + rv = PR_Connect(server, &server_address, PR_INTERVAL_NO_TIMEOUT); + + if (PR_FAILURE == rv) + { + if (verbosity > silent) { + PL_FPrintError(debug, "Client connect failed"); + } + return; + } + + while (ops_done < ops_required) + { + bytes = PR_Send( + server, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); + if ((-1 == bytes) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + break; + } + MW_ASSERT(sizeof(buffer) == bytes); + if (verbosity > chatty) + PR_fprintf( + debug, "%s: Client sent %d bytes\n", + shared->title, sizeof(buffer)); + bytes = PR_Recv( + server, buffer, sizeof(buffer), empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) + PR_fprintf( + debug, "%s: Client received %d bytes\n", + shared->title, sizeof(buffer)); + if ((-1 == bytes) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + break; + } + MW_ASSERT(sizeof(buffer) == bytes); + PR_Sleep(shared->timeout); + } + rv = PR_Close(server); + MW_ASSERT(PR_SUCCESS == rv); + +} /* ClientThread */ + +static void OneInThenCancelled(Shared *shared) +{ + PRStatus rv; + PRRecvWait *desc_out, *desc_in = PR_NEWZAP(PRRecvWait); + + shared->timeout = PR_INTERVAL_NO_TIMEOUT; + + desc_in->fd = PR_NewTCPSocket(); + desc_in->timeout = shared->timeout; + + if (verbosity > chatty) { + PrintRecvDesc(desc_in, "Adding desc"); + } + + rv = PR_AddWaitFileDesc(shared->group, desc_in); + MW_ASSERT(PR_SUCCESS == rv); + + if (verbosity > chatty) { + PrintRecvDesc(desc_in, "Cancelling"); + } + rv = PR_CancelWaitFileDesc(shared->group, desc_in); + MW_ASSERT(PR_SUCCESS == rv); + + desc_out = PR_WaitRecvReady(shared->group); + MW_ASSERT(desc_out == desc_in); + MW_ASSERT(PR_MW_INTERRUPT == desc_out->outcome); + MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError()); + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Ready"); + } + + rv = PR_Close(desc_in->fd); + MW_ASSERT(PR_SUCCESS == rv); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: destroying group\n", shared->title); + } + + PR_DELETE(desc_in); +} /* OneInThenCancelled */ + +static void OneOpOneThread(Shared *shared) +{ + PRStatus rv; + PRRecvWait *desc_out, *desc_in = PR_NEWZAP(PRRecvWait); + + desc_in->fd = PR_NewTCPSocket(); + desc_in->timeout = shared->timeout; + + if (verbosity > chatty) { + PrintRecvDesc(desc_in, "Adding desc"); + } + + rv = PR_AddWaitFileDesc(shared->group, desc_in); + MW_ASSERT(PR_SUCCESS == rv); + desc_out = PR_WaitRecvReady(shared->group); + MW_ASSERT(desc_out == desc_in); + MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome); + MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError()); + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Ready"); + } + + rv = PR_Close(desc_in->fd); + MW_ASSERT(PR_SUCCESS == rv); + + PR_DELETE(desc_in); +} /* OneOpOneThread */ + +static void ManyOpOneThread(Shared *shared) +{ + PRStatus rv; + PRIntn index; + PRRecvWait *desc_in; + PRRecvWait *desc_out; + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: adding %d descs\n", shared->title, wait_objects); + } + + for (index = 0; index < wait_objects; ++index) + { + desc_in = CreateRecvWait(PR_NewTCPSocket(), shared->timeout); + + rv = PR_AddWaitFileDesc(shared->group, desc_in); + MW_ASSERT(PR_SUCCESS == rv); + } + + while (ops_done < ops_required) + { + desc_out = PR_WaitRecvReady(shared->group); + MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome); + MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError()); + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Ready/readding"); + } + rv = PR_AddWaitFileDesc(shared->group, desc_out); + MW_ASSERT(PR_SUCCESS == rv); + (void)PR_AtomicIncrement(&ops_done); + } + + CancelGroup(shared); +} /* ManyOpOneThread */ + +static void PR_CALLBACK SomeOpsThread(void *arg) +{ + PRRecvWait *desc_out; + PRStatus rv = PR_SUCCESS; + Shared *shared = (Shared*)arg; + do /* until interrupted */ + { + desc_out = PR_WaitRecvReady(shared->group); + if (NULL == desc_out) + { + MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError()); + if (verbosity > quiet) { + PR_fprintf(debug, "Aborted\n"); + } + break; + } + MW_ASSERT(PR_MW_TIMEOUT == desc_out->outcome); + MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError()); + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Ready"); + } + + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Re-Adding"); + } + desc_out->timeout = shared->timeout; + rv = PR_AddWaitFileDesc(shared->group, desc_out); + PR_AtomicIncrement(&ops_done); + if (ops_done > ops_required) { + break; + } + } while (PR_SUCCESS == rv); + MW_ASSERT(PR_SUCCESS == rv); +} /* SomeOpsThread */ + +static void SomeOpsSomeThreads(Shared *shared) +{ + PRStatus rv; + PRThread **thread; + PRIntn index; + PRRecvWait *desc_in; + + thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * worker_threads); + + /* Create some threads */ + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: creating threads\n", shared->title); + } + for (index = 0; index < worker_threads; ++index) + { + thread[index] = PR_CreateThread( + PR_USER_THREAD, SomeOpsThread, shared, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + } + + /* then create some operations */ + if (verbosity > quiet) { + PR_fprintf(debug, "%s: creating desc\n", shared->title); + } + for (index = 0; index < wait_objects; ++index) + { + desc_in = CreateRecvWait(PR_NewTCPSocket(), shared->timeout); + rv = PR_AddWaitFileDesc(shared->group, desc_in); + MW_ASSERT(PR_SUCCESS == rv); + } + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: sleeping\n", shared->title); + } + while (ops_done < ops_required) { + PR_Sleep(shared->timeout); + } + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: interrupting/joining threads\n", shared->title); + } + for (index = 0; index < worker_threads; ++index) + { + rv = PR_Interrupt(thread[index]); + MW_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(thread[index]); + MW_ASSERT(PR_SUCCESS == rv); + } + PR_DELETE(thread); + + CancelGroup(shared); +} /* SomeOpsSomeThreads */ + +static PRStatus ServiceRequest(Shared *shared, PRRecvWait *desc) +{ + PRInt32 bytes_out; + + if (verbosity > chatty) + PR_fprintf( + debug, "%s: Service received %d bytes\n", + shared->title, desc->bytesRecv); + + if (0 == desc->bytesRecv) { + goto quitting; + } + if ((-1 == desc->bytesRecv) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + goto aborted; + } + + bytes_out = PR_Send( + desc->fd, desc->buffer.start, desc->bytesRecv, 0, shared->timeout); + if (verbosity > chatty) + PR_fprintf( + debug, "%s: Service sent %d bytes\n", + shared->title, bytes_out); + + if ((-1 == bytes_out) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) { + goto aborted; + } + MW_ASSERT(bytes_out == desc->bytesRecv); + + return PR_SUCCESS; + +aborted: +quitting: + return PR_FAILURE; +} /* ServiceRequest */ + +static void PR_CALLBACK ServiceThread(void *arg) +{ + PRStatus rv = PR_SUCCESS; + PRRecvWait *desc_out = NULL; + Shared *shared = (Shared*)arg; + do /* until interrupted */ + { + if (NULL != desc_out) + { + desc_out->timeout = PR_INTERVAL_NO_TIMEOUT; + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Service re-adding"); + } + rv = PR_AddWaitFileDesc(shared->group, desc_out); + MW_ASSERT(PR_SUCCESS == rv); + } + + desc_out = PR_WaitRecvReady(shared->group); + if (NULL == desc_out) + { + MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError()); + break; + } + + switch (desc_out->outcome) + { + case PR_MW_SUCCESS: + { + PR_AtomicIncrement(&ops_done); + if (verbosity > chatty) { + PrintRecvDesc(desc_out, "Service ready"); + } + rv = ServiceRequest(shared, desc_out); + break; + } + case PR_MW_INTERRUPT: + MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError()); + rv = PR_FAILURE; /* if interrupted, then exit */ + break; + case PR_MW_TIMEOUT: + MW_ASSERT(PR_IO_TIMEOUT_ERROR == PR_GetError()); + case PR_MW_FAILURE: + if (verbosity > silent) { + PL_FPrintError(debug, "RecvReady failure"); + } + break; + default: + break; + } + } while (PR_SUCCESS == rv); + + if (NULL != desc_out) { + DestroyRecvWait(desc_out); + } + +} /* ServiceThread */ + +static void PR_CALLBACK EnumerationThread(void *arg) +{ + PRStatus rv; + PRIntn count; + PRRecvWait *desc; + Shared *shared = (Shared*)arg; + PRIntervalTime five_seconds = PR_SecondsToInterval(5); + PRMWaitEnumerator *enumerator = PR_CreateMWaitEnumerator(shared->group); + MW_ASSERT(NULL != enumerator); + + while (PR_SUCCESS == PR_Sleep(five_seconds)) + { + count = 0; + desc = NULL; + while (NULL != (desc = PR_EnumerateWaitGroup(enumerator, desc))) + { + if (verbosity > chatty) { + PrintRecvDesc(desc, shared->title); + } + count += 1; + } + if (verbosity > silent) + PR_fprintf(debug, + "%s Enumerated %d objects\n", shared->title, count); + } + + MW_ASSERT(PR_PENDING_INTERRUPT_ERROR == PR_GetError()); + + + rv = PR_DestroyMWaitEnumerator(enumerator); + MW_ASSERT(PR_SUCCESS == rv); +} /* EnumerationThread */ + +static void PR_CALLBACK ServerThread(void *arg) +{ + PRStatus rv; + PRIntn index; + PRRecvWait *desc_in; + PRThread **worker_thread; + Shared *shared = (Shared*)arg; + PRFileDesc *listener, *service; + PRNetAddr server_address, client_address; + + worker_thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * worker_threads); + if (verbosity > quiet) { + PR_fprintf(debug, "%s: Server creating worker_threads\n", shared->title); + } + for (index = 0; index < worker_threads; ++index) + { + worker_thread[index] = PR_CreateThread( + PR_USER_THREAD, ServiceThread, shared, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + } + + rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &server_address); + MW_ASSERT(PR_SUCCESS == rv); + + listener = PR_NewTCPSocket(); MW_ASSERT(NULL != listener); + if (verbosity > chatty) + PR_fprintf( + debug, "%s: Server listener socket @0x%x\n", + shared->title, listener); + rv = PR_Bind(listener, &server_address); MW_ASSERT(PR_SUCCESS == rv); + rv = PR_Listen(listener, 10); MW_ASSERT(PR_SUCCESS == rv); + while (ops_done < ops_required) + { + if (verbosity > quiet) { + PR_fprintf(debug, "%s: Server accepting connection\n", shared->title); + } + service = PR_Accept(listener, &client_address, PR_INTERVAL_NO_TIMEOUT); + if (NULL == service) + { + if (PR_PENDING_INTERRUPT_ERROR == PR_GetError()) { + break; + } + PL_PrintError("Accept failed"); + MW_ASSERT(PR_FALSE && "Accept failed"); + } + else + { + desc_in = CreateRecvWait(service, shared->timeout); + desc_in->timeout = PR_INTERVAL_NO_TIMEOUT; + if (verbosity > chatty) { + PrintRecvDesc(desc_in, "Service adding"); + } + rv = PR_AddWaitFileDesc(shared->group, desc_in); + MW_ASSERT(PR_SUCCESS == rv); + } + } + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: Server interrupting worker_threads\n", shared->title); + } + for (index = 0; index < worker_threads; ++index) + { + rv = PR_Interrupt(worker_thread[index]); + MW_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(worker_thread[index]); + MW_ASSERT(PR_SUCCESS == rv); + } + PR_DELETE(worker_thread); + + PR_Close(listener); + + CancelGroup(shared); + +} /* ServerThread */ + +static void RealOneGroupIO(Shared *shared) +{ + /* + ** Create a server that listens for connections and then services + ** requests that come in over those connections. The server never + ** deletes a connection and assumes a basic RPC model of operation. + ** + ** Use worker_threads threads to service how every many open ports + ** there might be. + ** + ** Oh, ya. Almost forget. Create (some) clients as well. + */ + PRStatus rv; + PRIntn index; + PRThread *server_thread, *enumeration_thread, **client_thread; + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: creating server_thread\n", shared->title); + } + + server_thread = PR_CreateThread( + PR_USER_THREAD, ServerThread, shared, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: creating enumeration_thread\n", shared->title); + } + + enumeration_thread = PR_CreateThread( + PR_USER_THREAD, EnumerationThread, shared, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: snoozing before creating clients\n", shared->title); + } + PR_Sleep(5 * shared->timeout); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: creating client_threads\n", shared->title); + } + client_thread = (PRThread**)PR_CALLOC(sizeof(PRThread*) * client_threads); + for (index = 0; index < client_threads; ++index) + { + client_thread[index] = PR_CreateThread( + PR_USER_THREAD, ClientThread, shared, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + } + + while (ops_done < ops_required) { + PR_Sleep(shared->timeout); + } + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: interrupting/joining client_threads\n", shared->title); + } + for (index = 0; index < client_threads; ++index) + { + rv = PR_Interrupt(client_thread[index]); + MW_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(client_thread[index]); + MW_ASSERT(PR_SUCCESS == rv); + } + PR_DELETE(client_thread); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: interrupting/joining enumeration_thread\n", shared->title); + } + rv = PR_Interrupt(enumeration_thread); + MW_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(enumeration_thread); + MW_ASSERT(PR_SUCCESS == rv); + + if (verbosity > quiet) { + PR_fprintf(debug, "%s: interrupting/joining server_thread\n", shared->title); + } + rv = PR_Interrupt(server_thread); + MW_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(server_thread); + MW_ASSERT(PR_SUCCESS == rv); +} /* RealOneGroupIO */ + +static void RunThisOne( + void (*func)(Shared*), const char *name, const char *test_name) +{ + Shared *shared; + if ((NULL == test_name) || (0 == PL_strcmp(name, test_name))) + { + if (verbosity > silent) { + PR_fprintf(debug, "%s()\n", name); + } + shared = MakeShared(name); + ops_done = 0; + func(shared); /* run the test */ + MW_ASSERT(0 == desc_allocated); + DestroyShared(shared); + } +} /* RunThisOne */ + +static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta) +{ + return (Verbosity)(((PRIntn)verbosity) + delta); +} /* ChangeVerbosity */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + const char *test_name = NULL; + PLOptState *opt = PL_CreateOptState(argc, argv, "dqGc:o:p:t:w:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: + test_name = opt->value; + break; + case 'd': /* debug mode */ + if (verbosity < noisy) { + verbosity = ChangeVerbosity(verbosity, 1); + } + break; + case 'q': /* debug mode */ + if (verbosity > silent) { + verbosity = ChangeVerbosity(verbosity, -1); + } + break; + case 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'c': /* number of client threads */ + client_threads = atoi(opt->value); + break; + case 'o': /* operations to compelete */ + ops_required = atoi(opt->value); + break; + case 'p': /* default port */ + default_port = atoi(opt->value); + break; + case 't': /* number of threads waiting */ + worker_threads = atoi(opt->value); + break; + case 'w': /* number of wait objects */ + wait_objects = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (verbosity > 0) { + debug = PR_GetSpecialFD(PR_StandardError); + } + + RunThisOne(OneInThenCancelled, "OneInThenCancelled", test_name); + RunThisOne(OneOpOneThread, "OneOpOneThread", test_name); + RunThisOne(ManyOpOneThread, "ManyOpOneThread", test_name); + RunThisOne(SomeOpsSomeThreads, "SomeOpsSomeThreads", test_name); + RunThisOne(RealOneGroupIO, "RealOneGroupIO", test_name); + return 0; +} /* main */ + +/* multwait.c */ diff --git a/nsprpub/pr/tests/nameshm1.c b/nsprpub/pr/tests/nameshm1.c new file mode 100644 index 0000000000..ca24b4e813 --- /dev/null +++ b/nsprpub/pr/tests/nameshm1.c @@ -0,0 +1,586 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** 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> + +#ifdef DEBUG +#define SEM_D "D" +#else +#define SEM_D +#endif +#ifdef IS_64 +#define SEM_64 "64" +#else +#define SEM_64 +#endif + +#define SEM_NAME1 "/tmp/nameshmSEM1" SEM_D SEM_64 +#define SEM_NAME2 "/tmp/nameshmSEM2" SEM_D SEM_64 +#define OPT_NAME "/tmp/xxxNSPRshm" SEM_D SEM_64 +#define EXE_NAME "nameshm1" +#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] = OPT_NAME; + +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] = EXE_NAME; + 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() */ + +int main(int 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/nsprpub/pr/tests/nbconn.c b/nsprpub/pr/tests/nbconn.c new file mode 100644 index 0000000000..d1dd67c042 --- /dev/null +++ b/nsprpub/pr/tests/nbconn.c @@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A test for nonblocking connect. Functions tested include PR_Connect, + * PR_Poll, and PR_GetConnectStatus. + * + * The test should be invoked with a host name, for example: + * nbconn www.netscape.com + * It will do a nonblocking connect to port 80 (HTTP) on that host, + * and when connected, issue the "GET /" HTTP command. + * + * You should run this test in three ways: + * 1. To a known web site, such as www.netscape.com. The HTML of the + * top-level page at the web site should be printed. + * 2. To a machine not running a web server at port 80. This test should + * fail. Ideally the error code should be PR_CONNECT_REFUSED_ERROR. + * But it is possible to return PR_UNKNOWN_ERROR on certain platforms. + * 3. To an unreachable machine, for example, a machine that is off line. + * The test should fail after the connect times out. Ideally the + * error code should be PR_IO_TIMEOUT_ERROR, but it is possible to + * return PR_UNKNOWN_ERROR on certain platforms. + */ + +#include "nspr.h" +#include "plgetopt.h" +#include <stdio.h> +#include <string.h> + +#define SERVER_MAX_BIND_COUNT 100 +#define DATA_BUF_SIZE 256 +#define TCP_SERVER_PORT 10000 +#define TCP_UNUSED_PORT 211 + +typedef struct Server_Param { + PRFileDesc *sp_fd; /* server port */ +} Server_Param; +static void PR_CALLBACK TCP_Server(void *arg); + +int _debug_on; +#define DPRINTF(arg) if (_debug_on) printf arg + +static PRIntn connection_success_test(); +static PRIntn connection_failure_test(); + +int main(int argc, char **argv) +{ + PRHostEnt he; + char buf[1024]; + PRNetAddr addr; + PRPollDesc pd; + PRStatus rv; + PRSocketOptionData optData; + const char *hostname = NULL; + PRIntn default_case, n, bytes_read, bytes_sent; + PRInt32 failed_already = 0; + + /* + * -d debug mode + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: /* debug mode */ + hostname = opt->value; + break; + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_STDIO_INIT(); + if (hostname) { + default_case = 0; + } + else { + default_case = 1; + } + + if (default_case) { + + /* + * In the default case the following tests are executed: + * 1. successful connection: a server thread accepts a connection + * from the main thread + * 2. unsuccessful connection: the main thread tries to connect to a + * nonexistent port and expects to get an error + */ + rv = connection_success_test(); + if (rv == 0) { + rv = connection_failure_test(); + } + return rv; + } else { + PRFileDesc *sock; + + if (PR_GetHostByName(argv[1], buf, sizeof(buf), &he) == PR_FAILURE) { + printf( "Unknown host: %s\n", argv[1]); + exit(1); + } else { + printf( "host: %s\n", buf); + } + PR_EnumerateHostEnt(0, &he, 80, &addr); + + sock = PR_NewTCPSocket(); + optData.option = PR_SockOpt_Nonblocking; + optData.value.non_blocking = PR_TRUE; + PR_SetSocketOption(sock, &optData); + rv = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); + if (rv == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) { + printf( "Connect in progress\n"); + } + + pd.fd = sock; + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (n == -1) { + printf( "PR_Poll failed\n"); + exit(1); + } + printf( "PR_Poll returns %d\n", n); + if (pd.out_flags & PR_POLL_READ) { + printf( "PR_POLL_READ\n"); + } + if (pd.out_flags & PR_POLL_WRITE) { + printf( "PR_POLL_WRITE\n"); + } + if (pd.out_flags & PR_POLL_EXCEPT) { + printf( "PR_POLL_EXCEPT\n"); + } + if (pd.out_flags & PR_POLL_ERR) { + printf( "PR_POLL_ERR\n"); + } + if (pd.out_flags & PR_POLL_NVAL) { + printf( "PR_POLL_NVAL\n"); + } + + if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { + printf("PR_GetConnectStatus: connect succeeded\n"); + PR_Write(sock, "GET /\r\n\r\n", 9); + PR_Shutdown(sock, PR_SHUTDOWN_SEND); + pd.in_flags = PR_POLL_READ; + while (1) { + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + printf( "poll returns %d\n", n); + n = PR_Read(sock, buf, sizeof(buf)); + printf( "read returns %d\n", n); + if (n <= 0) { + break; + } + PR_Write(PR_STDOUT, buf, n); + } + } else { + if (PR_GetError() == PR_IN_PROGRESS_ERROR) { + printf( "PR_GetConnectStatus: connect still in progress\n"); + exit(1); + } + printf( "PR_GetConnectStatus: connect failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + } + PR_Close(sock); + printf( "PASS\n"); + return 0; + + } +} + + +/* + * TCP Server + * Server Thread + * Accept a connection from the client and write some data + */ +static void PR_CALLBACK +TCP_Server(void *arg) +{ + Server_Param *sp = (Server_Param *) arg; + PRFileDesc *sockfd, *newsockfd; + char data_buf[DATA_BUF_SIZE]; + PRIntn rv, bytes_read; + + sockfd = sp->sp_fd; + if ((newsockfd = PR_Accept(sockfd, NULL, + PR_INTERVAL_NO_TIMEOUT)) == NULL) { + fprintf(stderr,"ERROR - PR_Accept failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + return; + } + bytes_read = 0; + while (bytes_read != DATA_BUF_SIZE) { + rv = PR_Read(newsockfd, data_buf + bytes_read, + DATA_BUF_SIZE - bytes_read); + if (rv < 0) { + fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + PR_Close(newsockfd); + return; + } + PR_ASSERT(rv != 0); + bytes_read += rv; + } + DPRINTF(("Bytes read from client - %d\n",bytes_read)); + rv = PR_Write(newsockfd, data_buf,DATA_BUF_SIZE); + if (rv < 0) { + fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + PR_Close(newsockfd); + return; + } + PR_ASSERT(rv == DATA_BUF_SIZE); + DPRINTF(("Bytes written to client - %d\n",rv)); + PR_Close(newsockfd); +} + + +/* + * test for successful connection using a non-blocking socket + */ +static PRIntn +connection_success_test() +{ + PRFileDesc *sockfd = NULL, *conn_fd = NULL; + PRNetAddr netaddr; + PRInt32 i, rv; + PRPollDesc pd; + PRSocketOptionData optData; + PRThread *thr = NULL; + Server_Param sp; + char send_buf[DATA_BUF_SIZE], recv_buf[DATA_BUF_SIZE]; + PRIntn default_case, n, bytes_read, bytes_sent; + PRIntn failed_already = 0; + + /* + * Create a tcp socket + */ + if ((sockfd = PR_NewTCPSocket()) == NULL) { + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); + failed_already=1; + goto def_exit; + } + memset(&netaddr, 0, sizeof(netaddr)); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + while (PR_Bind(sockfd, &netaddr) < 0) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + netaddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + + if (PR_Listen(sockfd, 32) < 0) { + fprintf(stderr,"ERROR - PR_Listen failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + if ((conn_fd = PR_NewTCPSocket()) == NULL) { + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); + failed_already=1; + goto def_exit; + } + optData.option = PR_SockOpt_Nonblocking; + optData.value.non_blocking = PR_TRUE; + PR_SetSocketOption(conn_fd, &optData); + rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT); + if (rv == PR_FAILURE) { + if (PR_GetError() == PR_IN_PROGRESS_ERROR) { + DPRINTF(("Connect in progress\n")); + } else { + fprintf(stderr,"Error - PR_Connect failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + } + /* + * Now create a thread to accept a connection + */ + sp.sp_fd = sockfd; + thr = PR_CreateThread(PR_USER_THREAD, TCP_Server, (void *)&sp, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + if (thr == NULL) { + fprintf(stderr,"Error - PR_CreateThread failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + DPRINTF(("Created TCP_Server thread [0x%x]\n",thr)); + pd.fd = conn_fd; + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (n == -1) { + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { + PRInt32 rv; + + DPRINTF(("Connection successful\n")); + + /* + * Write some data, read it back and check data integrity to + * make sure the connection is good + */ + pd.in_flags = PR_POLL_WRITE; + bytes_sent = 0; + memset(send_buf, 'a', DATA_BUF_SIZE); + while (bytes_sent != DATA_BUF_SIZE) { + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (rv < 0) { + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_WRITE)); + rv = PR_Write(conn_fd, send_buf + bytes_sent, + DATA_BUF_SIZE - bytes_sent); + if (rv < 0) { + fprintf(stderr,"Error - PR_Write failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + PR_ASSERT(rv > 0); + bytes_sent += rv; + } + DPRINTF(("Bytes written to server - %d\n",bytes_sent)); + PR_Shutdown(conn_fd, PR_SHUTDOWN_SEND); + pd.in_flags = PR_POLL_READ; + bytes_read = 0; + memset(recv_buf, 0, DATA_BUF_SIZE); + while (bytes_read != DATA_BUF_SIZE) { + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (rv < 0) { + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + PR_ASSERT((rv == 1) && (pd.out_flags == PR_POLL_READ)); + rv = PR_Read(conn_fd, recv_buf + bytes_read, + DATA_BUF_SIZE - bytes_read); + if (rv < 0) { + fprintf(stderr,"Error - PR_Read failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + PR_ASSERT(rv != 0); + bytes_read += rv; + } + DPRINTF(("Bytes read from server - %d\n",bytes_read)); + /* + * verify the data read + */ + if (memcmp(send_buf, recv_buf, DATA_BUF_SIZE) != 0) { + fprintf(stderr,"ERROR - data corruption\n"); + failed_already=1; + goto def_exit; + } + DPRINTF(("Data integrity verified\n")); + } else { + fprintf(stderr,"PR_GetConnectStatus: connect failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already = 1; + goto def_exit; + } +def_exit: + if (thr) { + PR_JoinThread(thr); + thr = NULL; + } + if (sockfd) { + PR_Close(sockfd); + sockfd = NULL; + } + if (conn_fd) { + PR_Close(conn_fd); + conn_fd = NULL; + } + if (failed_already) { + return 1; + } + else { + return 0; + } + +} + +/* + * test for connection to a nonexistent port using a non-blocking socket + */ +static PRIntn +connection_failure_test() +{ + PRFileDesc *sockfd = NULL, *conn_fd = NULL; + PRNetAddr netaddr; + PRInt32 i, rv; + PRPollDesc pd; + PRSocketOptionData optData; + PRIntn n, failed_already = 0; + + /* + * Create a tcp socket + */ + if ((sockfd = PR_NewTCPSocket()) == NULL) { + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); + failed_already=1; + goto def_exit; + } + memset(&netaddr, 0, sizeof(netaddr)); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + while (PR_Bind(sockfd, &netaddr) < 0) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + netaddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + fprintf(stderr,"ERROR - PR_Bind failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr,"ERROR - PR_GetSockName failed: (%d,%d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } +#ifdef AIX + /* + * On AIX, set to unused/reserved port + */ + netaddr.inet.port = PR_htons(TCP_UNUSED_PORT); +#endif + if ((conn_fd = PR_NewTCPSocket()) == NULL) { + fprintf(stderr,"Error - PR_NewTCPSocket failed\n"); + failed_already=1; + goto def_exit; + } + optData.option = PR_SockOpt_Nonblocking; + optData.value.non_blocking = PR_TRUE; + PR_SetSocketOption(conn_fd, &optData); + rv = PR_Connect(conn_fd, &netaddr, PR_INTERVAL_NO_TIMEOUT); + if (rv == PR_FAILURE) { + DPRINTF(("PR_Connect to a non-listen port failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError())); + } else { + PR_ASSERT(rv == PR_SUCCESS); + fprintf(stderr,"Error - PR_Connect succeeded, expected to fail\n"); + failed_already=1; + goto def_exit; + } + pd.fd = conn_fd; + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (n == -1) { + fprintf(stderr,"Error - PR_Poll failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + goto def_exit; + } + if (PR_GetConnectStatus(&pd) == PR_SUCCESS) { + PRInt32 rv; + fprintf(stderr,"PR_GetConnectStatus succeeded, expected to fail\n"); + failed_already = 1; + goto def_exit; + } + rv = PR_GetError(); + DPRINTF(("Connection failed, successfully with PR_Error %d\n",rv)); +def_exit: + if (sockfd) { + PR_Close(sockfd); + sockfd = NULL; + } + if (conn_fd) { + PR_Close(conn_fd); + conn_fd = NULL; + } + if (failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/nblayer.c b/nsprpub/pr/tests/nblayer.c new file mode 100644 index 0000000000..9459866972 --- /dev/null +++ b/nsprpub/pr/tests/nblayer.c @@ -0,0 +1,778 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prmem.h" +#include "prprf.h" +#include "prlog.h" +#include "prerror.h" +#include "prnetdb.h" +#include "prthread.h" + +#include "plerror.h" +#include "plgetopt.h" +#include "prwin16.h" + +#include <stdlib.h> +#include <string.h> + +/* +** Testing layering of I/O +** +** The layered server +** A thread that acts as a server. It creates a TCP listener with a dummy +** layer pushed on top. Then listens for incoming connections. Each connection +** request for connection will be layered as well, accept one request, echo +** it back and close. +** +** The layered client +** Pretty much what you'd expect. +*/ + +static PRFileDesc *logFile; +static PRDescIdentity identity; +static PRNetAddr server_address; + +static PRIOMethods myMethods; + +typedef enum {rcv_get_debit, rcv_send_credit, rcv_data} RcvState; +typedef enum {xmt_send_debit, xmt_recv_credit, xmt_data} XmtState; + +struct PRFilePrivate +{ + RcvState rcvstate; + XmtState xmtstate; + PRInt32 rcvreq, rcvinprogress; + PRInt32 xmtreq, xmtinprogress; +}; + +typedef enum Verbosity {silent, quiet, chatty, noisy} Verbosity; + +static PRIntn minor_iterations = 5; +static PRIntn major_iterations = 1; +static Verbosity verbosity = quiet; + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +static PRUint16 default_port = 12273 PORT_INC_DO PORT_INC_3264; + +static PRFileDesc *PushLayer(PRFileDesc *stack) +{ + PRStatus rv; + PRFileDesc *layer = PR_CreateIOLayerStub(identity, &myMethods); + layer->secret = PR_NEWZAP(PRFilePrivate); + rv = PR_PushIOLayer(stack, PR_GetLayersIdentity(stack), layer); + PR_ASSERT(PR_SUCCESS == rv); + if (verbosity > quiet) { + PR_fprintf(logFile, "Pushed layer(0x%x) onto stack(0x%x)\n", layer, stack); + } + return stack; +} /* PushLayer */ + +static PRFileDesc *PopLayer(PRFileDesc *stack) +{ + PRFileDesc *popped = PR_PopIOLayer(stack, identity); + if (verbosity > quiet) { + PR_fprintf(logFile, "Popped layer(0x%x) from stack(0x%x)\n", popped, stack); + } + PR_DELETE(popped->secret); + popped->dtor(popped); + return stack; +} /* PopLayer */ + +static void PR_CALLBACK Client(void *arg) +{ + PRStatus rv; + PRIntn mits; + PRInt32 ready; + PRUint8 buffer[100]; + PRPollDesc polldesc; + PRIntn empty_flags = 0; + PRIntn bytes_read, bytes_sent; + PRFileDesc *stack = (PRFileDesc*)arg; + + /* Initialize the buffer so that Purify won't complain */ + memset(buffer, 0, sizeof(buffer)); + + rv = PR_Connect(stack, &server_address, PR_INTERVAL_NO_TIMEOUT); + if ((PR_FAILURE == rv) && (PR_IN_PROGRESS_ERROR == PR_GetError())) + { + if (verbosity > quiet) { + PR_fprintf(logFile, "Client connect 'in progress'\n"); + } + do + { + polldesc.fd = stack; + polldesc.out_flags = 0; + polldesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); + if ((1 != ready) /* if not 1, then we're dead */ + || (0 == (polldesc.in_flags & polldesc.out_flags))) + { + PR_NOT_REACHED("Whoa!"); + break; + } + if (verbosity > quiet) + PR_fprintf( + logFile, "Client connect 'in progress' [0x%x]\n", + polldesc.out_flags); + rv = PR_GetConnectStatus(&polldesc); + if ((PR_FAILURE == rv) + && (PR_IN_PROGRESS_ERROR != PR_GetError())) { + break; + } + } while (PR_FAILURE == rv); + } + PR_ASSERT(PR_SUCCESS == rv); + if (verbosity > chatty) { + PR_fprintf(logFile, "Client created connection\n"); + } + + for (mits = 0; mits < minor_iterations; ++mits) + { + bytes_sent = 0; + if (verbosity > quiet) { + PR_fprintf(logFile, "Client sending %d bytes\n", sizeof(buffer)); + } + do + { + if (verbosity > chatty) + PR_fprintf( + logFile, "Client sending %d bytes\n", + sizeof(buffer) - bytes_sent); + ready = PR_Send( + stack, buffer + bytes_sent, sizeof(buffer) - bytes_sent, + empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) { + PR_fprintf(logFile, "Client send status [%d]\n", ready); + } + if (0 < ready) { + bytes_sent += ready; + } + else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) + { + polldesc.fd = stack; + polldesc.out_flags = 0; + polldesc.in_flags = PR_POLL_WRITE; + ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); + if ((1 != ready) /* if not 1, then we're dead */ + || (0 == (polldesc.in_flags & polldesc.out_flags))) + { + PR_NOT_REACHED("Whoa!"); + break; + } + } + else { + break; + } + } while (bytes_sent < sizeof(buffer)); + PR_ASSERT(sizeof(buffer) == bytes_sent); + + bytes_read = 0; + do + { + if (verbosity > chatty) + PR_fprintf( + logFile, "Client receiving %d bytes\n", + bytes_sent - bytes_read); + ready = PR_Recv( + stack, buffer + bytes_read, bytes_sent - bytes_read, + empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) + PR_fprintf( + logFile, "Client receive status [%d]\n", ready); + if (0 < ready) { + bytes_read += ready; + } + else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) + { + polldesc.fd = stack; + polldesc.out_flags = 0; + polldesc.in_flags = PR_POLL_READ; + ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); + if ((1 != ready) /* if not 1, then we're dead */ + || (0 == (polldesc.in_flags & polldesc.out_flags))) + { + PR_NOT_REACHED("Whoa!"); + break; + } + } + else { + break; + } + } while (bytes_read < bytes_sent); + if (verbosity > chatty) { + PR_fprintf(logFile, "Client received %d bytes\n", bytes_read); + } + PR_ASSERT(bytes_read == bytes_sent); + } + + if (verbosity > quiet) { + PR_fprintf(logFile, "Client shutting down stack\n"); + } + + rv = PR_Shutdown(stack, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); +} /* Client */ + +static void PR_CALLBACK Server(void *arg) +{ + PRStatus rv; + PRInt32 ready; + PRUint8 buffer[100]; + PRFileDesc *service; + PRUintn empty_flags = 0; + struct PRPollDesc polldesc; + PRIntn bytes_read, bytes_sent; + PRFileDesc *stack = (PRFileDesc*)arg; + PRNetAddr client_address; + + do + { + if (verbosity > chatty) { + PR_fprintf(logFile, "Server accepting connection\n"); + } + service = PR_Accept(stack, &client_address, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) { + PR_fprintf(logFile, "Server accept status [0x%p]\n", service); + } + if ((NULL == service) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) + { + polldesc.fd = stack; + polldesc.out_flags = 0; + polldesc.in_flags = PR_POLL_READ | PR_POLL_EXCEPT; + ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); + if ((1 != ready) /* if not 1, then we're dead */ + || (0 == (polldesc.in_flags & polldesc.out_flags))) + { + PR_NOT_REACHED("Whoa!"); + break; + } + } + } while (NULL == service); + PR_ASSERT(NULL != service); + + if (verbosity > quiet) { + PR_fprintf(logFile, "Server accepting connection\n"); + } + + do + { + bytes_read = 0; + do + { + if (verbosity > chatty) + PR_fprintf( + logFile, "Server receiving %d bytes\n", + sizeof(buffer) - bytes_read); + ready = PR_Recv( + service, buffer + bytes_read, sizeof(buffer) - bytes_read, + empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (verbosity > chatty) { + PR_fprintf(logFile, "Server receive status [%d]\n", ready); + } + if (0 < ready) { + bytes_read += ready; + } + else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) + { + polldesc.fd = service; + polldesc.out_flags = 0; + polldesc.in_flags = PR_POLL_READ; + ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); + if ((1 != ready) /* if not 1, then we're dead */ + || (0 == (polldesc.in_flags & polldesc.out_flags))) + { + PR_NOT_REACHED("Whoa!"); + break; + } + } + else { + break; + } + } while (bytes_read < sizeof(buffer)); + + if (0 != bytes_read) + { + if (verbosity > chatty) { + PR_fprintf(logFile, "Server received %d bytes\n", bytes_read); + } + PR_ASSERT(bytes_read > 0); + + bytes_sent = 0; + do + { + ready = PR_Send( + service, buffer + bytes_sent, bytes_read - bytes_sent, + empty_flags, PR_INTERVAL_NO_TIMEOUT); + if (0 < ready) + { + bytes_sent += ready; + } + else if ((-1 == ready) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) + { + polldesc.fd = service; + polldesc.out_flags = 0; + polldesc.in_flags = PR_POLL_WRITE; + ready = PR_Poll(&polldesc, 1, PR_INTERVAL_NO_TIMEOUT); + if ((1 != ready) /* if not 1, then we're dead */ + || (0 == (polldesc.in_flags & polldesc.out_flags))) + { + PR_NOT_REACHED("Whoa!"); + break; + } + } + else { + break; + } + } while (bytes_sent < bytes_read); + PR_ASSERT(bytes_read == bytes_sent); + if (verbosity > chatty) { + PR_fprintf(logFile, "Server sent %d bytes\n", bytes_sent); + } + } + } while (0 != bytes_read); + + if (verbosity > quiet) { + PR_fprintf(logFile, "Server shutting down stack\n"); + } + rv = PR_Shutdown(service, PR_SHUTDOWN_BOTH); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); + +} /* Server */ + +static PRStatus PR_CALLBACK MyClose(PRFileDesc *fd) +{ + PR_DELETE(fd->secret); /* manage my secret file object */ + return (PR_GetDefaultIOMethods())->close(fd); /* let him do all the work */ +} /* MyClose */ + +static PRInt16 PR_CALLBACK MyPoll( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + PRInt16 my_flags, new_flags; + PRFilePrivate *mine = (PRFilePrivate*)fd->secret; + if (0 != (PR_POLL_READ & in_flags)) + { + /* client thinks he's reading */ + switch (mine->rcvstate) + { + case rcv_send_credit: + my_flags = (in_flags & ~PR_POLL_READ) | PR_POLL_WRITE; + break; + case rcv_data: + case rcv_get_debit: + my_flags = in_flags; + default: break; + } + } + else if (0 != (PR_POLL_WRITE & in_flags)) + { + /* client thinks he's writing */ + switch (mine->xmtstate) + { + case xmt_recv_credit: + my_flags = (in_flags & ~PR_POLL_WRITE) | PR_POLL_READ; + break; + case xmt_send_debit: + case xmt_data: + my_flags = in_flags; + default: break; + } + } + else { + PR_NOT_REACHED("How'd I get here?"); + } + new_flags = (fd->lower->methods->poll)(fd->lower, my_flags, out_flags); + if (verbosity > chatty) + PR_fprintf( + logFile, "Poll [i: 0x%x, m: 0x%x, o: 0x%x, n: 0x%x]\n", + in_flags, my_flags, *out_flags, new_flags); + return new_flags; +} /* MyPoll */ + +static PRFileDesc * PR_CALLBACK MyAccept( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRStatus rv; + PRFileDesc *newfd, *layer = fd; + PRFileDesc *newstack; + PRFilePrivate *newsecret; + + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + newstack = PR_NEW(PRFileDesc); + if (NULL == newstack) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + newsecret = PR_NEW(PRFilePrivate); + if (NULL == newsecret) + { + PR_DELETE(newstack); + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + *newstack = *fd; /* make a copy of the accepting layer */ + *newsecret = *fd->secret; + newstack->secret = newsecret; + + newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout); + if (NULL == newfd) + { + PR_DELETE(newsecret); + PR_DELETE(newstack); + return NULL; + } + + /* this PR_PushIOLayer call cannot fail */ + rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack); + PR_ASSERT(PR_SUCCESS == rv); + return newfd; /* that's it */ +} + +static PRInt32 PR_CALLBACK MyRecv( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + char *b; + PRInt32 rv; + PRFileDesc *lo = fd->lower; + PRFilePrivate *mine = (PRFilePrivate*)fd->secret; + + do + { + switch (mine->rcvstate) + { + case rcv_get_debit: + b = (char*)&mine->rcvreq; + mine->rcvreq = amount; + rv = lo->methods->recv( + lo, b + mine->rcvinprogress, + sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout); + if (0 == rv) { + goto closed; + } + if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) { + break; + } + mine->rcvinprogress += rv; /* accumulate the read */ + if (mine->rcvinprogress < sizeof(mine->rcvreq)) { + break; /* loop */ + } + mine->rcvstate = rcv_send_credit; + mine->rcvinprogress = 0; + case rcv_send_credit: + b = (char*)&mine->rcvreq; + rv = lo->methods->send( + lo, b + mine->rcvinprogress, + sizeof(mine->rcvreq) - mine->rcvinprogress, flags, timeout); + if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) { + break; + } + mine->rcvinprogress += rv; /* accumulate the read */ + if (mine->rcvinprogress < sizeof(mine->rcvreq)) { + break; /* loop */ + } + mine->rcvstate = rcv_data; + mine->rcvinprogress = 0; + case rcv_data: + b = (char*)buf; + rv = lo->methods->recv( + lo, b + mine->rcvinprogress, + mine->rcvreq - mine->rcvinprogress, flags, timeout); + if (0 == rv) { + goto closed; + } + if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) { + break; + } + mine->rcvinprogress += rv; /* accumulate the read */ + if (mine->rcvinprogress < amount) { + break; /* loop */ + } + mine->rcvstate = rcv_get_debit; + mine->rcvinprogress = 0; + return mine->rcvreq; /* << -- that's it! */ + default: + break; + } + } while (-1 != rv); + return rv; +closed: + mine->rcvinprogress = 0; + mine->rcvstate = rcv_get_debit; + return 0; +} /* MyRecv */ + +static PRInt32 PR_CALLBACK MySend( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + char *b; + PRInt32 rv; + PRFileDesc *lo = fd->lower; + PRFilePrivate *mine = (PRFilePrivate*)fd->secret; + + do + { + switch (mine->xmtstate) + { + case xmt_send_debit: + b = (char*)&mine->xmtreq; + mine->xmtreq = amount; + rv = lo->methods->send( + lo, b - mine->xmtinprogress, + sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout); + if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) { + break; + } + mine->xmtinprogress += rv; + if (mine->xmtinprogress < sizeof(mine->xmtreq)) { + break; + } + mine->xmtstate = xmt_recv_credit; + mine->xmtinprogress = 0; + case xmt_recv_credit: + b = (char*)&mine->xmtreq; + rv = lo->methods->recv( + lo, b + mine->xmtinprogress, + sizeof(mine->xmtreq) - mine->xmtinprogress, flags, timeout); + if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) { + break; + } + mine->xmtinprogress += rv; + if (mine->xmtinprogress < sizeof(mine->xmtreq)) { + break; + } + mine->xmtstate = xmt_data; + mine->xmtinprogress = 0; + case xmt_data: + b = (char*)buf; + rv = lo->methods->send( + lo, b + mine->xmtinprogress, + mine->xmtreq - mine->xmtinprogress, flags, timeout); + if ((-1 == rv) && (PR_WOULD_BLOCK_ERROR == PR_GetError())) { + break; + } + mine->xmtinprogress += rv; + if (mine->xmtinprogress < amount) { + break; + } + mine->xmtstate = xmt_send_debit; + mine->xmtinprogress = 0; + return mine->xmtreq; /* <<-- That's the one! */ + default: + break; + } + } while (-1 != rv); + return rv; +} /* MySend */ + +static Verbosity ChangeVerbosity(Verbosity verbosity, PRIntn delta) +{ + PRIntn verbage = (PRIntn)verbosity + delta; + if (verbage < (PRIntn)silent) { + verbage = (PRIntn)silent; + } + else if (verbage > (PRIntn)noisy) { + verbage = (PRIntn)noisy; + } + return (Verbosity)verbage; +} /* ChangeVerbosity */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PLOptStatus os; + PRFileDesc *client, *service; + PRNetAddr any_address; + const char *server_name = NULL; + const PRIOMethods *stubMethods; + PRThread *client_thread, *server_thread; + PRThreadScope thread_scope = PR_LOCAL_THREAD; + PRSocketOptionData socket_noblock, socket_nodelay; + PLOptState *opt = PL_CreateOptState(argc, argv, "dqGC:c:p:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: + server_name = opt->value; + break; + case 'd': /* debug mode */ + if (verbosity < noisy) { + verbosity = ChangeVerbosity(verbosity, 1); + } + break; + case 'q': /* debug mode */ + if (verbosity > silent) { + verbosity = ChangeVerbosity(verbosity, -1); + } + break; + case 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'C': /* number of threads waiting */ + major_iterations = atoi(opt->value); + break; + case 'c': /* number of client threads */ + minor_iterations = atoi(opt->value); + break; + case 'p': /* default port */ + default_port = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + PR_STDIO_INIT(); + + logFile = PR_GetSpecialFD(PR_StandardError); + identity = PR_GetUniqueIdentity("Dummy"); + stubMethods = PR_GetDefaultIOMethods(); + + /* + ** The protocol we're going to implement is one where in order to initiate + ** a send, the sender must first solicit permission. Therefore, every + ** send is really a send - receive - send sequence. + */ + myMethods = *stubMethods; /* first get the entire batch */ + myMethods.accept = MyAccept; /* then override the ones we care about */ + myMethods.recv = MyRecv; /* then override the ones we care about */ + myMethods.send = MySend; /* then override the ones we care about */ + myMethods.close = MyClose; /* then override the ones we care about */ + myMethods.poll = MyPoll; /* then override the ones we care about */ + + if (NULL == server_name) + rv = PR_InitializeNetAddr( + PR_IpAddrLoopback, default_port, &server_address); + else + { + rv = PR_StringToNetAddr(server_name, &server_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_InitializeNetAddr( + PR_IpAddrNull, default_port, &server_address); + } + PR_ASSERT(PR_SUCCESS == rv); + + socket_noblock.value.non_blocking = PR_TRUE; + socket_noblock.option = PR_SockOpt_Nonblocking; + socket_nodelay.value.no_delay = PR_TRUE; + socket_nodelay.option = PR_SockOpt_NoDelay; + + /* one type w/o layering */ + + while (major_iterations-- > 0) + { + if (verbosity > silent) { + PR_fprintf(logFile, "Beginning non-layered test\n"); + } + + client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); + service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); + + rv = PR_SetSocketOption(client, &socket_noblock); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_SetSocketOption(service, &socket_noblock); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_SetSocketOption(client, &socket_nodelay); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_SetSocketOption(service, &socket_nodelay); + PR_ASSERT(PR_SUCCESS == rv); + + rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); + + server_thread = PR_CreateThread( + PR_USER_THREAD, Server, service, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != server_thread); + + client_thread = PR_CreateThread( + PR_USER_THREAD, Client, client, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != client_thread); + + rv = PR_JoinThread(client_thread); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(server_thread); + PR_ASSERT(PR_SUCCESS == rv); + + rv = PR_Close(client); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(service); PR_ASSERT(PR_SUCCESS == rv); + if (verbosity > silent) { + PR_fprintf(logFile, "Ending non-layered test\n"); + } + + /* with layering */ + if (verbosity > silent) { + PR_fprintf(logFile, "Beginning layered test\n"); + } + client = PR_NewTCPSocket(); PR_ASSERT(NULL != client); + service = PR_NewTCPSocket(); PR_ASSERT(NULL != service); + + rv = PR_SetSocketOption(client, &socket_noblock); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_SetSocketOption(service, &socket_noblock); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_SetSocketOption(client, &socket_nodelay); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_SetSocketOption(service, &socket_nodelay); + PR_ASSERT(PR_SUCCESS == rv); + + PushLayer(client); + PushLayer(service); + + rv = PR_InitializeNetAddr(PR_IpAddrAny, default_port, &any_address); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Bind(service, &any_address); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Listen(service, 10); PR_ASSERT(PR_SUCCESS == rv); + + server_thread = PR_CreateThread( + PR_USER_THREAD, Server, service, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != server_thread); + + client_thread = PR_CreateThread( + PR_USER_THREAD, Client, client, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 16 * 1024); + PR_ASSERT(NULL != client_thread); + + rv = PR_JoinThread(client_thread); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(server_thread); + PR_ASSERT(PR_SUCCESS == rv); + + rv = PR_Close(PopLayer(client)); PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Close(PopLayer(service)); PR_ASSERT(PR_SUCCESS == rv); + if (verbosity > silent) { + PR_fprintf(logFile, "Ending layered test\n"); + } + } + return 0; +} /* main */ + +/* nblayer.c */ diff --git a/nsprpub/pr/tests/nonblock.c b/nsprpub/pr/tests/nonblock.c new file mode 100644 index 0000000000..f24ffb02d0 --- /dev/null +++ b/nsprpub/pr/tests/nonblock.c @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "prio.h" +#include "prerror.h" +#include "prlog.h" +#include "prprf.h" +#include "prnetdb.h" +#include "plerror.h" +#include "obsolete/probslet.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define NUMBER_ROUNDS 5 + +#if defined(WIN16) +/* +** Make win16 unit_time interval 300 milliseconds, others get 100 +*/ +#define UNIT_TIME 200 /* unit time in milliseconds */ +#else +#define UNIT_TIME 100 /* unit time in milliseconds */ +#endif +#define CHUNK_SIZE 10 +#undef USE_PR_SELECT /* If defined, we use PR_Select. + * If not defined, use PR_Poll instead. */ + +#if defined(USE_PR_SELECT) +#include "pprio.h" +#endif + +static void PR_CALLBACK +clientThreadFunc(void *arg) +{ + PRUintn port = (PRUintn)arg; + PRFileDesc *sock; + PRNetAddr addr; + char buf[CHUNK_SIZE]; + int i; + PRIntervalTime unitTime = PR_MillisecondsToInterval(UNIT_TIME); + PRSocketOptionData optval; + PRStatus retVal; + PRInt32 nBytes; + + /* Initialize the buffer so that Purify won't complain */ + memset(buf, 0, sizeof(buf)); + + addr.inet.family = PR_AF_INET; + addr.inet.port = PR_htons((PRUint16)port); + addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.ip); + + /* time 1 */ + PR_Sleep(unitTime); + sock = PR_NewTCPSocket(); + optval.option = PR_SockOpt_Nonblocking; + optval.value.non_blocking = PR_TRUE; + PR_SetSocketOption(sock, &optval); + retVal = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); + if (retVal == PR_FAILURE && PR_GetError() == PR_IN_PROGRESS_ERROR) { +#if !defined(USE_PR_SELECT) + PRPollDesc pd; + PRInt32 n; + fprintf(stderr, "connect: EWOULDBLOCK, good\n"); + pd.fd = sock; + pd.in_flags = PR_POLL_WRITE; + n = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(n == 1); + PR_ASSERT(pd.out_flags == PR_POLL_WRITE); +#else + PR_fd_set writeSet; + PRInt32 n; + fprintf(stderr, "connect: EWOULDBLOCK, good\n"); + PR_FD_ZERO(&writeSet); + PR_FD_SET(sock, &writeSet); + n = PR_Select(0, NULL, &writeSet, NULL, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(n == 1); + PR_ASSERT(PR_FD_ISSET(sock, &writeSet)); +#endif + } + printf("client connected\n"); + fflush(stdout); + + /* time 4, 7, 11, etc. */ + for (i = 0; i < NUMBER_ROUNDS; i++) { + PR_Sleep(3 * unitTime); + nBytes = PR_Write(sock, buf, sizeof(buf)); + if (nBytes == -1) { + if (PR_GetError() == PR_WOULD_BLOCK_ERROR) { + fprintf(stderr, "write: EWOULDBLOCK\n"); + exit(1); + } else { + fprintf(stderr, "write: failed\n"); + } + } + printf("client sent %d bytes\n", nBytes); + fflush(stdout); + } + + PR_Close(sock); +} + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + PRFileDesc *listenSock, *sock; + PRUint16 listenPort; + PRNetAddr addr; + char buf[CHUNK_SIZE]; + PRThread *clientThread; + PRInt32 retVal; + PRSocketOptionData optval; + PRIntn i; + PRIntervalTime unitTime = PR_MillisecondsToInterval(UNIT_TIME); + + /* Create a listening socket */ + if ((listenSock = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + exit(1); + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + exit(1); + } + if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + listenPort = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + exit(1); + } + + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on port %hu\n\n", + listenPort); + printf("%s", buf); + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + exit(1); + } + + printf("client thread created.\n"); + + optval.option = PR_SockOpt_Nonblocking; + optval.value.non_blocking = PR_TRUE; + PR_SetSocketOption(listenSock, &optval); + /* time 0 */ + sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (sock != NULL || PR_GetError() != PR_WOULD_BLOCK_ERROR) { + PL_PrintError("First Accept\n"); + fprintf(stderr, "First PR_Accept() xxx\n" ); + exit(1); + } + printf("accept: EWOULDBLOCK, good\n"); + fflush(stdout); + /* time 2 */ + PR_Sleep(2 * unitTime); + sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (sock == NULL) { + PL_PrintError("Second Accept\n"); + fprintf(stderr, "Second PR_Accept() failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("accept: succeeded, good\n"); + fflush(stdout); + PR_Close(listenSock); + + PR_SetSocketOption(sock, &optval); + + /* time 3, 5, 6, 8, etc. */ + for (i = 0; i < NUMBER_ROUNDS; i++) { + PR_Sleep(unitTime); + retVal = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); + if (retVal != -1 || PR_GetError() != PR_WOULD_BLOCK_ERROR) { + PL_PrintError("First Receive:\n"); + fprintf(stderr, "First PR_Recv: retVal: %ld, Error: %ld\n", + retVal, PR_GetError()); + exit(1); + } + printf("read: EWOULDBLOCK, good\n"); + fflush(stdout); + PR_Sleep(2 * unitTime); + retVal = PR_Recv(sock, buf, sizeof(buf), 0, PR_INTERVAL_NO_TIMEOUT); + if (retVal != CHUNK_SIZE) { + PL_PrintError("Second Receive:\n"); + fprintf(stderr, "Second PR_Recv: retVal: %ld, Error: %ld\n", + retVal, PR_GetError()); + exit(1); + } + printf("read: %d bytes, good\n", retVal); + fflush(stdout); + } + PR_Close(sock); + + printf("All tests finished\n"); + printf("PASS\n"); + return 0; +} + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ + diff --git a/nsprpub/pr/tests/ntioto.c b/nsprpub/pr/tests/ntioto.c new file mode 100644 index 0000000000..809e0ae5c6 --- /dev/null +++ b/nsprpub/pr/tests/ntioto.c @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: ntioto.c +** Description: +** This test, ntioto.c, was designed to reproduce a bug reported by NES +** on WindowsNT (fibers implementation). NSPR was asserting in ntio.c +** after PR_AcceptRead() had timed out. I/O performed subsequent to the +** call to PR_AcceptRead() could complete on a CPU other than the one +** on which it was started. The assert in ntio.c detected this, then +** asserted. +** +** Design: +** This test will fail with an assert in ntio.c if the problem it was +** designed to catch occurs. It returns 0 otherwise. +** +** The main() thread initializes and tears things down. A file is +** opened for writing; this file will be written to by AcceptThread() +** and JitterThread(). Main() creates a socket for reading, listens +** and binds the socket. +** +** ConnectThread() connects to the socket created by main, then polls +** the "state" variable. When state is AllDone, ConnectThread() exits. +** +** AcceptThread() calls PR_AcceptRead() on the socket. He fully expects +** it to time out. After the timeout, AccpetThread() interacts with +** JitterThread() via a common condition variable and the state +** variable. The two threads ping-pong back and forth, each thread +** writes the the file opened by main. This should provoke the +** condition reported by NES (if we didn't fix it). +** +** The failure is not solid. It may fail within a few ping-pongs between +** AcceptThread() and JitterThread() or may take a while. The default +** iteration count, jitter, is set by DEFAULT_JITTER. This may be +** modified at the command line with the -j option. +** +*/ + +#include <plgetopt.h> +#include <nspr.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* +** Test harness infrastructure +*/ +PRLogModuleInfo *lm; +PRLogModuleLevel msgLevel = PR_LOG_NONE; +PRIntn debug = 0; +PRIntn verbose = 0; +PRUint32 failed_already = 0; +/* end Test harness infrastructure */ + +/* JITTER_DEFAULT: the number of times AcceptThread() and JitterThread() ping-pong */ +#define JITTER_DEFAULT 100000 + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 + +PRIntervalTime timeout; +PRNetAddr listenAddr; +PRFileDesc *listenSock; +PRLock *ml; +PRCondVar *cv; +volatile enum { + RunJitter, + RunAcceptRead, + AllDone +} state = RunAcceptRead; +PRFileDesc *file1; +PRIntn iCounter = 0; +PRIntn jitter = JITTER_DEFAULT; +PRBool resume = PR_FALSE; + +/* +** Emit help text for this test +*/ +static void Help( void ) +{ + printf("Template: Help(): display your help message(s) here"); + exit(1); +} /* end Help() */ + + +/* +** static computation of PR_AcceptRead() buffer size. +*/ +#define ACCEPT_READ_DATASIZE 10 +#define ACCEPT_READ_BUFSIZE (PR_ACCEPT_READ_BUF_OVERHEAD + ACCEPT_READ_DATASIZE) + +static void AcceptThread(void *arg) +{ + PRIntn bytesRead; + char dataBuf[ACCEPT_READ_BUFSIZE]; + PRFileDesc *arSock; + PRNetAddr *arAddr; + + bytesRead = PR_AcceptRead( listenSock, + &arSock, + &arAddr, + dataBuf, + ACCEPT_READ_DATASIZE, + PR_SecondsToInterval(1)); + + if ( bytesRead == -1 && PR_GetError() == PR_IO_TIMEOUT_ERROR ) { + if ( debug ) { + printf("AcceptRead timed out\n"); + } + } else { + if ( debug ) { + printf("Oops! read: %d, error: %d\n", bytesRead, PR_GetError()); + } + } + + while( state != AllDone ) { + PR_Lock( ml ); + while( state != RunAcceptRead ) { + PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT ); + } + if ( ++iCounter >= jitter ) { + state = AllDone; + } + else { + state = RunJitter; + } + if ( verbose ) { + printf("."); + } + PR_NotifyCondVar( cv ); + PR_Unlock( ml ); + PR_Write( file1, ".", 1 ); + } + + return; +} /* end AcceptThread() */ + +static void JitterThread(void *arg) +{ + while( state != AllDone ) { + PR_Lock( ml ); + while( state != RunJitter && state != AllDone ) { + PR_WaitCondVar( cv, PR_INTERVAL_NO_TIMEOUT ); + } + if ( state != AllDone) { + state = RunAcceptRead; + } + if ( verbose ) { + printf("+"); + } + PR_NotifyCondVar( cv ); + PR_Unlock( ml ); + PR_Write( file1, "+", 1 ); + } + return; +} /* end Goofy() */ + +static void ConnectThread( void *arg ) +{ + PRStatus rv; + PRFileDesc *clientSock; + PRNetAddr serverAddress; + clientSock = PR_NewTCPSocket(); + + PR_ASSERT(clientSock); + + if ( resume ) { + if ( debug ) { + printf("pausing 3 seconds before connect\n"); + } + PR_Sleep( PR_SecondsToInterval(3)); + } + + memset(&serverAddress, 0, sizeof(serverAddress)); + rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddress); + PR_ASSERT( PR_SUCCESS == rv ); + rv = PR_Connect( clientSock, + &serverAddress, + PR_SecondsToInterval(1)); + PR_ASSERT( PR_SUCCESS == rv ); + + /* that's all we do. ... Wait for the acceptread() to timeout */ + while( state != AllDone ) { + PR_Sleep( PR_SecondsToInterval(1)); + } + return; +} /* end ConnectThread() */ + + +int main(int argc, char **argv) +{ + PRThread *tJitter; + PRThread *tAccept; + PRThread *tConnect; + PRStatus rv; + /* This test if valid for WinNT only! */ + +#if !defined(WINNT) + return 0; +#endif + + { + /* + ** Get command line options + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdrvj:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug */ + debug = 1; + msgLevel = PR_LOG_ERROR; + break; + case 'v': /* verbose mode */ + verbose = 1; + msgLevel = PR_LOG_DEBUG; + break; + case 'j': + jitter = atoi(opt->value); + if ( jitter == 0) { + jitter = JITTER_DEFAULT; + } + break; + case 'r': + resume = PR_TRUE; + break; + case 'h': /* help message */ + Help(); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } + + lm = PR_NewLogModule("Test"); /* Initialize logging */ + + /* set concurrency */ + PR_SetConcurrency( 4 ); + + /* setup thread synchronization mechanics */ + ml = PR_NewLock(); + cv = PR_NewCondVar( ml ); + + /* setup a tcp socket */ + memset(&listenAddr, 0, sizeof(listenAddr)); + rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr); + PR_ASSERT( PR_SUCCESS == rv ); + + listenSock = PR_NewTCPSocket(); + PR_ASSERT( listenSock ); + + rv = PR_Bind( listenSock, &listenAddr); + PR_ASSERT( PR_SUCCESS == rv ); + + rv = PR_Listen( listenSock, 5 ); + PR_ASSERT( PR_SUCCESS == rv ); + + /* open a file for writing, provoke bug */ + file1 = PR_Open("xxxTestFile", PR_CREATE_FILE | PR_RDWR, 666); + + /* create Connect thread */ + tConnect = PR_CreateThread( + PR_USER_THREAD, ConnectThread, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0 ); + PR_ASSERT( tConnect ); + + /* create jitter off thread */ + tJitter = PR_CreateThread( + PR_USER_THREAD, JitterThread, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0 ); + PR_ASSERT( tJitter ); + + /* create acceptread thread */ + tAccept = PR_CreateThread( + PR_USER_THREAD, AcceptThread, NULL, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, 0 ); + PR_ASSERT( tAccept ); + + /* wait for all threads to quit, then terminate gracefully */ + PR_JoinThread( tConnect ); + PR_JoinThread( tAccept ); + PR_JoinThread( tJitter ); + PR_Close( listenSock ); + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + PR_Close( file1 ); + PR_Delete( "xxxTestFile"); + + /* test return and exit */ + if (debug) { + printf("%s\n", (failed_already)? "FAIL" : "PASS"); + } + return( (failed_already == PR_TRUE )? 1 : 0 ); +} /* main() */ +/* end ntioto.c */ diff --git a/nsprpub/pr/tests/ntoh.c b/nsprpub/pr/tests/ntoh.c new file mode 100644 index 0000000000..980a1b4983 --- /dev/null +++ b/nsprpub/pr/tests/ntoh.c @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A test program for PR_htons, PR_ntohs, PR_htonl, PR_ntohl, + * PR_htonll, and PR_ntohll. + */ + +#include "prnetdb.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +/* Byte sequence in network byte order */ +static unsigned char bytes_n[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + +/* Integers in host byte order */ +static PRUint16 s_h = 0x0102; +static PRUint32 l_h = 0x01020304; +static PRUint64 ll_h = LL_INIT(0x01020304, 0x05060708); + +int main(int argc, char **argv) +{ + union { + PRUint16 s; + PRUint32 l; + PRUint64 ll; + unsigned char bytes[8]; + } un; + + un.s = s_h; + printf("%u %u\n", + un.bytes[0], un.bytes[1]); + un.s = PR_htons(un.s); + printf("%u %u\n", + un.bytes[0], un.bytes[1]); + if (memcmp(un.bytes, bytes_n, 2)) { + fprintf(stderr, "PR_htons failed\n"); + exit(1); + } + un.s = PR_ntohs(un.s); + printf("%u %u\n", + un.bytes[0], un.bytes[1]); + if (un.s != s_h) { + fprintf(stderr, "PR_ntohs failed\n"); + exit(1); + } + + un.l = l_h; + printf("%u %u %u %u\n", + un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3]); + un.l = PR_htonl(un.l); + printf("%u %u %u %u\n", + un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3]); + if (memcmp(un.bytes, bytes_n, 4)) { + fprintf(stderr, "PR_htonl failed\n"); + exit(1); + } + un.l = PR_ntohl(un.l); + printf("%u %u %u %u\n", + un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3]); + if (un.l != l_h) { + fprintf(stderr, "PR_ntohl failed\n"); + exit(1); + } + + un.ll = ll_h; + printf("%u %u %u %u %u %u %u %u\n", + un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3], + un.bytes[4], un.bytes[5], un.bytes[6], un.bytes[7]); + un.ll = PR_htonll(un.ll); + printf("%u %u %u %u %u %u %u %u\n", + un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3], + un.bytes[4], un.bytes[5], un.bytes[6], un.bytes[7]); + if (memcmp(un.bytes, bytes_n, 8)) { + fprintf(stderr, "PR_htonll failed\n"); + exit(1); + } + un.ll = PR_ntohll(un.ll); + printf("%u %u %u %u %u %u %u %u\n", + un.bytes[0], un.bytes[1], un.bytes[2], un.bytes[3], + un.bytes[4], un.bytes[5], un.bytes[6], un.bytes[7]); + if (LL_NE(un.ll, ll_h)) { + fprintf(stderr, "PR_ntohll failed\n"); + exit(1); + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/obsints.c b/nsprpub/pr/tests/obsints.c new file mode 100644 index 0000000000..30501ad746 --- /dev/null +++ b/nsprpub/pr/tests/obsints.c @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test: obsints.c + * + * Description: make sure that protypes.h defines the obsolete integer + * types intn, uintn, uint, int8, uint8, int16, uint16, int32, uint32, + * int64, and uint64. + */ + +#include <stdio.h> + +#ifdef NO_NSPR_10_SUPPORT + +/* nothing to do */ +int main(int argc, char **argv) +{ + printf("PASS\n"); + return 0; +} + +#else /* NO_NSPR_10_SUPPORT */ + +#include "prtypes.h" /* which includes protypes.h */ + +int main(int argc, char **argv) +{ + /* + * Compilation fails if any of these integer types are not + * defined by protypes.h. + */ + intn in; + uintn uin; + uint ui; + int8 i8; + uint8 ui8; + int16 i16; + uint16 ui16; + int32 i32; + uint32 ui32; + int64 i64; + uint64 ui64; + + printf("PASS\n"); + return 0; +} + +#endif /* NO_NSPR_10_SUPPORT */ diff --git a/nsprpub/pr/tests/op_2long.c b/nsprpub/pr/tests/op_2long.c new file mode 100644 index 0000000000..7a6b13122c --- /dev/null +++ b/nsprpub/pr/tests/op_2long.c @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: op_2long.c +** +** Description: Test Program to verify the PR_NAME_TOO_LONG_ERROR +** +** Modification History: +** 03-June-97 AGarcia- Initial version +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "prinit.h" +#include "prmem.h" +#include "prio.h" +#include "prerror.h" +#include <stdio.h> +#include "plerror.h" +#include "plgetopt.h" + +static PRFileDesc *t1; +PRIntn error_code; + +/* + * should exceed any system's maximum file name length + * Note: was set at 4096. This is legal on some unix (Linux 2.1+) platforms. + * + */ +#define TOO_LONG 5000 + +int main(int argc, char **argv) +{ + char nameTooLong[TOO_LONG]; + int i; + + /* Generate a really long pathname */ + for (i = 0; i < TOO_LONG - 1; i++) { + if (i % 10 == 0) { + nameTooLong[i] = '/'; + } else { + nameTooLong[i] = 'a'; + } + } + nameTooLong[TOO_LONG - 1] = 0; + + PR_STDIO_INIT(); + t1 = PR_Open(nameTooLong, PR_RDWR, 0666); + if (t1 == NULL) { + if (PR_GetError() == PR_NAME_TOO_LONG_ERROR) { + PL_PrintError("error code is"); + printf ("PASS\n"); + return 0; + } + else { + PL_PrintError("error code is"); + printf ("FAIL\n"); + return 1; + } + } + + else { + printf ("Test passed\n"); + return 0; + } + + + +} diff --git a/nsprpub/pr/tests/op_excl.c b/nsprpub/pr/tests/op_excl.c new file mode 100644 index 0000000000..e214af1257 --- /dev/null +++ b/nsprpub/pr/tests/op_excl.c @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: op_excl.c +** +** Description: Test Program to verify function of PR_EXCL open flag +** +** Modification History: +** 27-Oct-1999 lth. Initial version +***********************************************************************/ + +#include <plgetopt.h> +#include <nspr.h> +#include <stdio.h> +#include <stdlib.h> + +/* +** Test harness infrastructure +*/ +PRLogModuleInfo *lm; +PRLogModuleLevel msgLevel = PR_LOG_NONE; +PRIntn debug = 0; +PRUint32 failed_already = 0; +/* end Test harness infrastructure */ +/* +** Emit help text for this test +*/ +static void Help( void ) +{ + printf("op_excl: Help"); + printf("op_excl [-d]"); + printf("-d enables debug messages"); + exit(1); +} /* end Help() */ + + + +int main(int argc, char **argv) +{ + PRFileDesc *fd; + PRStatus rv; + PRInt32 written; + char outBuf[] = "op_excl.c test file"; +#define OUT_SIZE sizeof(outBuf) +#define NEW_FILENAME "xxxExclNewFile" + + { + /* + ** Get command line options + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "hd"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug */ + debug = 1; + msgLevel = PR_LOG_ERROR; + break; + case 'h': /* help message */ + Help(); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } + + lm = PR_NewLogModule("Test"); /* Initialize logging */ + + /* + ** First, open a file, PR_EXCL, we believe not to exist + */ + fd = PR_Open( NEW_FILENAME, PR_CREATE_FILE | PR_EXCL | PR_WRONLY, 0666 ); + if ( NULL == fd ) { + if (debug) { + fprintf( stderr, "Open exclusive. Expected success, got failure\n"); + } + failed_already = 1; + goto Finished; + } + + written = PR_Write( fd, outBuf, OUT_SIZE ); + if ( OUT_SIZE != written ) { + if (debug) { + fprintf( stderr, "Write after open exclusive failed\n"); + } + failed_already = 1; + goto Finished; + } + + rv = PR_Close(fd); + if ( PR_FAILURE == rv ) { + if (debug) { + fprintf( stderr, "Close after open exclusive failed\n"); + } + failed_already = 1; + goto Finished; + } + + /* + ** Second, open the same file, PR_EXCL, expect it to fail + */ + fd = PR_Open( NEW_FILENAME, PR_CREATE_FILE | PR_EXCL | PR_WRONLY, 0666 ); + if ( NULL != fd ) { + if (debug) { + fprintf( stderr, "Open exclusive. Expected failure, got success\n"); + } + failed_already = 1; + PR_Close(fd); + } + + rv = PR_Delete( NEW_FILENAME ); + if ( PR_FAILURE == rv ) { + if (debug) { + fprintf( stderr, "PR_Delete() failed\n"); + } + failed_already = 1; + } + +Finished: + if (debug) { + printf("%s\n", (failed_already)? "FAIL" : "PASS"); + } + return( (failed_already == PR_TRUE )? 1 : 0 ); +} /* main() */ +/* end op_excl.c */ + diff --git a/nsprpub/pr/tests/op_filnf.c b/nsprpub/pr/tests/op_filnf.c new file mode 100644 index 0000000000..b4ab2fb156 --- /dev/null +++ b/nsprpub/pr/tests/op_filnf.c @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: op_filnf.c +** +** Description: Test Program to verify the PR_FILE_NOT_FOUND_ERROR +** This test program also uses the TRUNCATE option +** +** Modification History: +** 03-June-97 AGarcia- Initial version +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "prinit.h" +#include "prmem.h" +#include "prio.h" +#include "prerror.h" +#include <stdio.h> +#include "plgetopt.h" + +static PRFileDesc *t1; +PRIntn error_code; + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + t1 = PR_Open("./tmp-ttools/err03.tmp", PR_TRUNCATE | PR_RDWR, 0666); + if (t1 == NULL) { + if (PR_GetError() == PR_FILE_NOT_FOUND_ERROR) { + printf ("error code is %d \n", PR_GetError()); + printf ("PASS\n"); + return 0; + } + else { + printf ("error code is %d \n", PR_GetError()); + printf ("FAIL\n"); + return 1; + } + } + PR_Close(t1); + printf ("opened a file that should not exist\n"); + printf ("FAIL\n"); + return 1; +} diff --git a/nsprpub/pr/tests/op_filok.c b/nsprpub/pr/tests/op_filok.c new file mode 100644 index 0000000000..4f8688b7f1 --- /dev/null +++ b/nsprpub/pr/tests/op_filok.c @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: op_filok.c +** +** Description: Test Program to verify the PR_Open finding an existing file. +** +** Modification History: +** 03-June-97 AGarcia- Initial version +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "prinit.h" +#include "prmem.h" +#include "prio.h" +#include "prerror.h" +#include <stdio.h> + +static PRFileDesc *t1; + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + + t1 = PR_Open(argv[0], PR_RDONLY, 0666); + + if (t1 == NULL) { + printf ("error code is %d \n", PR_GetError()); + printf ("File %s should be found\n", argv[0]); + return 1; + } else { + if (PR_Close(t1) == PR_SUCCESS) { + printf ("Test passed \n"); + return 0; + } else { + printf ("cannot close file\n"); + printf ("error code is %d\n", PR_GetError()); + return 1; + } + } +} diff --git a/nsprpub/pr/tests/op_noacc.c b/nsprpub/pr/tests/op_noacc.c new file mode 100644 index 0000000000..21ad04be46 --- /dev/null +++ b/nsprpub/pr/tests/op_noacc.c @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: op_noacc.c +** +** Description: Test Program to verify the PR_NO_ACCESS_RIGHTS_ERROR in PR_Open +** +** Modification History: +** 03-June-97 AGarcia- Initial version +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "prinit.h" +#include "prmem.h" +#include "prio.h" +#include "prerror.h" +#include <stdio.h> +#include "plgetopt.h" + +static PRFileDesc *err01; +PRIntn error_code; + +int main(int argc, char **argv) +{ +#ifdef XP_PC + printf("op_noacc: Test not valid on MS-Windows.\n\tNo concept of 'mode' on Open() call\n"); + return(0); +#endif + + + PR_STDIO_INIT(); + err01 = PR_Open("err01.tmp", PR_CREATE_FILE | PR_RDWR, 0); + if (err01 == NULL) { + int error = PR_GetError(); + printf ("error code is %d\n", error); + if (error == PR_NO_ACCESS_RIGHTS_ERROR) { + printf ("PASS\n"); + return 0; + } + } + printf ("FAIL\n"); + return 1; +} + diff --git a/nsprpub/pr/tests/op_nofil.c b/nsprpub/pr/tests/op_nofil.c new file mode 100644 index 0000000000..12d542017c --- /dev/null +++ b/nsprpub/pr/tests/op_nofil.c @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: op_nofil.c +** +** Description: Test Program to verify the PR_FILE_NOT_FOUND_ERROR +** +** Modification History: +** 03-June-97 AGarcia- Initial version +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "prinit.h" +#include "prmem.h" +#include "prio.h" +#include "prerror.h" +#include <stdio.h> +#include "plgetopt.h" + +/* + * A file name that cannot exist + */ +#define NO_SUCH_FILE "/no/such/file.tmp" + +static PRFileDesc *t1; + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + t1 = PR_Open(NO_SUCH_FILE, PR_RDONLY, 0666); + if (t1 == NULL) { + if (PR_GetError() == PR_FILE_NOT_FOUND_ERROR) { + printf ("error code is PR_FILE_NOT_FOUND_ERROR, as expected\n"); + printf ("PASS\n"); + return 0; + } else { + printf ("error code is %d \n", PR_GetError()); + printf ("FAIL\n"); + return 1; + } + } + printf ("File %s exists on this machine!?\n", NO_SUCH_FILE); + if (PR_Close(t1) == PR_FAILURE) { + printf ("cannot close file\n"); + printf ("error code is %d \n", PR_GetError()); + } + printf ("FAIL\n"); + return 1; +} diff --git a/nsprpub/pr/tests/openfile.c b/nsprpub/pr/tests/openfile.c new file mode 100644 index 0000000000..265e041895 --- /dev/null +++ b/nsprpub/pr/tests/openfile.c @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This test calls PR_OpenFile to create a bunch of files + * with various file modes. + */ + +#include "prio.h" +#include "prerror.h" +#include "prinit.h" + +#include <stdio.h> +#include <stdlib.h> + +#define TEMPLATE_FILE_NAME "template.txt" + +int main(int argc, char **argv) +{ + FILE *template; + char buf[32]; + PRInt32 nbytes; + PRFileDesc *fd; + + + /* Write in text mode. Let stdio deal with line endings. */ + template = fopen(TEMPLATE_FILE_NAME, "w"); + fputs("line 1\nline 2\n", template); + fclose(template); + + /* Read in binary mode */ + fd = PR_OpenFile(TEMPLATE_FILE_NAME, PR_RDONLY, 0666); + nbytes = PR_Read(fd, buf, sizeof(buf)); + PR_Close(fd); + PR_Delete(TEMPLATE_FILE_NAME); + + fd = PR_OpenFile("tfil0700.txt", PR_RDWR | PR_CREATE_FILE, 0700); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0500.txt", PR_RDWR | PR_CREATE_FILE, 0500); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0400.txt", PR_RDWR | PR_CREATE_FILE, 0400); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0644.txt", PR_RDWR | PR_CREATE_FILE, 0644); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0664.txt", PR_RDWR | PR_CREATE_FILE, 0664); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0660.txt", PR_RDWR | PR_CREATE_FILE, 0660); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0666.txt", PR_RDWR | PR_CREATE_FILE, 0666); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + fd = PR_OpenFile("tfil0640.txt", PR_RDWR | PR_CREATE_FILE, 0640); + if (NULL == fd) { + fprintf(stderr, "PR_OpenFile failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + PR_Write(fd, buf, nbytes); + PR_Close(fd); + + PR_Cleanup(); + return 0; +} diff --git a/nsprpub/pr/tests/parent.c b/nsprpub/pr/tests/parent.c new file mode 100644 index 0000000000..f43ce0c3f0 --- /dev/null +++ b/nsprpub/pr/tests/parent.c @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** file: parent.c +** description: test the process machinery +*/ + +#include "prmem.h" +#include "prprf.h" +#include "prinit.h" +#include "prproces.h" +#include "prinrval.h" + +typedef struct Child +{ + const char *name; + char **argv; + PRProcess *process; + PRProcessAttr *attr; +} Child; + +/* for the default test 'cvar -c 2000' */ +static char *default_argv[] = {"cvar", "-c", "2000", NULL}; + +static void PrintUsage(void) +{ + PR_fprintf(PR_GetSpecialFD(PR_StandardError), + "Usage: parent [-d] child [options]\n"); +} + +int main(int argc, char **argv) +{ + PRStatus rv; + PRInt32 test_status = 1; + PRIntervalTime t_start, t_elapsed; + PRFileDesc *debug = NULL; + Child *child = PR_NEWZAP(Child); + + if (1 == argc) + { + /* no command-line arguments: run the default test */ + child->argv = default_argv; + } + else + { + argv += 1; /* don't care about our program name */ + while (*argv != NULL && argv[0][0] == '-') + { + if (argv[0][1] == 'd') { + debug = PR_GetSpecialFD(PR_StandardError); + } + else + { + PrintUsage(); + return 2; /* not sufficient */ + } + argv += 1; + } + child->argv = argv; + } + + if (NULL == *child->argv) + { + PrintUsage(); + return 2; + } + + child->name = *child->argv; + if (NULL != debug) { + PR_fprintf(debug, "Forking %s\n", child->name); + } + + child->attr = PR_NewProcessAttr(); + PR_ProcessAttrSetStdioRedirect( + child->attr, PR_StandardOutput, + PR_GetSpecialFD(PR_StandardOutput)); + PR_ProcessAttrSetStdioRedirect( + child->attr, PR_StandardError, + PR_GetSpecialFD(PR_StandardError)); + + t_start = PR_IntervalNow(); + child->process = PR_CreateProcess( + 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) + { + PR_fprintf( + debug, "Child was %sforked\n", + (0 == test_status) ? "" : "NOT "); + if (0 == test_status) + PR_fprintf( + debug, "PR_CreateProcess took %lu microseconds\n", + PR_IntervalToMicroseconds(t_elapsed)); + } + + if (0 == test_status) + { + if (NULL != debug) { + PR_fprintf(debug, "Waiting for child to exit\n"); + } + rv = PR_WaitProcess(child->process, &test_status); + if (PR_SUCCESS == rv) + { + if (NULL != debug) + PR_fprintf( + debug, "Child exited %s\n", + (0 == test_status) ? "successfully" : "with error"); + } + else + { + test_status = 1; + if (NULL != debug) { + PR_fprintf(debug, "PR_WaitProcess failed\n"); + } + } + } + PR_DELETE(child); + PR_Cleanup(); + return test_status; + +} /* main */ + +/* parent.c */ + diff --git a/nsprpub/pr/tests/parsetm.c b/nsprpub/pr/tests/parsetm.c new file mode 100644 index 0000000000..0e25ffe68d --- /dev/null +++ b/nsprpub/pr/tests/parsetm.c @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This test program should eventually become a full-blown test for + * PR_ParseTimeString. Right now it just verifies that PR_ParseTimeString + * doesn't crash on an out-of-range time string (bug 480740). + */ + +#include "prtime.h" + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRBool debug_mode = PR_TRUE; + +static char *dayOfWeek[] = +{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" }; +static char *month[] = +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" +}; + +static void PrintExplodedTime(const PRExplodedTime *et) { + PRInt32 totalOffset; + PRInt32 hourOffset, minOffset; + const char *sign; + + /* Print day of the week, month, day, hour, minute, and second */ + if (debug_mode) printf("%s %s %ld %02ld:%02ld:%02ld ", + dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday, + et->tm_hour, et->tm_min, et->tm_sec); + + /* Print time zone */ + totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset; + if (totalOffset == 0) { + if (debug_mode) { + printf("UTC "); + } + } else { + sign = "+"; + if (totalOffset < 0) { + totalOffset = -totalOffset; + sign = "-"; + } + hourOffset = totalOffset / 3600; + minOffset = (totalOffset % 3600) / 60; + if (debug_mode) { + printf("%s%02ld%02ld ", sign, hourOffset, minOffset); + } + } + + /* Print year */ + if (debug_mode) { + printf("%hd", et->tm_year); + } +} + +int main(int argc, char **argv) +{ + PRTime ct; + PRExplodedTime et; + PRStatus rv; + char *sp1 = "Sat, 1 Jan 3001 00:00:00"; /* no time zone */ + char *sp2 = "Fri, 31 Dec 3000 23:59:60"; /* no time zone, not normalized */ + +#if _MSC_VER >= 1400 && !defined(WINCE) + /* Run this test in the US Pacific Time timezone. */ + _putenv_s("TZ", "PST8PDT"); + _tzset(); +#endif + + rv = PR_ParseTimeString(sp1, PR_FALSE, &ct); + printf("rv = %d\n", rv); + PR_ExplodeTime(ct, PR_GMTParameters, &et); + PrintExplodedTime(&et); + printf("\n"); + + rv = PR_ParseTimeString(sp2, PR_FALSE, &ct); + printf("rv = %d\n", rv); + PR_ExplodeTime(ct, PR_GMTParameters, &et); + PrintExplodedTime(&et); + printf("\n"); + + return 0; +} diff --git a/nsprpub/pr/tests/peek.c b/nsprpub/pr/tests/peek.c new file mode 100644 index 0000000000..4dce8299dd --- /dev/null +++ b/nsprpub/pr/tests/peek.c @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A test case for the PR_MSG_PEEK flag of PR_Recv(). + * + * Test both blocking and non-blocking sockets. + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define BUFFER_SIZE 1024 + +static int iterations = 10; + +/* + * In iteration i, recv_amount[i] is the number of bytes we + * wish to receive, and send_amount[i] is the number of bytes + * we actually send. Therefore, the number of elements in the + * recv_amount or send_amount array should equal to 'iterations'. + * For this test to pass we need to ensure that + * recv_amount[i] <= BUFFER_SIZE, + * send_amount[i] <= BUFFER_SIZE, + * send_amount[i] <= recv_amount[i]. + */ +static PRInt32 recv_amount[10] = { + 16, 128, 256, 1024, 512, 512, 128, 256, 32, 32 +}; +static PRInt32 send_amount[10] = { + 16, 64, 128, 1024, 512, 256, 128, 64, 16, 32 +}; + +/* Blocking I/O */ +static void ServerB(void *arg) +{ + PRFileDesc *listenSock = (PRFileDesc *) arg; + PRFileDesc *sock; + char buf[BUFFER_SIZE]; + PRInt32 nbytes; + int i; + int j; + + sock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (NULL == sock) { + fprintf(stderr, "PR_Accept failed\n"); + exit(1); + } + + for (i = 0; i < iterations; i++) { + memset(buf, 0, sizeof(buf)); + nbytes = PR_Recv(sock, buf, recv_amount[i], + PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT); + if (-1 == nbytes) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes); + exit(1); + } + for (j = 0; j < nbytes; j++) { + if (buf[j] != 2*i) { + fprintf(stderr, "byte %d should be %d but is %d\n", + j, 2*i, buf[j]); + exit(1); + } + } + fprintf(stderr, "server: peeked expected data\n"); + + memset(buf, 0, sizeof(buf)); + nbytes = PR_Recv(sock, buf, recv_amount[i], + PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT); + if (-1 == nbytes) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes); + exit(1); + } + for (j = 0; j < nbytes; j++) { + if (buf[j] != 2*i) { + fprintf(stderr, "byte %d should be %d but is %d\n", + j, 2*i, buf[j]); + exit(1); + } + } + fprintf(stderr, "server: peeked expected data\n"); + + memset(buf, 0, sizeof(buf)); + nbytes = PR_Recv(sock, buf, recv_amount[i], + 0, PR_INTERVAL_NO_TIMEOUT); + if (-1 == nbytes) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes); + exit(1); + } + for (j = 0; j < nbytes; j++) { + if (buf[j] != 2*i) { + fprintf(stderr, "byte %d should be %d but is %d\n", + j, 2*i, buf[j]); + exit(1); + } + } + fprintf(stderr, "server: received expected data\n"); + + PR_Sleep(PR_SecondsToInterval(1)); + memset(buf, 2*i+1, send_amount[i]); + nbytes = PR_Send(sock, buf, send_amount[i], + 0, PR_INTERVAL_NO_TIMEOUT); + if (-1 == nbytes) { + fprintf(stderr, "PR_Send failed\n"); + exit(1); + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Send returned %d, absurd!\n", nbytes); + exit(1); + } + } + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +/* Non-blocking I/O */ +static void ClientNB(void *arg) +{ + PRFileDesc *sock; + PRSocketOptionData opt; + PRUint16 port = (PRUint16) arg; + PRNetAddr addr; + char buf[BUFFER_SIZE]; + PRPollDesc pd; + PRInt32 npds; + PRInt32 nbytes; + int i; + int j; + + sock = PR_OpenTCPSocket(PR_AF_INET6); + if (NULL == sock) { + fprintf(stderr, "PR_OpenTCPSocket failed\n"); + exit(1); + } + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_TRUE; + if (PR_SetSocketOption(sock, &opt) == PR_FAILURE) { + fprintf(stderr, "PR_SetSocketOption failed\n"); + exit(1); + } + memset(&addr, 0, sizeof(addr)); + if (PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, port, &addr) + == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + if (PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + if (PR_GetError() != PR_IN_PROGRESS_ERROR) { + fprintf(stderr, "PR_Connect failed\n"); + exit(1); + } + pd.fd = sock; + pd.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT; + npds = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (-1 == npds) { + fprintf(stderr, "PR_Poll failed\n"); + exit(1); + } + if (1 != npds) { + fprintf(stderr, "PR_Poll returned %d, absurd!\n", npds); + exit(1); + } + if (PR_GetConnectStatus(&pd) == PR_FAILURE) { + fprintf(stderr, "PR_GetConnectStatus failed\n"); + exit(1); + } + } + + for (i = 0; i < iterations; i++) { + PR_Sleep(PR_SecondsToInterval(1)); + memset(buf, 2*i, send_amount[i]); + while ((nbytes = PR_Send(sock, buf, send_amount[i], + 0, PR_INTERVAL_NO_TIMEOUT)) == -1) { + if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { + fprintf(stderr, "PR_Send failed\n"); + exit(1); + } + pd.fd = sock; + pd.in_flags = PR_POLL_WRITE; + npds = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (-1 == npds) { + fprintf(stderr, "PR_Poll failed\n"); + exit(1); + } + if (1 != npds) { + fprintf(stderr, "PR_Poll returned %d, absurd!\n", npds); + exit(1); + } + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Send returned %d, absurd!\n", nbytes); + exit(1); + } + + memset(buf, 0, sizeof(buf)); + while ((nbytes = PR_Recv(sock, buf, recv_amount[i], + PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT)) == -1) { + if (PR_GetError() != PR_WOULD_BLOCK_ERROR) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + pd.fd = sock; + pd.in_flags = PR_POLL_READ; + npds = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (-1 == npds) { + fprintf(stderr, "PR_Poll failed\n"); + exit(1); + } + if (1 != npds) { + fprintf(stderr, "PR_Poll returned %d, absurd!\n", npds); + exit(1); + } + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes); + exit(1); + } + for (j = 0; j < nbytes; j++) { + if (buf[j] != 2*i+1) { + fprintf(stderr, "byte %d should be %d but is %d\n", + j, 2*i+1, buf[j]); + exit(1); + } + } + fprintf(stderr, "client: peeked expected data\n"); + + memset(buf, 0, sizeof(buf)); + nbytes = PR_Recv(sock, buf, recv_amount[i], + PR_MSG_PEEK, PR_INTERVAL_NO_TIMEOUT); + if (-1 == nbytes) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes); + exit(1); + } + for (j = 0; j < nbytes; j++) { + if (buf[j] != 2*i+1) { + fprintf(stderr, "byte %d should be %d but is %d\n", + j, 2*i+1, buf[j]); + exit(1); + } + } + fprintf(stderr, "client: peeked expected data\n"); + + memset(buf, 0, sizeof(buf)); + nbytes = PR_Recv(sock, buf, recv_amount[i], + 0, PR_INTERVAL_NO_TIMEOUT); + if (-1 == nbytes) { + fprintf(stderr, "PR_Recv failed\n"); + exit(1); + } + if (send_amount[i] != nbytes) { + fprintf(stderr, "PR_Recv returned %d, absurd!\n", nbytes); + exit(1); + } + for (j = 0; j < nbytes; j++) { + if (buf[j] != 2*i+1) { + fprintf(stderr, "byte %d should be %d but is %d\n", + j, 2*i+1, buf[j]); + exit(1); + } + } + fprintf(stderr, "client: received expected data\n"); + } + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +static void +RunTest(PRThreadScope scope, PRFileDesc *listenSock, PRUint16 port) +{ + PRThread *server, *client; + + server = PR_CreateThread(PR_USER_THREAD, ServerB, listenSock, + PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0); + if (NULL == server) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + client = PR_CreateThread( + PR_USER_THREAD, ClientNB, (void *) port, + PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0); + if (NULL == client) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + + if (PR_JoinThread(server) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + if (PR_JoinThread(client) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock; + PRNetAddr addr; + PRUint16 port; + + listenSock = PR_OpenTCPSocket(PR_AF_INET6); + if (NULL == listenSock) { + fprintf(stderr, "PR_OpenTCPSocket failed\n"); + exit(1); + } + memset(&addr, 0, sizeof(addr)); + if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + if (PR_Bind(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + port = PR_ntohs(addr.ipv6.port); + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + + fprintf(stderr, "Running the test with local threads\n"); + RunTest(PR_LOCAL_THREAD, listenSock, port); + fprintf(stderr, "Running the test with global threads\n"); + RunTest(PR_GLOBAL_THREAD, listenSock, port); + + if (PR_Close(listenSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/perf.c b/nsprpub/pr/tests/perf.c new file mode 100644 index 0000000000..cd8ac1439b --- /dev/null +++ b/nsprpub/pr/tests/perf.c @@ -0,0 +1,447 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int _debug_on = 0; +#define DPRINTF(arg) if (_debug_on) printf arg + +#include "obsolete/prsem.h" + +PRLock *lock; +PRMonitor *mon; +PRMonitor *mon2; + +#define DEFAULT_COUNT 1000 + +PRInt32 count; + +static void nop(int a, int b, int c) +{ +} + +static void LocalProcedureCall(void) +{ + PRInt32 i; + + for (i = 0; i < count; i++) { + nop(i, i, 5); + } +} + +static void DLLProcedureCall(void) +{ + PRInt32 i; + PRThreadState state; + PRThread *self = PR_GetCurrentThread(); + + for (i = 0; i < count; i++) { + state = PR_GetThreadState(self); + } +} + +static void Now(void) +{ + PRInt32 i; + PRTime time; + + for (i = 0; i < count; i++) { + time = PR_Now(); + } +} + +static void Interval(void) +{ + PRInt32 i; + PRIntervalTime time; + + for (i = 0; i < count; i++) { + time = PR_IntervalNow(); + } +} + +static void IdleLock(void) +{ + PRInt32 i; + + for (i = 0; i < count; i++) { + PR_Lock(lock); + PR_Unlock(lock); + } +} + +static void IdleMonitor(void) +{ + PRInt32 i; + + for (i = 0; i < count; i++) { + PR_EnterMonitor(mon); + PR_ExitMonitor(mon); + } +} + +static void IdleCMonitor(void) +{ + PRInt32 i; + + for (i = 0; i < count; i++) { + PR_CEnterMonitor((void*)7); + PR_CExitMonitor((void*)7); + } +} + +/************************************************************************/ + +static void PR_CALLBACK dull(void *arg) +{ +} + +static void CDThread(void) +{ + PRInt32 i; + int num_threads = count; + + /* + * Cannot create too many threads + */ + if (num_threads > 1000) { + num_threads = 1000; + } + + for (i = 0; i < num_threads; i++) { + PRThread *t = PR_CreateThread(PR_USER_THREAD, + dull, 0, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (NULL == t) { + fprintf(stderr, "CDThread: cannot create thread %3d\n", i); + } else { + DPRINTF(("CDThread: created thread %3d \n",i)); + } + PR_Sleep(0); + } +} + +static int alive; +static int cxq; + +static void PR_CALLBACK CXReader(void *arg) +{ + PRInt32 i, n; + + PR_EnterMonitor(mon); + n = count / 2; + for (i = 0; i < n; i++) { + while (cxq == 0) { + DPRINTF(("CXReader: thread = 0x%lx waiting\n", + PR_GetCurrentThread())); + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + --cxq; + PR_Notify(mon); + } + PR_ExitMonitor(mon); + + PR_EnterMonitor(mon2); + --alive; + PR_Notify(mon2); + PR_ExitMonitor(mon2); + DPRINTF(("CXReader: thread = 0x%lx exiting\n", PR_GetCurrentThread())); +} + +static void PR_CALLBACK CXWriter(void *arg) +{ + PRInt32 i, n; + + PR_EnterMonitor(mon); + n = count / 2; + for (i = 0; i < n; i++) { + while (cxq == 1) { + DPRINTF(("CXWriter: thread = 0x%lx waiting\n", + PR_GetCurrentThread())); + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + ++cxq; + PR_Notify(mon); + } + PR_ExitMonitor(mon); + + PR_EnterMonitor(mon2); + --alive; + PR_Notify(mon2); + PR_ExitMonitor(mon2); + DPRINTF(("CXWriter: thread = 0x%lx exiting\n", PR_GetCurrentThread())); +} + +static void ContextSwitch(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *t1, *t2; + + PR_EnterMonitor(mon2); + alive = 2; + cxq = 0; + + t1 = PR_CreateThread(PR_USER_THREAD, + CXReader, 0, + PR_PRIORITY_NORMAL, + scope1, + PR_UNJOINABLE_THREAD, + 0); + if (NULL == t1) { + fprintf(stderr, "ContextSwitch: cannot create thread\n"); + } else { + DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n", + (scope1 == PR_GLOBAL_THREAD ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), + t1)); + } + t2 = PR_CreateThread(PR_USER_THREAD, + CXWriter, 0, + PR_PRIORITY_NORMAL, + scope2, + PR_UNJOINABLE_THREAD, + 0); + if (NULL == t2) { + fprintf(stderr, "ContextSwitch: cannot create thread\n"); + } else { + DPRINTF(("ContextSwitch: created %s thread = 0x%lx\n", + (scope2 == PR_GLOBAL_THREAD ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), + t2)); + } + + /* Wait for both of the threads to exit */ + while (alive) { + PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(mon2); +} + +static void ContextSwitchUU(void) +{ + ContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD); +} + +static void ContextSwitchUK(void) +{ + ContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); +} + +static void ContextSwitchKU(void) +{ + ContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); +} + +static void ContextSwitchKK(void) +{ + ContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); +} + +/************************************************************************/ + +static void PR_CALLBACK SemaThread(void *argSema) +{ + PRSemaphore **sem = (PRSemaphore **)argSema; + PRInt32 i, n; + + n = count / 2; + for (i = 0; i < n; i++) { + DPRINTF(("SemaThread: thread = 0x%lx waiting on sem = 0x%lx\n", + PR_GetCurrentThread(), sem[0])); + PR_WaitSem(sem[0]); + DPRINTF(("SemaThread: thread = 0x%lx posting on sem = 0x%lx\n", + PR_GetCurrentThread(), sem[1])); + PR_PostSem(sem[1]); + } + + PR_EnterMonitor(mon2); + --alive; + PR_Notify(mon2); + PR_ExitMonitor(mon2); + DPRINTF(("SemaThread: thread = 0x%lx exiting\n", PR_GetCurrentThread())); +} + +static PRSemaphore *sem_set1[2]; +static PRSemaphore *sem_set2[2]; + +static void SemaContextSwitch(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *t1, *t2; + sem_set1[0] = PR_NewSem(1); + sem_set1[1] = PR_NewSem(0); + sem_set2[0] = sem_set1[1]; + sem_set2[1] = sem_set1[0]; + + PR_EnterMonitor(mon2); + alive = 2; + cxq = 0; + + t1 = PR_CreateThread(PR_USER_THREAD, + SemaThread, + sem_set1, + PR_PRIORITY_NORMAL, + scope1, + PR_UNJOINABLE_THREAD, + 0); + if (NULL == t1) { + fprintf(stderr, "SemaContextSwitch: cannot create thread\n"); + } else { + DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n", + (scope1 == PR_GLOBAL_THREAD ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), + t1)); + } + t2 = PR_CreateThread(PR_USER_THREAD, + SemaThread, + sem_set2, + PR_PRIORITY_NORMAL, + scope2, + PR_UNJOINABLE_THREAD, + 0); + if (NULL == t2) { + fprintf(stderr, "SemaContextSwitch: cannot create thread\n"); + } else { + DPRINTF(("SemaContextSwitch: created %s thread = 0x%lx\n", + (scope2 == PR_GLOBAL_THREAD ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD"), + t2)); + } + + /* Wait for both of the threads to exit */ + while (alive) { + PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(mon2); + + PR_DestroySem(sem_set1[0]); + PR_DestroySem(sem_set1[1]); +} + +static void SemaContextSwitchUU(void) +{ + SemaContextSwitch(PR_LOCAL_THREAD, PR_LOCAL_THREAD); +} + +static void SemaContextSwitchUK(void) +{ + SemaContextSwitch(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); +} + +static void SemaContextSwitchKU(void) +{ + SemaContextSwitch(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); +} + +static void SemaContextSwitchKK(void) +{ + SemaContextSwitch(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); +} + + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow() - start; + d = (double)PR_IntervalToMicroseconds(stop); + + printf("%40s: %6.2f usec\n", msg, d / count); +} + +int main(int argc, char **argv) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + case 'c': /* loop count */ + count = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (0 == count) { + count = DEFAULT_COUNT; + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_BlockClockInterrupts(); + PR_UnblockClockInterrupts(); + PR_STDIO_INIT(); + + lock = PR_NewLock(); + mon = PR_NewMonitor(); + mon2 = PR_NewMonitor(); + + Measure(LocalProcedureCall, "local procedure call overhead"); + Measure(DLLProcedureCall, "DLL procedure call overhead"); + Measure(Now, "current calendar time"); + Measure(Interval, "interval time"); + Measure(IdleLock, "idle lock lock/unlock pair"); + Measure(IdleMonitor, "idle monitor entry/exit pair"); + Measure(IdleCMonitor, "idle cache monitor entry/exit pair"); + Measure(CDThread, "create/destroy thread pair"); + Measure(ContextSwitchUU, "context switch - user/user"); + Measure(ContextSwitchUK, "context switch - user/kernel"); + Measure(ContextSwitchKU, "context switch - kernel/user"); + Measure(ContextSwitchKK, "context switch - kernel/kernel"); + Measure(SemaContextSwitchUU, "sema context switch - user/user"); + Measure(SemaContextSwitchUK, "sema context switch - user/kernel"); + Measure(SemaContextSwitchKU, "sema context switch - kernel/user"); + Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel"); + + printf("--------------\n"); + printf("Adding 7 additional CPUs\n"); + + PR_SetConcurrency(8); + printf("--------------\n"); + + Measure(LocalProcedureCall, "local procedure call overhead"); + Measure(DLLProcedureCall, "DLL procedure call overhead"); + Measure(Now, "current calendar time"); + Measure(Interval, "interval time"); + Measure(IdleLock, "idle lock lock/unlock pair"); + Measure(IdleMonitor, "idle monitor entry/exit pair"); + Measure(IdleCMonitor, "idle cache monitor entry/exit pair"); + Measure(CDThread, "create/destroy thread pair"); + Measure(ContextSwitchUU, "context switch - user/user"); + Measure(ContextSwitchUK, "context switch - user/kernel"); + Measure(ContextSwitchKU, "context switch - kernel/user"); + Measure(ContextSwitchKK, "context switch - kernel/kernel"); + Measure(SemaContextSwitchUU, "sema context switch - user/user"); + Measure(SemaContextSwitchUK, "sema context switch - user/kernel"); + Measure(SemaContextSwitchKU, "sema context switch - kernel/user"); + Measure(SemaContextSwitchKK, "sema context switch - kernel/kernel"); + + PR_DestroyLock(lock); + PR_DestroyMonitor(mon); + PR_DestroyMonitor(mon2); + + PR_Cleanup(); + return 0; +} diff --git a/nsprpub/pr/tests/pipeping.c b/nsprpub/pr/tests/pipeping.c new file mode 100644 index 0000000000..26b63c659d --- /dev/null +++ b/nsprpub/pr/tests/pipeping.c @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: pipeping.c + * + * Description: + * This test runs in conjunction with the pipepong test. + * This test creates two pipes and redirects the stdin and + * stdout of the pipepong test to the pipes. Then this + * test writes "ping" to the pipepong test and the pipepong + * test writes "pong" back. To run this pair of tests, + * just invoke pipeping. + * + * Tested areas: process creation, pipes, file descriptor + * inheritance, standard I/O redirection. + */ + +#include "prerror.h" +#include "prio.h" +#include "prproces.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef XP_OS2 +static char *child_argv[] = { "pipepong.exe", NULL }; +#else +static char *child_argv[] = { "pipepong", NULL }; +#endif + +#define NUM_ITERATIONS 10 + +int main(int argc, char **argv) +{ + PRFileDesc *in_pipe[2]; + PRFileDesc *out_pipe[2]; + PRStatus status; + PRProcess *process; + PRProcessAttr *attr; + char buf[1024]; + PRInt32 nBytes; + PRInt32 exitCode; + int idx; + + status = PR_CreatePipe(&in_pipe[0], &in_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + status = PR_CreatePipe(&out_pipe[0], &out_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + + status = PR_SetFDInheritable(in_pipe[0], PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + status = PR_SetFDInheritable(in_pipe[1], PR_TRUE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + status = PR_SetFDInheritable(out_pipe[0], PR_TRUE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + status = PR_SetFDInheritable(out_pipe[1], PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + + attr = PR_NewProcessAttr(); + if (attr == NULL) { + fprintf(stderr, "PR_NewProcessAttr failed\n"); + exit(1); + } + + PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, out_pipe[0]); + PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, in_pipe[1]); + + process = PR_CreateProcess(child_argv[0], child_argv, NULL, attr); + if (process == NULL) { + fprintf(stderr, "PR_CreateProcess failed\n"); + exit(1); + } + PR_DestroyProcessAttr(attr); + status = PR_Close(out_pipe[0]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(in_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + strcpy(buf, "ping"); + printf("ping process: sending \"%s\"\n", buf); + nBytes = PR_Write(out_pipe[1], buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(in_pipe[0], buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("ping process: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "ping process: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "pong") != 0) { + fprintf(stderr, "ping process: expected \"pong\" but got \"%s\"\n", + buf); + exit(1); + } + } + + status = PR_Close(in_pipe[0]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(out_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_WaitProcess(process, &exitCode); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_WaitProcess failed\n"); + exit(1); + } + if (exitCode == 0) { + printf("PASS\n"); + return 0; + } else { + printf("FAIL\n"); + return 1; + } +} diff --git a/nsprpub/pr/tests/pipeping2.c b/nsprpub/pr/tests/pipeping2.c new file mode 100644 index 0000000000..ed96109172 --- /dev/null +++ b/nsprpub/pr/tests/pipeping2.c @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: pipeping2.c + * + * Description: + * This test runs in conjunction with the pipepong2 test. + * This test creates two pipes and passes two pipe fd's + * to the pipepong2 test. Then this test writes "ping" to + * to the pipepong2 test and the pipepong2 test writes "pong" + * back. To run this pair of tests, just invoke pipeping2. + * + * Tested areas: process creation, pipes, file descriptor + * inheritance. + */ + +#include "prerror.h" +#include "prio.h" +#include "prproces.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define NUM_ITERATIONS 10 + +static char *child_argv[] = { "pipepong2", NULL }; + +int main(int argc, char **argv) +{ + PRFileDesc *in_pipe[2]; + PRFileDesc *out_pipe[2]; + PRStatus status; + PRProcess *process; + PRProcessAttr *attr; + char buf[1024]; + PRInt32 nBytes; + PRInt32 exitCode; + int idx; + + status = PR_CreatePipe(&in_pipe[0], &in_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + status = PR_CreatePipe(&out_pipe[0], &out_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + + status = PR_SetFDInheritable(in_pipe[0], PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + status = PR_SetFDInheritable(in_pipe[1], PR_TRUE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + status = PR_SetFDInheritable(out_pipe[0], PR_TRUE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + status = PR_SetFDInheritable(out_pipe[1], PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + + attr = PR_NewProcessAttr(); + if (attr == NULL) { + fprintf(stderr, "PR_NewProcessAttr failed\n"); + exit(1); + } + + status = PR_ProcessAttrSetInheritableFD(attr, out_pipe[0], "PIPE_READ"); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_ProcessAttrSetInheritableFD failed\n"); + exit(1); + } + status = PR_ProcessAttrSetInheritableFD(attr, in_pipe[1], "PIPE_WRITE"); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_ProcessAttrSetInheritableFD failed\n"); + exit(1); + } + + process = PR_CreateProcess(child_argv[0], child_argv, NULL, attr); + if (process == NULL) { + fprintf(stderr, "PR_CreateProcess failed\n"); + exit(1); + } + PR_DestroyProcessAttr(attr); + status = PR_Close(out_pipe[0]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(in_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + strcpy(buf, "ping"); + printf("ping process: sending \"%s\"\n", buf); + nBytes = PR_Write(out_pipe[1], buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(in_pipe[0], buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + printf("ping process: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "ping process: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "pong") != 0) { + fprintf(stderr, "ping process: expected \"pong\" but got \"%s\"\n", + buf); + exit(1); + } + } + + status = PR_Close(in_pipe[0]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(out_pipe[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_WaitProcess(process, &exitCode); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_WaitProcess failed\n"); + exit(1); + } + if (exitCode == 0) { + printf("PASS\n"); + return 0; + } else { + printf("FAIL\n"); + return 1; + } +} diff --git a/nsprpub/pr/tests/pipepong.c b/nsprpub/pr/tests/pipepong.c new file mode 100644 index 0000000000..965aec32de --- /dev/null +++ b/nsprpub/pr/tests/pipepong.c @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: pipepong.c + * + * Description: + * This test runs in conjunction with the pipeping test. + * The pipeping test creates two pipes and redirects the + * stdin and stdout of this test to the pipes. Then the + * pipeping test writes "ping" to this test and this test + * writes "pong" back. Note that this test does not depend + * on NSPR at all. To run this pair of tests, just invoke + * pipeping. + * + * Tested areas: process creation, pipes, file descriptor + * inheritance, standard I/O redirection. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define NUM_ITERATIONS 10 + +int main(int argc, char **argv) +{ + char buf[1024]; + size_t nBytes; + int idx; + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + memset(buf, 0, sizeof(buf)); + nBytes = fread(buf, 1, 5, stdin); + fprintf(stderr, "pong process: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "pong process: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "ping") != 0) { + fprintf(stderr, "pong process: expected \"ping\" but got \"%s\"\n", + buf); + exit(1); + } + + strcpy(buf, "pong"); + fprintf(stderr, "pong process: sending \"%s\"\n", buf); + nBytes = fwrite(buf, 1, 5, stdout); + if (nBytes != 5) { + fprintf(stderr, "pong process: fwrite failed\n"); + exit(1); + } + fflush(stdout); + } + + return 0; +} diff --git a/nsprpub/pr/tests/pipepong2.c b/nsprpub/pr/tests/pipepong2.c new file mode 100644 index 0000000000..839f252a08 --- /dev/null +++ b/nsprpub/pr/tests/pipepong2.c @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: pipepong2.c + * + * Description: + * This test runs in conjunction with the pipeping2 test. + * The pipeping2 test creates two pipes and passes two + * pipe fd's to this test. Then the pipeping2 test writes + * "ping" to this test and this test writes "pong" back. + * To run this pair of tests, just invoke pipeping2. + * + * Tested areas: process creation, pipes, file descriptor + * inheritance. + */ + +#include "prerror.h" +#include "prio.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define NUM_ITERATIONS 10 + +int main(int argc, char **argv) +{ + PRFileDesc *pipe_read, *pipe_write; + PRStatus status; + char buf[1024]; + PRInt32 nBytes; + int idx; + + pipe_read = PR_GetInheritedFD("PIPE_READ"); + if (pipe_read == NULL) { + fprintf(stderr, "PR_GetInheritedFD failed\n"); + exit(1); + } + status = PR_SetFDInheritable(pipe_read, PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + pipe_write = PR_GetInheritedFD("PIPE_WRITE"); + if (pipe_write == NULL) { + fprintf(stderr, "PR_GetInheritedFD failed\n"); + exit(1); + } + status = PR_SetFDInheritable(pipe_write, PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(pipe_read, buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("pong process: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "pong process: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "ping") != 0) { + fprintf(stderr, "pong process: expected \"ping\" but got \"%s\"\n", + buf); + exit(1); + } + + strcpy(buf, "pong"); + printf("pong process: sending \"%s\"\n", buf); + nBytes = PR_Write(pipe_write, buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed\n"); + exit(1); + } + } + + status = PR_Close(pipe_read); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(pipe_write); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + return 0; +} diff --git a/nsprpub/pr/tests/pipeself.c b/nsprpub/pr/tests/pipeself.c new file mode 100644 index 0000000000..7066c58d1c --- /dev/null +++ b/nsprpub/pr/tests/pipeself.c @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: pipeself.c + * + * Description: + * This test has two threads communicating with each other using + * two unidirectional pipes. The primordial thread is the ping + * thread and the other thread is the pong thread. The ping + * thread writes "ping" to the pong thread and the pong thread + * writes "pong" back. + */ + +#include "prio.h" +#include "prerror.h" +#include "prthread.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define NUM_ITERATIONS 10 + +static PRFileDesc *ping_in, *ping_out; +static PRFileDesc *pong_in, *pong_out; + +static void PongThreadFunc(void *arg) +{ + char buf[1024]; + int idx; + PRInt32 nBytes; + PRStatus status; + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(pong_in, buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + printf("pong thread: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "pong thread: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "ping") != 0) { + fprintf(stderr, "pong thread: expected \"ping\" but got \"%s\"\n", + buf); + exit(1); + } + strcpy(buf, "pong"); + printf("pong thread: sending \"%s\"\n", buf); + nBytes = PR_Write(pong_out, buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } + } + + status = PR_Close(pong_in); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(pong_out); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + PRStatus status; + PRThread *pongThread; + char buf[1024]; + PRInt32 nBytes; + int idx; + + status = PR_CreatePipe(&ping_in, &pong_out); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + status = PR_CreatePipe(&pong_in, &ping_out); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + + pongThread = PR_CreateThread(PR_USER_THREAD, PongThreadFunc, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (pongThread == NULL) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + strcpy(buf, "ping"); + printf("ping thread: sending \"%s\"\n", buf); + nBytes = PR_Write(ping_out, buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(ping_in, buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + printf("ping thread: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "ping thread: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "pong") != 0) { + fprintf(stderr, "ping thread: expected \"pong\" but got \"%s\"\n", + buf); + exit(1); + } + } + + status = PR_Close(ping_in); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(ping_out); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_JoinThread(pongThread); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + +#if defined(XP_UNIX) + /* + * Test PR_Available for pipes + */ + status = PR_CreatePipe(&ping_in, &ping_out); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_CreatePipe failed\n"); + exit(1); + } + nBytes = PR_Write(ping_out, buf, 250); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } + nBytes = PR_Available(ping_in); + if (nBytes < 0) { + fprintf(stderr, "PR_Available failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } else if (nBytes != 250) { + fprintf(stderr, "PR_Available: expected 250 bytes but got %d bytes\n", + nBytes); + exit(1); + } + printf("PR_Available: expected %d, got %d bytes\n",250, nBytes); + /* read some data */ + nBytes = PR_Read(ping_in, buf, 7); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + /* check available data */ + nBytes = PR_Available(ping_in); + if (nBytes < 0) { + fprintf(stderr, "PR_Available failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } else if (nBytes != (250 - 7)) { + fprintf(stderr, "PR_Available: expected 243 bytes but got %d bytes\n", + nBytes); + exit(1); + } + printf("PR_Available: expected %d, got %d bytes\n",243, nBytes); + /* read all data */ + nBytes = PR_Read(ping_in, buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } else if (nBytes != 243) { + fprintf(stderr, "PR_Read failed: expected %d, got %d bytes\n", + 243, nBytes); + exit(1); + } + /* check available data */ + nBytes = PR_Available(ping_in); + if (nBytes < 0) { + fprintf(stderr, "PR_Available failed: (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } else if (nBytes != 0) { + fprintf(stderr, "PR_Available: expected 0 bytes but got %d bytes\n", + nBytes); + exit(1); + } + printf("PR_Available: expected %d, got %d bytes\n", 0, nBytes); + + status = PR_Close(ping_in); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_Close(ping_out); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +#endif /* XP_UNIX */ + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/poll_er.c b/nsprpub/pr/tests/poll_er.c new file mode 100644 index 0000000000..b3a751d83e --- /dev/null +++ b/nsprpub/pr/tests/poll_er.c @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: prpoll_err.c +** +** Description: This program tests PR_Poll with sockets. +** error reporting operation is tested +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "primpl.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +static void +ClientThreadFunc(void *arg) +{ + PRFileDesc *badFD = (PRFileDesc *) arg; + /* + * Make the fd invalid + */ +#if defined(XP_UNIX) + close(PR_FileDesc2NativeHandle(badFD)); +#elif defined(XP_OS2) + soclose(PR_FileDesc2NativeHandle(badFD)); +#elif defined(WIN32) || defined(WIN16) + closesocket(PR_FileDesc2NativeHandle(badFD)); +#else +#error "Unknown architecture" +#endif +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1, *listenSock2; + PRFileDesc *badFD; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + char buf[128]; + PRPollDesc pds0[10], pds1[10], *pds, *other_pds; + PRIntn npds; + PRInt32 retVal; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Poll with sockets.\n"); + printf("error reporting is tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + if (debug_mode) { + printf("%s", buf); + } + + /* Set up the poll descriptor array */ + pds = pds0; + other_pds = pds1; + memset(pds, 0, sizeof(pds)); + pds[0].fd = listenSock1; + pds[0].in_flags = PR_POLL_READ; + pds[1].fd = listenSock2; + pds[1].in_flags = PR_POLL_READ; + npds = 2; + + + /* Testing bad fd */ + if (debug_mode) { + printf("PR_Poll should detect a bad file descriptor\n"); + } + if ((badFD = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a TCP socket\n"); + goto exit_now; + } + + pds[2].fd = badFD; + pds[2].in_flags = PR_POLL_READ; + npds = 3; + + if (PR_CreateThread(PR_USER_THREAD, ClientThreadFunc, + badFD, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0) == NULL) { + fprintf(stderr, "cannot create thread\n"); + exit(1); + } + + retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT); + if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) { + fprintf(stderr, "Failed to detect the bad fd: " + "PR_Poll returns %d, out_flags is 0x%hx\n", + retVal, pds[2].out_flags); + failed_already=1; + goto exit_now; + } + if (debug_mode) { + printf("PR_Poll detected the bad fd. Test passed.\n\n"); + } + PR_Cleanup(); + goto exit_now; +exit_now: + if(failed_already) { + return 1; + } + else { + return 0; + } +} + diff --git a/nsprpub/pr/tests/poll_nm.c b/nsprpub/pr/tests/poll_nm.c new file mode 100644 index 0000000000..f356d141ac --- /dev/null +++ b/nsprpub/pr/tests/poll_nm.c @@ -0,0 +1,355 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: prpoll_norm.c +** +** Description: This program tests PR_Poll with sockets. +** Normal operation are tested +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prio.h" +#include "prlog.h" +#include "prprf.h" +#include "prnetdb.h" +#include "obsolete/probslet.h" + +#include "private/pprio.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +#define NUM_ITERATIONS 5 + +static void PR_CALLBACK +clientThreadFunc(void *arg) +{ + PRUintn port = (PRUintn) arg; + PRFileDesc *sock; + PRNetAddr addr; + char buf[128]; + int i; + PRStatus sts; + PRInt32 n; + + addr.inet.family = PR_AF_INET; + addr.inet.port = PR_htons((PRUint16)port); + addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + memset(buf, 0, sizeof(buf)); + PR_snprintf(buf, sizeof(buf), "%hu", port); + + for (i = 0; i < NUM_ITERATIONS; i++) { + sock = PR_NewTCPSocket(); + PR_ASSERT(sock != NULL); + + sts = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(sts == PR_SUCCESS); + + n = PR_Write(sock, buf, sizeof(buf)); + PR_ASSERT(n >= 0); + + sts = PR_Close(sock); + PR_ASSERT(sts == PR_SUCCESS); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1 = NULL, *listenSock2 = NULL; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + char buf[128]; + PRThread *clientThread; + PRPollDesc pds0[20], pds1[20], *pds, *other_pds; + PRIntn npds; + PRInt32 retVal; + PRIntn i, j; + PRSocketOptionData optval; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Poll with sockets.\n"); + printf("Normal operation are tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + memset(&addr, 0, sizeof(addr)); + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort1 = PR_ntohs(addr.inet.port); + optval.option = PR_SockOpt_Nonblocking; + optval.value.non_blocking = PR_TRUE; + PR_SetSocketOption(listenSock1, &optval); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort2 = PR_ntohs(addr.inet.port); + PR_SetSocketOption(listenSock2, &optval); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + if (debug_mode) { + printf("%s", buf); + } + + /* Set up the poll descriptor array */ + pds = pds0; + other_pds = pds1; + memset(pds, 0, sizeof(pds)); + pds[0].fd = listenSock1; + pds[0].in_flags = PR_POLL_READ; + pds[1].fd = listenSock2; + pds[1].in_flags = PR_POLL_READ; + /* Add some unused entries to test if they are ignored by PR_Poll() */ + memset(&pds[2], 0, sizeof(pds[2])); + memset(&pds[3], 0, sizeof(pds[3])); + memset(&pds[4], 0, sizeof(pds[4])); + npds = 5; + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort1, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + failed_already=1; + goto exit_now; + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort2, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + failed_already=1; + goto exit_now; + } + + if (debug_mode) { + printf("Two client threads are created. Each of them will\n"); + printf("send data to one of the two ports the server is listening on.\n"); + printf("The data they send is the port number. Each of them send\n"); + printf("the data five times, so you should see ten lines below,\n"); + printf("interleaved in an arbitrary order.\n"); + } + + /* two clients, three events per iteration: accept, read, close */ + i = 0; + while (i < 2 * 3 * NUM_ITERATIONS) { + PRPollDesc *tmp; + int nextIndex; + int nEvents = 0; + + retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(retVal != 0); /* no timeout */ + if (retVal == -1) { + fprintf(stderr, "PR_Poll failed\n"); + failed_already=1; + goto exit_now; + } + + nextIndex = 2; + /* the two listening sockets */ + for (j = 0; j < 2; j++) { + other_pds[j] = pds[j]; + PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0 + && (pds[j].out_flags & PR_POLL_EXCEPT) == 0); + if (pds[j].out_flags & PR_POLL_READ) { + PRFileDesc *sock; + + nEvents++; + sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT); + if (sock == NULL) { + fprintf(stderr, "PR_Accept() failed\n"); + failed_already=1; + goto exit_now; + } + other_pds[nextIndex].fd = sock; + other_pds[nextIndex].in_flags = PR_POLL_READ; + nextIndex++; + } else if (pds[j].out_flags & PR_POLL_ERR) { + fprintf(stderr, "PR_Poll() indicates that an fd has error\n"); + failed_already=1; + goto exit_now; + } else if (pds[j].out_flags & PR_POLL_NVAL) { + fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n", + PR_FileDesc2NativeHandle(pds[j].fd)); + failed_already=1; + goto exit_now; + } + } + + for (j = 2; j < npds; j++) { + if (NULL == pds[j].fd) { + /* + * Keep the unused entries in the poll descriptor array + * for testing purposes. + */ + other_pds[nextIndex] = pds[j]; + nextIndex++; + continue; + } + + PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0 + && (pds[j].out_flags & PR_POLL_EXCEPT) == 0); + if (pds[j].out_flags & PR_POLL_READ) { + PRInt32 nAvail; + PRInt32 nRead; + + nEvents++; + nAvail = PR_Available(pds[j].fd); + nRead = PR_Read(pds[j].fd, buf, sizeof(buf)); + PR_ASSERT(nAvail == nRead); + if (nRead == -1) { + fprintf(stderr, "PR_Read() failed\n"); + failed_already=1; + goto exit_now; + } else if (nRead == 0) { + PR_Close(pds[j].fd); + continue; + } else { + /* Just to be safe */ + buf[127] = '\0'; + if (debug_mode) { + printf("The server received \"%s\" from a client\n", buf); + } + } + } else if (pds[j].out_flags & PR_POLL_ERR) { + fprintf(stderr, "PR_Poll() indicates that an fd has error\n"); + failed_already=1; + goto exit_now; + } else if (pds[j].out_flags & PR_POLL_NVAL) { + fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n"); + failed_already=1; + goto exit_now; + } + other_pds[nextIndex] = pds[j]; + nextIndex++; + } + + PR_ASSERT(retVal == nEvents); + /* swap */ + tmp = pds; + pds = other_pds; + other_pds = tmp; + npds = nextIndex; + i += nEvents; + } + + if (debug_mode) { + printf("Tests passed\n"); + } + +exit_now: + + if (listenSock1) { + PR_Close(listenSock1); + } + if (listenSock2) { + PR_Close(listenSock2); + } + + PR_Cleanup(); + + if(failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/poll_to.c b/nsprpub/pr/tests/poll_to.c new file mode 100644 index 0000000000..91291bcb11 --- /dev/null +++ b/nsprpub/pr/tests/poll_to.c @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: prpoll_to.c +** +** Description: This program tests PR_Poll with sockets. +** Timeout operation is tested +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prio.h" +#include "prlog.h" +#include "prprf.h" +#include "prnetdb.h" + +#include "private/pprio.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1 = NULL, *listenSock2 = NULL; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + char buf[128]; + PRPollDesc pds0[10], pds1[10], *pds, *other_pds; + PRIntn npds; + PRInt32 retVal; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Poll with sockets.\n"); + printf("Timeout is tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + memset(&addr, 0, sizeof(addr)); + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + if (debug_mode) { + printf("%s", buf); + } + + /* Set up the poll descriptor array */ + pds = pds0; + other_pds = pds1; + memset(pds, 0, sizeof(pds)); + pds[0].fd = listenSock1; + pds[0].in_flags = PR_POLL_READ; + pds[1].fd = listenSock2; + pds[1].in_flags = PR_POLL_READ; + npds = 2; + + /* Testing timeout */ + if (debug_mode) { + printf("PR_Poll should time out in 5 seconds\n"); + } + retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5)); + if (retVal != 0) { + PR_snprintf(buf, sizeof(buf), + "PR_Poll should time out and return 0, but it returns %ld\n", + retVal); + fprintf(stderr, "%s", buf); + if (!debug_mode) { + failed_already=1; + } + goto exit_now; + } + if (debug_mode) { + printf("PR_Poll timed out. Test passed.\n\n"); + } + +exit_now: + + if (listenSock1) { + PR_Close(listenSock1); + } + if (listenSock2) { + PR_Close(listenSock2); + } + + PR_Cleanup(); + + if(failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/pollable.c b/nsprpub/pr/tests/pollable.c new file mode 100644 index 0000000000..7d0b51cac3 --- /dev/null +++ b/nsprpub/pr/tests/pollable.c @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A test for the pollable events. + * + * A number of threads are in a ring configuration, each waiting on + * a pollable event that is set by its upstream neighbor. + */ + +#include "prinit.h" +#include "prio.h" +#include "prthread.h" +#include "prerror.h" +#include "prmem.h" +#include "prlog.h" +#include "prprf.h" + +#include "plgetopt.h" + +#include <stdlib.h> + +#define DEFAULT_THREADS 10 +#define DEFAULT_LOOPS 100 + +PRIntn numThreads = DEFAULT_THREADS; +PRIntn numIterations = DEFAULT_LOOPS; +PRIntervalTime dally = PR_INTERVAL_NO_WAIT; +PRFileDesc *debug_out = NULL; +PRBool debug_mode = PR_FALSE; +PRBool verbosity = PR_FALSE; + +typedef struct ThreadData { + PRFileDesc *event; + int index; + struct ThreadData *next; +} ThreadData; + +void ThreadRoutine(void *arg) +{ + ThreadData *data = (ThreadData *) arg; + PRIntn i; + PRPollDesc pd; + PRInt32 rv; + + pd.fd = data->event; + pd.in_flags = PR_POLL_READ; + + for (i = 0; i < numIterations; i++) { + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (rv == -1) { + PR_fprintf(PR_STDERR, "PR_Poll failed\n"); + exit(1); + } + if (verbosity) { + PR_fprintf(debug_out, "thread %d awakened\n", data->index); + } + PR_ASSERT(rv != 0); + PR_ASSERT(pd.out_flags & PR_POLL_READ); + if (PR_WaitForPollableEvent(data->event) == PR_FAILURE) { + PR_fprintf(PR_STDERR, "consume event failed\n"); + exit(1); + } + if (dally != PR_INTERVAL_NO_WAIT) { + PR_Sleep(dally); + } + if (verbosity) { + PR_fprintf(debug_out, "thread %d posting event\n", data->index); + } + if (PR_SetPollableEvent(data->next->event) == PR_FAILURE) { + PR_fprintf(PR_STDERR, "post event failed\n"); + exit(1); + } + } +} + +static void Help(void) +{ + debug_out = PR_STDOUT; + + PR_fprintf( + debug_out, "Usage: pollable [-c n] [-t n] [-d] [-v] [-G] [-C n] [-D n]\n"); + PR_fprintf( + debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS); + PR_fprintf( + debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS); + PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n"); + PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n"); + PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n"); + PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n"); + PR_fprintf(debug_out, "-D n\tdally setting (msecs) (default: 0)\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + ThreadData selfData; + ThreadData *data; + PRThread **thread; + void *block; + PRIntn i; + PRIntervalTime timeStart, timeEnd; + PRPollDesc pd; + PRInt32 rv; + PRThreadScope thread_scope = PR_LOCAL_THREAD; + PRBool help = PR_FALSE; + PRUintn concurrency = 1; + PRUintn average; + PLOptStatus os; + PLOptState *opt; + + PR_STDIO_INIT(); + + opt = PL_CreateOptState(argc, argv, "hdvc:t:C:GD:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) { + case 'v': /* verbose mode */ + verbosity = PR_TRUE; + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'c': /* loop counter */ + numIterations = atoi(opt->value); + break; + case 't': /* thread limit */ + numThreads = atoi(opt->value); + break; + case 'C': /* Concurrency limit */ + concurrency = atoi(opt->value); + break; + case 'G': /* global threads only */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'D': /* dally */ + dally = PR_MillisecondsToInterval(atoi(opt->value)); + break; + case 'h': /* help message */ + Help(); + help = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (help) { + return 1; + } + + if (concurrency > 1) { + PR_SetConcurrency(concurrency); + } + + if (PR_TRUE == debug_mode) { + debug_out = PR_STDOUT; + PR_fprintf(debug_out, "Test parameters\n"); + PR_fprintf(debug_out, "\tThreads involved: %d\n", numThreads); + PR_fprintf(debug_out, "\tIteration limit: %d\n", numIterations); + PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency); + PR_fprintf(debug_out, "\tThread type: %s\n", + (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); + } + + /* + * Malloc a block of memory and divide it into data and thread. + */ + block = PR_MALLOC(numThreads * (sizeof(ThreadData) + sizeof(PRThread *))); + if (block == NULL) { + PR_fprintf(PR_STDERR, "cannot malloc, failed\n"); + exit(1); + } + data = (ThreadData *) block; + thread = (PRThread **) &data[numThreads]; + + /* Pollable event */ + selfData.event = PR_NewPollableEvent(); + if (selfData.event == NULL) { + PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + selfData.next = &data[0]; + for (i = 0; i < numThreads; i++) { + data[i].event = PR_NewPollableEvent(); + if (data[i].event == NULL) { + PR_fprintf(PR_STDERR, "cannot create event: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + data[i].index = i; + if (i != numThreads - 1) { + data[i].next = &data[i + 1]; + } else { + data[i].next = &selfData; + } + + thread[i] = PR_CreateThread(PR_USER_THREAD, + ThreadRoutine, &data[i], PR_PRIORITY_NORMAL, + thread_scope, PR_JOINABLE_THREAD, 0); + if (thread[i] == NULL) { + PR_fprintf(PR_STDERR, "cannot create thread\n"); + exit(1); + } + } + + timeStart = PR_IntervalNow(); + pd.fd = selfData.event; + pd.in_flags = PR_POLL_READ; + for (i = 0; i < numIterations; i++) { + if (dally != PR_INTERVAL_NO_WAIT) { + PR_Sleep(dally); + } + if (verbosity) { + PR_fprintf(debug_out, "main thread posting event\n"); + } + if (PR_SetPollableEvent(selfData.next->event) == PR_FAILURE) { + PR_fprintf(PR_STDERR, "set event failed\n"); + exit(1); + } + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + if (rv == -1) { + PR_fprintf(PR_STDERR, "wait failed\n"); + exit(1); + } + PR_ASSERT(rv != 0); + PR_ASSERT(pd.out_flags & PR_POLL_READ); + if (verbosity) { + PR_fprintf(debug_out, "main thread awakened\n"); + } + if (PR_WaitForPollableEvent(selfData.event) == PR_FAILURE) { + PR_fprintf(PR_STDERR, "consume event failed\n"); + exit(1); + } + } + timeEnd = PR_IntervalNow(); + + if (debug_mode) { + average = PR_IntervalToMicroseconds(timeEnd - timeStart) + / (numIterations * numThreads); + PR_fprintf(debug_out, "Average switch times %d usecs for %d threads\n", + average, numThreads); + } + + for (i = 0; i < numThreads; i++) { + if (PR_JoinThread(thread[i]) == PR_FAILURE) { + PR_fprintf(PR_STDERR, "join thread failed\n"); + exit(1); + } + PR_DestroyPollableEvent(data[i].event); + } + PR_DELETE(block); + PR_DestroyPollableEvent(selfData.event); + + PR_fprintf(PR_STDOUT, "PASSED\n"); + return 0; +} diff --git a/nsprpub/pr/tests/prfdbl.c b/nsprpub/pr/tests/prfdbl.c new file mode 100644 index 0000000000..3bb0650b2e --- /dev/null +++ b/nsprpub/pr/tests/prfdbl.c @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This is a simple test of the PR_fprintf() function for doubles. + */ + +#include "prprf.h" + +int main() +{ + double pi = 3.1415926; + double e = 2.71828; + double root2 = 1.414; + double zero = 0.0; + double nan = zero / zero; + + PR_fprintf(PR_STDOUT, "pi is %f.\n", pi); + PR_fprintf(PR_STDOUT, "e is %f.\n", e); + PR_fprintf(PR_STDOUT, "The square root of 2 is %f.\n", root2); + PR_fprintf(PR_STDOUT, "NaN is %f.\n", nan); + + PR_fprintf(PR_STDOUT, "pi is %301f.\n", pi); + PR_fprintf(PR_STDOUT, "e is %65416.123f.\n", e); + PR_fprintf(PR_STDOUT, "e is %0000000000000000000065416.123f.\n", e); + PR_fprintf(PR_STDOUT, "NaN is %1024.1f.\n", nan); + return 0; +} diff --git a/nsprpub/pr/tests/prftest.c b/nsprpub/pr/tests/prftest.c new file mode 100644 index 0000000000..091c990a79 --- /dev/null +++ b/nsprpub/pr/tests/prftest.c @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: prftest.c + * Description: + * This is a simple test of the PR_snprintf() function defined + * in prprf.c. + */ + +#include "prlong.h" +#include "prprf.h" + +#include <string.h> + +#define BUF_SIZE 128 + +int main(int argc, char **argv) +{ + PRInt16 i16; + PRIntn n; + PRInt32 i32; + PRInt64 i64; + char buf[BUF_SIZE]; + char answer[BUF_SIZE]; + int i, rv = 0; + + i16 = -1; + n = -1; + i32 = -1; + LL_I2L(i64, i32); + + PR_snprintf(buf, BUF_SIZE, "%hx %x %lx %llx", i16, n, i32, i64); + strcpy(answer, "ffff "); + for (i = PR_BYTES_PER_INT * 2; i; i--) { + strcat(answer, "f"); + } + strcat(answer, " ffffffff ffffffffffffffff"); + + if (!strcmp(buf, answer)) { + printf("PR_snprintf test 1 passed\n"); + } else { + printf("PR_snprintf test 1 failed\n"); + printf("Converted string is %s\n", buf); + printf("Should be %s\n", answer); + rv = 1; + } + + i16 = -32; + n = 30; + i32 = 64; + LL_I2L(i64, 333); + PR_snprintf(buf, BUF_SIZE, "%d %hd %lld %ld", n, i16, i64, i32); + if (!strcmp(buf, "30 -32 333 64")) { + printf("PR_snprintf test 2 passed\n"); + } else { + printf("PR_snprintf test 2 failed\n"); + printf("Converted string is %s\n", buf); + printf("Should be 30 -32 333 64\n"); + rv = 1; + } + + return rv; +} diff --git a/nsprpub/pr/tests/prftest1.c b/nsprpub/pr/tests/prftest1.c new file mode 100644 index 0000000000..5d6374a455 --- /dev/null +++ b/nsprpub/pr/tests/prftest1.c @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prftest1.c +** Description: +** This is a simple test of the PR_snprintf() function defined +** in prprf.c. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" +#include "prttools.h" + +#include "prinit.h" +#include "prlong.h" +#include "prprf.h" + +#include <string.h> + +#define BUF_SIZE 128 + +/*********************************************************************** +** PRIVATE FUNCTION: Test_Result +** DESCRIPTION: Used in conjunction with the regress tool, prints out the +** status of the test case. +** INPUTS: PASS/FAIL +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: Determine what the status is and print accordingly. +** +***********************************************************************/ + + +static void Test_Result (int result) +{ + if (result == PASS) { + printf ("PASS\n"); + } + else { + printf ("FAIL\n"); + } +} + +int main(int argc, char **argv) +{ + PRInt16 i16; + PRIntn n; + PRInt32 i32; + PRInt64 i64; + char buf[BUF_SIZE]; + char answer[BUF_SIZE]; + int i; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + PR_STDIO_INIT(); + + i16 = -1; + n = -1; + i32 = -1; + LL_I2L(i64, i32); + + PR_snprintf(buf, BUF_SIZE, "%hx %x %lx %llx", i16, n, i32, i64); + strcpy(answer, "ffff "); + for (i = PR_BYTES_PER_INT * 2; i; i--) { + strcat(answer, "f"); + } + strcat(answer, " ffffffff ffffffffffffffff"); + + if (!strcmp(buf, answer)) { + if (debug_mode) { + printf("PR_snprintf test 1 passed\n"); + } + else { + Test_Result (PASS); + } + } else { + if (debug_mode) { + printf("PR_snprintf test 1 failed\n"); + printf("Converted string is %s\n", buf); + printf("Should be %s\n", answer); + } + else { + Test_Result (FAIL); + } + } + + return 0; +} diff --git a/nsprpub/pr/tests/prftest2.c b/nsprpub/pr/tests/prftest2.c new file mode 100644 index 0000000000..6376fd2ffd --- /dev/null +++ b/nsprpub/pr/tests/prftest2.c @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: prftest2.c +** Description: +** This is a simple test of the PR_snprintf() function defined +** in prprf.c. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prlong.h" +#include "prinit.h" +#include "prprf.h" + +#include <string.h> + +#define BUF_SIZE 128 + +PRIntn failed_already=0; +PRIntn debug_mode; + +int main(int argc, char **argv) +{ + PRInt16 i16; + PRIntn n; + PRInt32 i32; + PRInt64 i64; + char buf[BUF_SIZE]; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + + PR_STDIO_INIT(); + i16 = -32; + n = 30; + i32 = 64; + LL_I2L(i64, 333); + PR_snprintf(buf, BUF_SIZE, "%d %hd %lld %ld", n, i16, i64, i32); + if (!strcmp(buf, "30 -32 333 64")) { + if (debug_mode) { + printf("PR_snprintf test 2 passed\n"); + } + } else { + if (debug_mode) { + printf("PR_snprintf test 2 failed\n"); + printf("Converted string is %s\n", buf); + printf("Should be 30 -32 333 64\n"); + } + else { + failed_already=1; + } + } + if(failed_already) + { + printf("FAILED\n"); + return 1; + } + else + { + printf("PASSED\n"); + return 0; + } +} diff --git a/nsprpub/pr/tests/prfz.c b/nsprpub/pr/tests/prfz.c new file mode 100644 index 0000000000..7179dbecdb --- /dev/null +++ b/nsprpub/pr/tests/prfz.c @@ -0,0 +1,85 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This is a simple test of the PR_fprintf() function for size_t formats. + */ + +#include "prprf.h" +#include <sys/types.h> +#include <limits.h> +#include <string.h> +#include <stdint.h> + +int +main(int argc, char **argv) +{ + char buffer[128]; + + size_t unsigned_small = 266; +#ifdef XP_UNIX + ssize_t signed_small_p = 943; + ssize_t signed_small_n = -1; +#endif + size_t unsigned_max = SIZE_MAX; + size_t unsigned_min = 0; +#ifdef XP_UNIX + ssize_t signed_max = SSIZE_MAX; +#endif + + printf("Test: unsigned small '%%zu' : "); + PR_snprintf(buffer, sizeof(buffer), "%zu", unsigned_small); + if (strncmp(buffer, "266", sizeof(buffer)) != 0) { + printf("Failed, got '%s'\n", buffer); + return -1; + } + printf("OK\n"); + +#ifdef XP_UNIX + printf("Test: signed small positive '%%zd' : "); + PR_snprintf(buffer, sizeof(buffer), "%zd", signed_small_p); + if (strncmp(buffer, "943", sizeof(buffer)) != 0) { + printf("Failed, got '%s'\n", buffer); + return -1; + } + printf("OK\n"); + + printf("Test: signed small negative '%%zd' : "); + PR_snprintf(buffer, sizeof(buffer), "%zd", signed_small_n); + if (strncmp(buffer, "-1", sizeof(buffer)) != 0) { + printf("Failed, got '%s'\n", buffer); + return -1; + } + printf("OK\n"); +#endif + + printf("Test: 0 '%%zu' : "); + PR_snprintf(buffer, sizeof(buffer), "%zu", unsigned_min); + if (strncmp(buffer, "0", sizeof(buffer)) != 0) { + printf("Failed, got '%s'\n", buffer); + return -1; + } + printf("OK\n"); + + printf("Test: SIZE_MAX '%%zx' : "); + PR_snprintf(buffer, sizeof(buffer), "%zx", unsigned_max); + if (strspn(buffer, "f") != sizeof(size_t) * 2) { + printf("Failed, got '%s'\n", buffer); + return -1; + } + printf("OK\n"); + +#ifdef XP_UNIX + printf("Test: SSIZE_MAX '%%zx' : "); + PR_snprintf(buffer, sizeof(buffer), "%zx", signed_max); + if (*buffer != '7' || + strspn(buffer + 1, "f") != sizeof(ssize_t) * 2 - 1) { + printf("Failed, got '%s'\n", buffer); + return -1; + } + printf("OK\n"); +#endif + + return 0; +} diff --git a/nsprpub/pr/tests/primblok.c b/nsprpub/pr/tests/primblok.c new file mode 100644 index 0000000000..1182d3ed73 --- /dev/null +++ b/nsprpub/pr/tests/primblok.c @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: primblok.c + * Purpose: testing whether the primordial thread can block in a + * native blocking function without affecting the correct + * functioning of NSPR I/O functions (Bugzilla bug #30746) + */ + +#if !defined(WINNT) + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("This test is not relevant on this platform\n"); + return 0; +} + +#else /* WINNT */ + +#include "nspr.h" + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define TEST_FILE_NAME "primblok.dat" + +/* use InterlockedExchange to update this variable */ +static LONG iothread_done; + +static void PR_CALLBACK IOThread(void *arg) +{ + PRFileDesc *fd; + char buf[32]; + PRInt32 nbytes; + + /* Give the primordial thread one second to block */ + Sleep(1000); + + /* + * See if our PR_Write call will hang when the primordial + * thread is blocking in a native blocking function. + */ + fd = PR_Open(TEST_FILE_NAME, PR_WRONLY|PR_CREATE_FILE, 0666); + if (NULL == fd) { + fprintf(stderr, "PR_Open failed\n"); + exit(1); + } + memset(buf, 0xaf, sizeof(buf)); + fprintf(stderr, "iothread: calling PR_Write\n"); + nbytes = PR_Write(fd, buf, sizeof(buf)); + fprintf(stderr, "iothread: PR_Write returned\n"); + if (nbytes != sizeof(buf)) { + fprintf(stderr, "PR_Write returned %d\n", nbytes); + exit(1); + } + if (PR_Close(fd) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (PR_Delete(TEST_FILE_NAME) == PR_FAILURE) { + fprintf(stderr, "PR_Delete failed\n"); + exit(1); + } + + /* Tell the main thread that we are done */ + InterlockedExchange(&iothread_done, 1); +} + +int main(int argc, char **argv) +{ + PRThread *iothread; + + /* Must be a global thread */ + iothread = PR_CreateThread( + PR_USER_THREAD, IOThread, NULL, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (iothread == NULL) { + fprintf(stderr, "cannot create thread\n"); + exit(1); + } + + /* + * Block in a native blocking function. + * Give iothread 5 seconds to finish its task. + */ + Sleep(5000); + + /* + * Is iothread done or is it hung? + * + * I'm actually only interested in reading the value + * of iothread_done. I'm using InterlockedExchange as + * a thread-safe way to read iothread_done. + */ + if (InterlockedExchange(&iothread_done, 1) == 0) { + fprintf(stderr, "iothread is hung\n"); + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (PR_JoinThread(iothread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + printf("PASSED\n"); + return 0; +} /* main */ + +#endif /* WINNT */ diff --git a/nsprpub/pr/tests/priotest.c b/nsprpub/pr/tests/priotest.c new file mode 100644 index 0000000000..1e2249de63 --- /dev/null +++ b/nsprpub/pr/tests/priotest.c @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: priotest.c + * Purpose: testing priorities + */ + +#include "prcmon.h" +#include "prinit.h" +#include "prinrval.h" +#include "prlock.h" +#include "prlog.h" +#include "prmon.h" +#include "prprf.h" +#include "prthread.h" +#include "prtypes.h" + +#include "plerror.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> + +#define DEFAULT_DURATION 5 + +static PRBool failed = PR_FALSE; +static PRIntervalTime oneSecond; +static PRFileDesc *debug_out = NULL; +static PRBool debug_mode = PR_FALSE; + +static PRUint32 PerSecond(PRIntervalTime timein) +{ + PRUint32 loop = 0; + while (((PRIntervalTime)(PR_IntervalNow()) - timein) < oneSecond) { + loop += 1; + } + return loop; +} /* PerSecond */ + +static void PR_CALLBACK Low(void *arg) +{ + PRUint32 t3 = 0, t2 = 0, t1 = 0, t0, *tn = (PRUint32*)arg; + while (1) + { + t0 = PerSecond(PR_IntervalNow()); + *tn = (t3 + 3 * t2 + 3 * t1 + t0) / 8; + t3 = t2; t2 = t1; t1 = t0; + } +} /* Low */ + +static void PR_CALLBACK High(void *arg) +{ + PRUint32 t3 = 0, t2 = 0, t1 = 0, t0, *tn = (PRUint32*)arg; + while (1) + { + PRIntervalTime timein = PR_IntervalNow(); + PR_Sleep(oneSecond >> 2); /* 0.25 seconds */ + t0 = PerSecond(timein); + *tn = (t3 + 3 * t2 + 3 * t1 + t0) / 8; + t3 = t2; t2 = t1; t1 = t0; + } +} /* High */ + +static void Help(void) +{ + PR_fprintf( + debug_out, "Usage: priotest [-d] [-c n]\n"); + PR_fprintf( + debug_out, "-c n\tduration of test in seconds (default: %d)\n", DEFAULT_DURATION); + PR_fprintf( + debug_out, "-d\tturn on debugging output (default: FALSE)\n"); +} /* Help */ + +static void RudimentaryTests(void) +{ + /* + ** Try some rudimentary tests like setting valid priority and + ** getting it back, or setting invalid priorities and getting + ** back a valid answer. + */ + PRThreadPriority priority; + PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_URGENT); + priority = PR_GetThreadPriority(PR_GetCurrentThread()); + failed = ((PR_TRUE == failed) || (PR_PRIORITY_URGENT != priority)) + ? PR_TRUE : PR_FALSE; + if (debug_mode && (PR_PRIORITY_URGENT != priority)) + { + PR_fprintf(debug_out, "PR_[S/G]etThreadPriority() failed\n"); + } + + + PR_SetThreadPriority( + PR_GetCurrentThread(), (PRThreadPriority)(PR_PRIORITY_FIRST - 1)); + priority = PR_GetThreadPriority(PR_GetCurrentThread()); + failed = ((PR_TRUE == failed) || (PR_PRIORITY_FIRST != priority)) + ? PR_TRUE : PR_FALSE; + if (debug_mode && (PR_PRIORITY_FIRST != priority)) + { + PR_fprintf(debug_out, "PR_SetThreadPriority(-1) failed\n"); + } + + PR_SetThreadPriority( + PR_GetCurrentThread(), (PRThreadPriority)(PR_PRIORITY_LAST + 1)); + priority = PR_GetThreadPriority(PR_GetCurrentThread()); + failed = ((PR_TRUE == failed) || (PR_PRIORITY_LAST != priority)) + ? PR_TRUE : PR_FALSE; + if (debug_mode && (PR_PRIORITY_LAST != priority)) + { + PR_fprintf(debug_out, "PR_SetThreadPriority(+1) failed\n"); + } + +} /* RudimentataryTests */ + +static void CreateThreads(PRUint32 *lowCount, PRUint32 *highCount) +{ + (void)PR_CreateThread( + PR_USER_THREAD, Low, lowCount, PR_PRIORITY_LOW, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + (void)PR_CreateThread( + PR_USER_THREAD, High, highCount, PR_PRIORITY_HIGH, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); +} /* CreateThreads */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + PRIntn duration = DEFAULT_DURATION; + PRUint32 totalCount, highCount = 0, lowCount = 0; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdc:"); + + debug_out = PR_STDOUT; + oneSecond = PR_SecondsToInterval(1); + + 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': /* test duration */ + duration = atoi(opt->value); + break; + case 'h': /* help message */ + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + PR_STDIO_INIT(); + + if (duration == 0) { + duration = DEFAULT_DURATION; + } + + RudimentaryTests(); + + printf("Priority test: running for %d seconds\n\n", duration); + + (void)PerSecond(PR_IntervalNow()); + totalCount = PerSecond(PR_IntervalNow()); + + PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_URGENT); + + if (debug_mode) + { + PR_fprintf(debug_out, + "The high priority thread should get approximately three\n"); + PR_fprintf( debug_out, + "times what the low priority thread manages. A maximum of \n"); + PR_fprintf( debug_out, "%d cycles are available.\n\n", totalCount); + } + + duration = (duration + 4) / 5; + CreateThreads(&lowCount, &highCount); + while (duration--) + { + PRIntn loop = 5; + while (loop--) { + PR_Sleep(oneSecond); + } + if (debug_mode) { + PR_fprintf(debug_out, "high : low :: %d : %d\n", highCount, lowCount); + } + } + + + PR_ProcessExit((failed) ? 1 : 0); + + PR_NOT_REACHED("You can't get here -- but you did!"); + return 1; /* or here */ + +} /* main */ + +/* priotest.c */ diff --git a/nsprpub/pr/tests/provider.c b/nsprpub/pr/tests/provider.c new file mode 100644 index 0000000000..3450b20aea --- /dev/null +++ b/nsprpub/pr/tests/provider.c @@ -0,0 +1,1412 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Notes: + * [1] lth. The call to Sleep() is a hack to get the test case to run + * on Windows 95. Without it, the test case fails with an error + * WSAECONNRESET following a recv() call. The error is caused by the + * server side thread termination without a shutdown() or closesocket() + * call. Windows docmunentation suggests that this is predicted + * behavior; that other platforms get away with it is ... serindipity. + * The test case should shutdown() or closesocket() before + * thread termination. I didn't have time to figure out where or how + * to do it. The Sleep() call inserts enough delay to allow the + * client side to recv() all his data before the server side thread + * terminates. Whew! ... + * + ** Modification History: + * 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. + * The debug mode will print all of the printfs associated with this test. + * The regress mode will be the default mode. Since the regress tool limits + * the output to a one line status:PASS or FAIL,all of the printf statements + * have been handled with an if (debug_mode) statement. + */ + +#include "prclist.h" +#include "prcvar.h" +#include "prerror.h" +#include "prinit.h" +#include "prinrval.h" +#include "prio.h" +#include "prlock.h" +#include "prlog.h" +#include "prtime.h" +#include "prmem.h" +#include "prnetdb.h" +#include "prprf.h" +#include "prthread.h" + +#include "pprio.h" +#include "primpl.h" + +#include "plstr.h" +#include "plerror.h" +#include "plgetopt.h" + +#include <stdlib.h> +#include <string.h> + +#if defined(XP_UNIX) +#include <math.h> +#endif + +/* +** This is the beginning of the test +*/ + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define RECV_FLAGS 0 +#define SEND_FLAGS 0 +#define BUFFER_SIZE 1024 +#define DEFAULT_BACKLOG 5 +#define DEFAULT_PORT 13000 PORT_INC_DO PORT_INC_3264 +#define DEFAULT_CLIENTS 1 +#define ALLOWED_IN_ACCEPT 1 +#define DEFAULT_CLIPPING 1000 +#define DEFAULT_WORKERS_MIN 1 +#define DEFAULT_WORKERS_MAX 1 +#define DEFAULT_SERVER "localhost" +#define DEFAULT_EXECUTION_TIME 10 +#define DEFAULT_CLIENT_TIMEOUT 4000 +#define DEFAULT_SERVER_TIMEOUT 4000 +#define DEFAULT_SERVER_PRIORITY PR_PRIORITY_HIGH + +typedef enum CSState_e {cs_init, cs_run, cs_stop, cs_exit} CSState_t; + +static void PR_CALLBACK Worker(void *arg); +typedef struct CSPool_s CSPool_t; +typedef struct CSWorker_s CSWorker_t; +typedef struct CSServer_s CSServer_t; +typedef enum Verbosity +{ + TEST_LOG_ALWAYS, + TEST_LOG_ERROR, + TEST_LOG_WARNING, + TEST_LOG_NOTICE, + TEST_LOG_INFO, + TEST_LOG_STATUS, + TEST_LOG_VERBOSE +} Verbosity; + +static enum { + thread_nspr, thread_pthread, thread_sproc, thread_win32 +} thread_provider; + +static PRInt32 domain = AF_INET; +static PRInt32 protocol = 6; /* TCP */ +static PRFileDesc *debug_out = NULL; +static PRBool debug_mode = PR_FALSE; +static PRBool pthread_stats = PR_FALSE; +static Verbosity verbosity = TEST_LOG_ALWAYS; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +struct CSWorker_s +{ + PRCList element; /* list of the server's workers */ + + PRThread *thread; /* this worker objects thread */ + CSServer_t *server; /* back pointer to server structure */ +}; + +struct CSPool_s +{ + PRCondVar *exiting; + PRCondVar *acceptComplete; + PRUint32 accepting, active, workers; +}; + +struct CSServer_s +{ + PRCList list; /* head of worker list */ + + PRLock *ml; + PRThread *thread; /* the main server thread */ + PRCondVar *stateChange; + + PRUint16 port; /* port we're listening on */ + PRUint32 backlog; /* size of our listener backlog */ + PRFileDesc *listener; /* the fd accepting connections */ + + CSPool_t pool; /* statistics on worker threads */ + CSState_t state; /* the server's state */ + struct /* controlling worker counts */ + { + PRUint32 minimum, maximum, accepting; + } workers; + + /* statistics */ + PRIntervalTime started, stopped; + PRUint32 operations, bytesTransferred; +}; + +typedef struct CSDescriptor_s +{ + PRInt32 size; /* size of transfer */ + char filename[60]; /* filename, null padded */ +} CSDescriptor_t; + +typedef struct CSClient_s +{ + PRLock *ml; + PRThread *thread; + PRCondVar *stateChange; + PRNetAddr serverAddress; + + CSState_t state; + + /* statistics */ + PRIntervalTime started, stopped; + PRUint32 operations, bytesTransferred; +} CSClient_t; + +#define TEST_LOG(l, p, a) \ + do { \ + if (debug_mode || (p <= verbosity)) printf a; \ + } while (0) + +PRLogModuleInfo *cltsrv_log_file = NULL; + +#define MY_ASSERT(_expr) \ + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) + +#define TEST_ASSERT(_expr) \ + ((_expr)?((void)0):_MY_Assert(# _expr,__FILE__,__LINE__)) + +static void _MY_Assert(const char *s, const char *file, PRIntn ln) +{ + PL_PrintError(NULL); + PR_Assert(s, file, ln); +} /* _MY_Assert */ + +static PRBool Aborted(PRStatus rv) +{ + return ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) ? + PR_TRUE : PR_FALSE; +} + +static void TimeOfDayMessage(const char *msg, PRThread* me) +{ + char buffer[100]; + PRExplodedTime tod; + PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tod); + (void)PR_FormatTime(buffer, sizeof(buffer), "%H:%M:%S", &tod); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("%s(0x%p): %s\n", msg, me, buffer)); +} /* TimeOfDayMessage */ + + +static void PR_CALLBACK Client(void *arg) +{ + PRStatus rv; + PRIntn index; + char buffer[1024]; + PRFileDesc *fd = NULL; + PRUintn clipping = DEFAULT_CLIPPING; + CSClient_t *client = (CSClient_t*)arg; + PRThread *me = client->thread = PR_GetCurrentThread(); + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_CLIENT_TIMEOUT); + + + for (index = 0; index < sizeof(buffer); ++index) { + buffer[index] = (char)index; + } + + client->started = PR_IntervalNow(); + + PR_Lock(client->ml); + client->state = cs_run; + PR_NotifyCondVar(client->stateChange); + PR_Unlock(client->ml); + + TimeOfDayMessage("Client started at", me); + + while (cs_run == client->state) + { + PRInt32 bytes, descbytes, filebytes, netbytes; + + (void)PR_NetAddrToString(&client->serverAddress, buffer, sizeof(buffer)); + TEST_LOG(cltsrv_log_file, TEST_LOG_INFO, + ("\tClient(0x%p): connecting to server at %s\n", me, buffer)); + + fd = PR_Socket(domain, SOCK_STREAM, protocol); + TEST_ASSERT(NULL != fd); + rv = PR_Connect(fd, &client->serverAddress, timeout); + if (PR_FAILURE == rv) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): conection failed\n", me)); + goto aborted; + } + + memset(descriptor, 0, sizeof(*descriptor)); + descriptor->size = PR_htonl(descbytes = rand() % clipping); + PR_snprintf( + descriptor->filename, sizeof(descriptor->filename), + "CS%p%p-%p.dat", client->started, me, client->operations); + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tClient(0x%p): sending descriptor for %u bytes\n", me, descbytes)); + bytes = PR_Send( + fd, descriptor, sizeof(*descriptor), SEND_FLAGS, timeout); + if (sizeof(CSDescriptor_t) != bytes) + { + if (Aborted(PR_FAILURE)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): send descriptor timeout\n", me)); + goto retry; + } + } + TEST_ASSERT(sizeof(*descriptor) == bytes); + + netbytes = 0; + while (netbytes < descbytes) + { + filebytes = sizeof(buffer); + if ((descbytes - netbytes) < filebytes) { + filebytes = descbytes - netbytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tClient(0x%p): sending %d bytes\n", me, filebytes)); + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); + if (filebytes != bytes) + { + if (Aborted(PR_FAILURE)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): send data timeout\n", me)); + goto retry; + } + } + TEST_ASSERT(bytes == filebytes); + netbytes += bytes; + } + filebytes = 0; + while (filebytes < descbytes) + { + netbytes = sizeof(buffer); + if ((descbytes - filebytes) < netbytes) { + netbytes = descbytes - filebytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tClient(0x%p): receiving %d bytes\n", me, netbytes)); + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); + if (-1 == bytes) + { + if (Aborted(PR_FAILURE)) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): receive data aborted\n", me)); + goto aborted; + } + else if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): receive data timeout\n", me)); + else + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tClient(0x%p): receive error (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto retry; + } + if (0 == bytes) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tClient(0x%p): unexpected end of stream\n", + PR_GetCurrentThread())); + break; + } + filebytes += bytes; + } + + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); + if (Aborted(rv)) { + goto aborted; + } + TEST_ASSERT(PR_SUCCESS == rv); +retry: + (void)PR_Close(fd); fd = NULL; + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("\tClient(0x%p): disconnected from server\n", me)); + + PR_Lock(client->ml); + client->operations += 1; + client->bytesTransferred += 2 * descbytes; + rv = PR_WaitCondVar(client->stateChange, rand() % clipping); + PR_Unlock(client->ml); + if (Aborted(rv)) { + break; + } + } + +aborted: + client->stopped = PR_IntervalNow(); + + PR_ClearInterrupt(); + if (NULL != fd) { + rv = PR_Close(fd); + } + + PR_Lock(client->ml); + client->state = cs_exit; + PR_NotifyCondVar(client->stateChange); + PR_Unlock(client->ml); + PR_DELETE(descriptor); + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("\tClient(0x%p): stopped after %u operations and %u bytes\n", + PR_GetCurrentThread(), client->operations, client->bytesTransferred)); + +} /* Client */ + +static PRStatus ProcessRequest(PRFileDesc *fd, CSServer_t *server) +{ + PRStatus drv, rv; + char buffer[1024]; + PRFileDesc *file = NULL; + PRThread * me = PR_GetCurrentThread(); + PRInt32 bytes, descbytes, netbytes, filebytes = 0; + CSDescriptor_t *descriptor = PR_NEW(CSDescriptor_t); + PRIntervalTime timeout = PR_MillisecondsToInterval(DEFAULT_SERVER_TIMEOUT); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): receiving desciptor\n", me)); + bytes = PR_Recv( + fd, descriptor, sizeof(*descriptor), RECV_FLAGS, timeout); + if (-1 == bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto exit; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tProcessRequest(0x%p): receive timeout\n", me)); + } + goto exit; + } + if (0 == bytes) + { + rv = PR_FAILURE; + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tProcessRequest(0x%p): unexpected end of file\n", me)); + goto exit; + } + descbytes = PR_ntohl(descriptor->size); + TEST_ASSERT(sizeof(*descriptor) == bytes); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): read descriptor {%d, %s}\n", + me, descbytes, descriptor->filename)); + + file = PR_Open( + descriptor->filename, (PR_CREATE_FILE | PR_WRONLY), 0666); + if (NULL == file) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\tProcessRequest(0x%p): open file timeout\n", me)); + goto aborted; + } + } + TEST_ASSERT(NULL != file); + + filebytes = 0; + while (filebytes < descbytes) + { + netbytes = sizeof(buffer); + if ((descbytes - filebytes) < netbytes) { + netbytes = descbytes - filebytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): receive %d bytes\n", me, netbytes)); + bytes = PR_Recv(fd, buffer, netbytes, RECV_FLAGS, timeout); + if (-1 == bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): receive data timeout\n", me)); + goto aborted; + } + /* + * XXX: I got (PR_CONNECT_RESET_ERROR, ERROR_NETNAME_DELETED) + * on NT here. This is equivalent to ECONNRESET on Unix. + * -wtc + */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_WARNING, + ("\t\tProcessRequest(0x%p): unexpected error (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + if(0 == bytes) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_WARNING, + ("\t\tProcessRequest(0x%p): unexpected end of stream\n", me)); + rv = PR_FAILURE; + goto aborted; + } + filebytes += bytes; + netbytes = bytes; + /* The byte count for PR_Write should be positive */ + MY_ASSERT(netbytes > 0); + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): write %d bytes to file\n", me, netbytes)); + bytes = PR_Write(file, buffer, netbytes); + if (netbytes != bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): write file timeout\n", me)); + goto aborted; + } + } + TEST_ASSERT(bytes > 0); + } + + PR_Lock(server->ml); + server->operations += 1; + server->bytesTransferred += filebytes; + PR_Unlock(server->ml); + + rv = PR_Close(file); file = NULL; + if (Aborted(rv)) { + goto aborted; + } + TEST_ASSERT(PR_SUCCESS == rv); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): opening %s\n", me, descriptor->filename)); + file = PR_Open(descriptor->filename, PR_RDONLY, 0); + if (NULL == file) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): open file timeout\n", + PR_GetCurrentThread())); + goto aborted; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): other file open error (%u, %u)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + TEST_ASSERT(NULL != file); + + netbytes = 0; + while (netbytes < descbytes) + { + filebytes = sizeof(buffer); + if ((descbytes - netbytes) < filebytes) { + filebytes = descbytes - netbytes; + } + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tProcessRequest(0x%p): read %d bytes from file\n", me, filebytes)); + bytes = PR_Read(file, buffer, filebytes); + if (filebytes != bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): read file timeout\n", me)); + else + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): other file error (%d, %d)\n", + me, PR_GetError(), PR_GetOSError())); + goto aborted; + } + TEST_ASSERT(bytes > 0); + netbytes += bytes; + filebytes = bytes; + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): sending %d bytes\n", me, filebytes)); + bytes = PR_Send(fd, buffer, filebytes, SEND_FLAGS, timeout); + if (filebytes != bytes) + { + rv = PR_FAILURE; + if (Aborted(rv)) { + goto aborted; + } + if (PR_IO_TIMEOUT_ERROR == PR_GetError()) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tProcessRequest(0x%p): send data timeout\n", me)); + goto aborted; + } + break; + } + TEST_ASSERT(bytes > 0); + } + + PR_Lock(server->ml); + server->bytesTransferred += filebytes; + PR_Unlock(server->ml); + + rv = PR_Shutdown(fd, PR_SHUTDOWN_BOTH); + if (Aborted(rv)) { + goto aborted; + } + + rv = PR_Close(file); file = NULL; + if (Aborted(rv)) { + goto aborted; + } + TEST_ASSERT(PR_SUCCESS == rv); + +aborted: + PR_ClearInterrupt(); + if (NULL != file) { + PR_Close(file); + } + drv = PR_Delete(descriptor->filename); + TEST_ASSERT(PR_SUCCESS == drv); +exit: + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tProcessRequest(0x%p): Finished\n", me)); + + PR_DELETE(descriptor); + +#if defined(WIN95) + PR_Sleep(PR_MillisecondsToInterval(200)); /* lth. see note [1] */ +#endif + return rv; +} /* ProcessRequest */ + +typedef void (*StartFn)(void*); +typedef struct StartObject +{ + StartFn start; + void *arg; +} StartObject; + +#if defined(_PR_PTHREADS) +#include "md/_pth.h" +#include <pthread.h> + +static void *pthread_start(void *arg) +{ + StartObject *so = (StartObject*)arg; + StartFn start = so->start; + void *data = so->arg; + PR_Free(so); + start(data); + return NULL; +} /* pthread_start */ +#endif /* defined(_PR_PTHREADS) */ + +#if defined(WIN32) +#include <process.h> /* for _beginthreadex() */ + +static PRUintn __stdcall windows_start(void *arg) +{ + StartObject *so = (StartObject*)arg; + StartFn start = so->start; + void *data = so->arg; + PR_Free(so); + start(data); + return 0; +} /* windows_start */ +#endif /* defined(WIN32) */ + +static PRStatus JoinThread(PRThread *thread) +{ + PRStatus rv; + switch (thread_provider) + { + case thread_nspr: + rv = PR_JoinThread(thread); + break; + case thread_pthread: +#if defined(_PR_PTHREADS) + rv = PR_SUCCESS; + break; +#endif /* defined(_PR_PTHREADS) */ + case thread_win32: +#if defined(WIN32) + rv = PR_SUCCESS; + break; +#endif + default: + rv = PR_FAILURE; + break; + } + return rv; +} /* JoinThread */ + +static PRStatus NewThread( + StartFn start, void *arg, PRThreadPriority prio, PRThreadState state) +{ + PRStatus rv; + + switch (thread_provider) + { + case thread_nspr: + { + PRThread *thread = PR_CreateThread( + PR_USER_THREAD, start, arg, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0); + rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS; + } + break; + case thread_pthread: +#if defined(_PR_PTHREADS) + { + int rv; + pthread_t id; + pthread_attr_t tattr; + StartObject *start_object; + start_object = PR_NEW(StartObject); + PR_ASSERT(NULL != start_object); + start_object->start = start; + start_object->arg = arg; + + rv = _PT_PTHREAD_ATTR_INIT(&tattr); + PR_ASSERT(0 == rv); + + rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + PR_ASSERT(0 == rv); + + rv = pthread_attr_setstacksize(&tattr, 64 * 1024); + PR_ASSERT(0 == rv); + + rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object); + (void)_PT_PTHREAD_ATTR_DESTROY(&tattr); + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; + } +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; +#endif /* defined(_PR_PTHREADS) */ + break; + + case thread_sproc: + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; + break; + case thread_win32: +#if defined(WIN32) + { + void *th; + PRUintn id; + StartObject *start_object; + start_object = PR_NEW(StartObject); + PR_ASSERT(NULL != start_object); + start_object->start = start; + start_object->arg = arg; + th = (void*)_beginthreadex( + NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */ + 0U, /* DWORD - initial thread stack size, in bytes */ + windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */ + start_object, /* LPVOID - argument for new thread */ + STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */ + &id /* LPDWORD - pointer to returned thread identifier */ ); + + rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS; + } +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; +#endif + break; + default: + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + rv = PR_FAILURE; + } + return rv; +} /* NewThread */ + +static PRStatus CreateWorker(CSServer_t *server, CSPool_t *pool) +{ + PRStatus rv; + CSWorker_t *worker = PR_NEWZAP(CSWorker_t); + worker->server = server; + PR_INIT_CLIST(&worker->element); + rv = NewThread( + Worker, worker, DEFAULT_SERVER_PRIORITY, PR_UNJOINABLE_THREAD); + if (PR_FAILURE == rv) { + PR_DELETE(worker); + } + + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, + ("\tCreateWorker(0x%p): create new worker (0x%p)\n", + PR_GetCurrentThread(), worker->thread)); + + return rv; +} /* CreateWorker */ + +static void PR_CALLBACK Worker(void *arg) +{ + PRStatus rv; + PRNetAddr from; + PRFileDesc *fd = NULL; + CSWorker_t *worker = (CSWorker_t*)arg; + CSServer_t *server = worker->server; + CSPool_t *pool = &server->pool; + + PRThread *me = worker->thread = PR_GetCurrentThread(); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1)); + + PR_Lock(server->ml); + PR_APPEND_LINK(&worker->element, &server->list); + pool->workers += 1; /* define our existance */ + + while (cs_run == server->state) + { + while (pool->accepting >= server->workers.accepting) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", + me, pool->accepting)); + rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT); + if (Aborted(rv) || (cs_run != server->state)) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\tWorker(0x%p): has been %s\n", + me, (Aborted(rv) ? "interrupted" : "stopped"))); + goto exit; + } + } + pool->accepting += 1; /* how many are really in accept */ + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\t\tWorker(0x%p): calling accept\n", me)); + fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT); + + PR_Lock(server->ml); + pool->accepting -= 1; + PR_NotifyCondVar(pool->acceptComplete); + + if ((NULL == fd) && Aborted(PR_FAILURE)) + { + if (NULL != server->listener) + { + PR_Close(server->listener); + server->listener = NULL; + } + goto exit; + } + + if (NULL != fd) + { + /* + ** Create another worker of the total number of workers is + ** less than the minimum specified or we have none left in + ** accept() AND we're not over the maximum. + ** This sort of presumes that the number allowed in accept + ** is at least as many as the minimum. Otherwise we'll keep + ** creating new threads and deleting them soon after. + */ + PRBool another = + ((pool->workers < server->workers.minimum) || + ((0 == pool->accepting) + && (pool->workers < server->workers.maximum))) ? + PR_TRUE : PR_FALSE; + pool->active += 1; + PR_Unlock(server->ml); + + if (another) { + (void)CreateWorker(server, pool); + } + + rv = ProcessRequest(fd, server); + if (PR_SUCCESS != rv) + TEST_LOG( + cltsrv_log_file, TEST_LOG_ERROR, + ("\t\tWorker(0x%p): server process ended abnormally\n", me)); + (void)PR_Close(fd); fd = NULL; + + PR_Lock(server->ml); + pool->active -= 1; + } + } + +exit: + PR_ClearInterrupt(); + PR_Unlock(server->ml); + + if (NULL != fd) + { + (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH); + (void)PR_Close(fd); + } + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers)); + + PR_Lock(server->ml); + pool->workers -= 1; /* undefine our existance */ + PR_REMOVE_AND_INIT_LINK(&worker->element); + PR_NotifyCondVar(pool->exiting); + PR_Unlock(server->ml); + + PR_DELETE(worker); /* destruction of the "worker" object */ + +} /* Worker */ + +static void PR_CALLBACK Server(void *arg) +{ + PRStatus rv; + PRNetAddr serverAddress; + CSServer_t *server = (CSServer_t*)arg; + PRThread *me = server->thread = PR_GetCurrentThread(); + PRSocketOptionData sockOpt; + + server->listener = PR_Socket(domain, SOCK_STREAM, protocol); + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + rv = PR_SetSocketOption(server->listener, &sockOpt); + TEST_ASSERT(PR_SUCCESS == rv); + + memset(&serverAddress, 0, sizeof(serverAddress)); + rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress); + + rv = PR_Bind(server->listener, &serverAddress); + TEST_ASSERT(PR_SUCCESS == rv); + + rv = PR_Listen(server->listener, server->backlog); + TEST_ASSERT(PR_SUCCESS == rv); + + server->started = PR_IntervalNow(); + TimeOfDayMessage("Server started at", me); + + PR_Lock(server->ml); + server->state = cs_run; + PR_NotifyCondVar(server->stateChange); + PR_Unlock(server->ml); + + /* + ** Create the first worker (actually, a thread that accepts + ** connections and then processes the work load as needed). + ** From this point on, additional worker threads are created + ** as they are needed by existing worker threads. + */ + rv = CreateWorker(server, &server->pool); + TEST_ASSERT(PR_SUCCESS == rv); + + /* + ** From here on this thread is merely hanging around as the contact + ** point for the main test driver. It's just waiting for the driver + ** to declare the test complete. + */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tServer(0x%p): waiting for state change\n", me)); + + PR_Lock(server->ml); + while ((cs_run == server->state) && !Aborted(rv)) + { + rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(server->ml); + PR_ClearInterrupt(); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("\tServer(0x%p): shutting down workers\n", me)); + + /* + ** Get all the worker threads to exit. They know how to + ** clean up after themselves, so this is just a matter of + ** waiting for clorine in the pool to take effect. During + ** this stage we're ignoring interrupts. + */ + server->workers.minimum = server->workers.maximum = 0; + + PR_Lock(server->ml); + while (!PR_CLIST_IS_EMPTY(&server->list)) + { + PRCList *head = PR_LIST_HEAD(&server->list); + CSWorker_t *worker = (CSWorker_t*)head; + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker)); + rv = PR_Interrupt(worker->thread); + TEST_ASSERT(PR_SUCCESS == rv); + PR_REMOVE_AND_INIT_LINK(head); + } + + while (server->pool.workers > 0) + { + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("\tServer(0x%p): waiting for %u workers to exit\n", + me, server->pool.workers)); + (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT); + } + + server->state = cs_exit; + PR_NotifyCondVar(server->stateChange); + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("\tServer(0x%p): stopped after %u operations and %u bytes\n", + me, server->operations, server->bytesTransferred)); + + if (NULL != server->listener) { + PR_Close(server->listener); + } + server->stopped = PR_IntervalNow(); + +} /* Server */ + +static void WaitForCompletion(PRIntn execution) +{ + while (execution > 0) + { + PRIntn dally = (execution > 30) ? 30 : execution; + PR_Sleep(PR_SecondsToInterval(dally)); + if (pthread_stats) { + PT_FPrintStats(debug_out, "\nPThread Statistics\n"); + } + execution -= dally; + } +} /* WaitForCompletion */ + +static void Help(void) +{ + PR_fprintf(debug_out, "cltsrv test program usage:\n"); + PR_fprintf(debug_out, "\t-a <n> threads allowed in accept (5)\n"); + PR_fprintf(debug_out, "\t-b <n> backlock for listen (5)\n"); + PR_fprintf(debug_out, "\t-c <threads> number of clients to create (1)\n"); + PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n"); + PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n"); + PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds (10)\n"); + PR_fprintf(debug_out, "\t-s <string> dsn name of server (localhost)\n"); + PR_fprintf(debug_out, "\t-G use GLOBAL threads (LOCAL)\n"); + PR_fprintf(debug_out, "\t-T <string> thread provider ('n' | 'p' | 'w')(n)\n"); + PR_fprintf(debug_out, "\t-X use XTP as transport (TCP)\n"); + PR_fprintf(debug_out, "\t-6 Use IPv6 (IPv4)\n"); + PR_fprintf(debug_out, "\t-v verbosity (accumulative) (0)\n"); + PR_fprintf(debug_out, "\t-p pthread statistics (FALSE)\n"); + PR_fprintf(debug_out, "\t-d debug mode (FALSE)\n"); + PR_fprintf(debug_out, "\t-h this message\n"); +} /* Help */ + +static Verbosity IncrementVerbosity(void) +{ + PRIntn verboge = (PRIntn)verbosity + 1; + return (Verbosity)verboge; +} /* IncrementVerbosity */ + +int main(int argc, char **argv) +{ + PRUintn index; + PRBool boolean; + CSClient_t *client; + PRStatus rv, joinStatus; + CSServer_t *server = NULL; + char *thread_type; + + PRUintn backlog = DEFAULT_BACKLOG; + PRUintn clients = DEFAULT_CLIENTS; + const char *serverName = DEFAULT_SERVER; + PRBool serverIsLocal = PR_TRUE; + PRUintn accepting = ALLOWED_IN_ACCEPT; + PRUintn workersMin = DEFAULT_WORKERS_MIN; + PRUintn workersMax = DEFAULT_WORKERS_MAX; + PRIntn execution = DEFAULT_EXECUTION_TIME; + + /* + * -G use global threads + * -a <n> threads allowed in accept + * -b <n> backlock for listen + * -c <threads> number of clients to create + * -w <threads> minimal number of server threads + * -W <threads> maximum number of server threads + * -e <seconds> duration of the test in seconds + * -s <string> dsn name of server (implies no server here) + * -v verbosity + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp"); + +#if defined(WIN32) + thread_provider = thread_win32; +#elif defined(_PR_PTHREADS) + thread_provider = thread_pthread; +#else + thread_provider = thread_nspr; +#endif + + debug_out = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'X': /* use XTP as transport */ + protocol = 36; + break; + case '6': /* Use IPv6 */ + domain = PR_AF_INET6; + break; + case 'a': /* the value for accepting */ + accepting = atoi(opt->value); + break; + case 'b': /* the value for backlock */ + backlog = atoi(opt->value); + break; + case 'T': /* the thread provider */ + if ('n' == *opt->value) { + thread_provider = thread_nspr; + } + else if ('p' == *opt->value) { + thread_provider = thread_pthread; + } + else if ('w' == *opt->value) { + thread_provider = thread_win32; + } + else { + Help(); + return 2; + } + break; + case 'c': /* number of client threads */ + clients = atoi(opt->value); + break; + case 'w': /* minimum server worker threads */ + workersMin = atoi(opt->value); + break; + case 'W': /* maximum server worker threads */ + workersMax = atoi(opt->value); + break; + case 'e': /* program execution time in seconds */ + execution = atoi(opt->value); + break; + case 's': /* server's address */ + serverName = opt->value; + break; + case 'v': /* verbosity */ + verbosity = IncrementVerbosity(); + break; + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'p': /* pthread mode */ + pthread_stats = PR_TRUE; + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + if (0 != PL_strcmp(serverName, DEFAULT_SERVER)) { + serverIsLocal = PR_FALSE; + } + if (0 == execution) { + execution = DEFAULT_EXECUTION_TIME; + } + if (0 == workersMax) { + workersMax = DEFAULT_WORKERS_MAX; + } + if (0 == workersMin) { + workersMin = DEFAULT_WORKERS_MIN; + } + if (0 == accepting) { + accepting = ALLOWED_IN_ACCEPT; + } + if (0 == backlog) { + backlog = DEFAULT_BACKLOG; + } + + if (workersMin > accepting) { + accepting = workersMin; + } + + PR_STDIO_INIT(); + TimeOfDayMessage("Client/Server started at", PR_GetCurrentThread()); + + cltsrv_log_file = PR_NewLogModule("cltsrv_log"); + MY_ASSERT(NULL != cltsrv_log_file); + boolean = PR_SetLogFile("cltsrv.log"); + MY_ASSERT(boolean); + + if (serverIsLocal) + { + /* Establish the server */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("main(0x%p): starting server\n", PR_GetCurrentThread())); + + server = PR_NEWZAP(CSServer_t); + PR_INIT_CLIST(&server->list); + server->state = cs_init; + server->ml = PR_NewLock(); + server->backlog = backlog; + server->port = DEFAULT_PORT; + server->workers.minimum = workersMin; + server->workers.maximum = workersMax; + server->workers.accepting = accepting; + server->stateChange = PR_NewCondVar(server->ml); + server->pool.exiting = PR_NewCondVar(server->ml); + server->pool.acceptComplete = PR_NewCondVar(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("main(0x%p): creating server thread\n", PR_GetCurrentThread())); + + rv = NewThread( + Server, server, PR_PRIORITY_HIGH, PR_JOINABLE_THREAD); + TEST_ASSERT(PR_SUCCESS == rv); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): waiting for server init\n", PR_GetCurrentThread())); + + PR_Lock(server->ml); + while (server->state == cs_init) { + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): server init complete (port #%d)\n", + PR_GetCurrentThread(), server->port)); + } + + if (clients != 0) + { + /* Create all of the clients */ + PRHostEnt host; + char buffer[BUFFER_SIZE]; + client = (CSClient_t*)PR_CALLOC(clients * sizeof(CSClient_t)); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): creating %d client threads\n", + PR_GetCurrentThread(), clients)); + + if (!serverIsLocal) + { + rv = PR_GetHostByName(serverName, buffer, BUFFER_SIZE, &host); + if (PR_SUCCESS != rv) + { + PL_FPrintError(PR_STDERR, "PR_GetHostByName"); + return 2; + } + } + + for (index = 0; index < clients; ++index) + { + client[index].state = cs_init; + client[index].ml = PR_NewLock(); + if (serverIsLocal) + { + (void)PR_InitializeNetAddr( + PR_IpAddrLoopback, DEFAULT_PORT, + &client[index].serverAddress); + } + else + { + (void)PR_EnumerateHostEnt( + 0, &host, DEFAULT_PORT, &client[index].serverAddress); + } + client[index].stateChange = PR_NewCondVar(client[index].ml); + TEST_LOG( + cltsrv_log_file, TEST_LOG_INFO, + ("main(0x%p): creating client threads\n", PR_GetCurrentThread())); + rv = NewThread( + Client, &client[index], PR_PRIORITY_NORMAL, PR_JOINABLE_THREAD); + TEST_ASSERT(PR_SUCCESS == rv); + PR_Lock(client[index].ml); + while (cs_init == client[index].state) { + PR_WaitCondVar(client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(client[index].ml); + } + } + + /* Then just let them go at it for a bit */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("main(0x%p): waiting for execution interval (%d seconds)\n", + PR_GetCurrentThread(), execution)); + + WaitForCompletion(execution); + + TimeOfDayMessage("Shutting down", PR_GetCurrentThread()); + + if (clients != 0) + { + for (index = 0; index < clients; ++index) + { + TEST_LOG(cltsrv_log_file, TEST_LOG_STATUS, + ("main(0x%p): notifying client(0x%p) to stop\n", + PR_GetCurrentThread(), client[index].thread)); + + PR_Lock(client[index].ml); + if (cs_run == client[index].state) + { + client[index].state = cs_stop; + PR_Interrupt(client[index].thread); + while (cs_stop == client[index].state) + PR_WaitCondVar( + client[index].stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(client[index].ml); + + TEST_LOG(cltsrv_log_file, TEST_LOG_VERBOSE, + ("main(0x%p): joining client(0x%p)\n", + PR_GetCurrentThread(), client[index].thread)); + + joinStatus = JoinThread(client[index].thread); + TEST_ASSERT(PR_SUCCESS == joinStatus); + PR_DestroyCondVar(client[index].stateChange); + PR_DestroyLock(client[index].ml); + } + PR_DELETE(client); + } + + if (NULL != server) + { + /* All clients joined - retrieve the server */ + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("main(0x%p): notifying server(0x%p) to stop\n", + PR_GetCurrentThread(), server->thread)); + + PR_Lock(server->ml); + server->state = cs_stop; + PR_Interrupt(server->thread); + while (cs_exit != server->state) { + PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(server->ml); + + TEST_LOG( + cltsrv_log_file, TEST_LOG_NOTICE, + ("main(0x%p): joining server(0x%p)\n", + PR_GetCurrentThread(), server->thread)); + joinStatus = JoinThread(server->thread); + TEST_ASSERT(PR_SUCCESS == joinStatus); + + PR_DestroyCondVar(server->stateChange); + PR_DestroyCondVar(server->pool.exiting); + PR_DestroyCondVar(server->pool.acceptComplete); + PR_DestroyLock(server->ml); + PR_DELETE(server); + } + + TEST_LOG( + cltsrv_log_file, TEST_LOG_ALWAYS, + ("main(0x%p): test complete\n", PR_GetCurrentThread())); + + if (thread_provider == thread_win32) { + thread_type = "\nWin32 Thread Statistics\n"; + } + else if (thread_provider == thread_pthread) { + thread_type = "\npthread Statistics\n"; + } + else if (thread_provider == thread_sproc) { + thread_type = "\nsproc Statistics\n"; + } + else { + PR_ASSERT(thread_provider == thread_nspr); + thread_type = "\nPRThread Statistics\nn"; + } + + PT_FPrintStats(debug_out, thread_type); + + TimeOfDayMessage("Test exiting at", PR_GetCurrentThread()); + PR_Cleanup(); + return 0; +} /* main */ + +/* cltsrv.c */ diff --git a/nsprpub/pr/tests/prpoll.c b/nsprpub/pr/tests/prpoll.c new file mode 100644 index 0000000000..5c87a57d17 --- /dev/null +++ b/nsprpub/pr/tests/prpoll.c @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef WIN32 +#include <windows.h> +#endif + +#ifdef XP_UNIX +#include <unistd.h> /* for close() */ +#endif + +#include "prinit.h" +#include "prio.h" +#include "prlog.h" +#include "prprf.h" +#include "prnetdb.h" + +#include "private/pprio.h" + +#define CLIENT_LOOPS 5 +#define BUF_SIZE 128 + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifdef WINCE + +int main(int argc, char **argv) +{ + fprintf(stderr, "Invalid/Broken Test for WinCE/WinMobile\n"); + exit(1); +} + +#else + +static void +clientThreadFunc(void *arg) +{ + PRUint16 port = (PRUint16) arg; + PRFileDesc *sock; + PRNetAddr addr; + char buf[BUF_SIZE]; + int i; + + addr.inet.family = PR_AF_INET; + addr.inet.port = PR_htons(port); + addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + PR_snprintf(buf, sizeof(buf), "%hu", port); + + for (i = 0; i < 5; i++) { + sock = PR_NewTCPSocket(); + PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); + + PR_Write(sock, buf, sizeof(buf)); + PR_Close(sock); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1, *listenSock2; + PRFileDesc *badFD; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + char buf[BUF_SIZE]; + PRThread *clientThread; + PRPollDesc pds0[10], pds1[10], *pds, *other_pds; + PRIntn npds; + PRInt32 retVal; + PRInt32 rv; + PROsfd sd; + struct sockaddr_in saddr; + PRIntn saddr_len; + PRUint16 listenPort3; + PRFileDesc *socket_poll_fd; + PRIntn i, j; + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + printf("This program tests PR_Poll with sockets.\n"); + printf("Timeout, error reporting, and normal operation are tested.\n\n"); + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + exit(1); + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + exit(1); + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + exit(1); + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + exit(1); + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + exit(1); + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + exit(1); + } + /* Set up the poll descriptor array */ + pds = pds0; + other_pds = pds1; + memset(pds, 0, sizeof(pds)); + npds = 0; + pds[npds].fd = listenSock1; + pds[npds].in_flags = PR_POLL_READ; + npds++; + pds[npds].fd = listenSock2; + pds[npds].in_flags = PR_POLL_READ; + npds++; + + sd = socket(AF_INET, SOCK_STREAM, 0); + PR_ASSERT(sd >= 0); + memset((char *) &saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + saddr.sin_port = htons(0); + + rv = bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)); + PR_ASSERT(rv == 0); + saddr_len = sizeof(saddr); + rv = getsockname(sd, (struct sockaddr *) &saddr, &saddr_len); + PR_ASSERT(rv == 0); + listenPort3 = ntohs(saddr.sin_port); + + rv = listen(sd, 5); + PR_ASSERT(rv == 0); + pds[npds].fd = socket_poll_fd = PR_CreateSocketPollFd(sd); + PR_ASSERT(pds[npds].fd); + pds[npds].in_flags = PR_POLL_READ; + npds++; + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu, %hu and %hu\n\n", + listenPort1, listenPort2, listenPort3); + printf("%s", buf); + + /* Testing timeout */ + printf("PR_Poll should time out in 5 seconds\n"); + retVal = PR_Poll(pds, npds, PR_SecondsToInterval(5)); + if (retVal != 0) { + PR_snprintf(buf, sizeof(buf), + "PR_Poll should time out and return 0, but it returns %ld\n", + retVal); + fprintf(stderr, "%s", buf); + exit(1); + } + printf("PR_Poll timed out. Test passed.\n\n"); + + /* Testing bad fd */ + printf("PR_Poll should detect a bad file descriptor\n"); + if ((badFD = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a TCP socket\n"); + exit(1); + } + + pds[npds].fd = badFD; + pds[npds].in_flags = PR_POLL_READ; + npds++; + PR_Close(badFD); /* make the fd bad */ +#if 0 + retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT); + if (retVal != 1 || (unsigned short) pds[2].out_flags != PR_POLL_NVAL) { + fprintf(stderr, "Failed to detect the bad fd: " + "PR_Poll returns %d, out_flags is 0x%hx\n", + retVal, pds[npds - 1].out_flags); + exit(1); + } + printf("PR_Poll detected the bad fd. Test passed.\n\n"); +#endif + npds--; + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort1, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + exit(1); + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort2, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + exit(1); + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort3, + PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + exit(1); + } + + + printf("Three client threads are created. Each of them will\n"); + printf("send data to one of the three ports the server is listening on.\n"); + printf("The data they send is the port number. Each of them send\n"); + printf("the data five times, so you should see ten lines below,\n"); + printf("interleaved in an arbitrary order.\n"); + + /* 30 events total */ + i = 0; + while (i < 30) { + PRPollDesc *tmp; + int nextIndex; + int nEvents = 0; + + retVal = PR_Poll(pds, npds, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(retVal != 0); /* no timeout */ + if (retVal == -1) { + fprintf(stderr, "PR_Poll failed\n"); + exit(1); + } + + nextIndex = 3; + /* the three listening sockets */ + for (j = 0; j < 3; j++) { + other_pds[j] = pds[j]; + PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0 + && (pds[j].out_flags & PR_POLL_EXCEPT) == 0); + if (pds[j].out_flags & PR_POLL_READ) { + PRFileDesc *sock; + + nEvents++; + if (j == 2) { + PROsfd newsd; + newsd = accept(PR_FileDesc2NativeHandle(pds[j].fd), NULL, 0); + if (newsd == -1) { + fprintf(stderr, "accept() failed\n"); + exit(1); + } + other_pds[nextIndex].fd = PR_CreateSocketPollFd(newsd); + PR_ASSERT(other_pds[nextIndex].fd); + other_pds[nextIndex].in_flags = PR_POLL_READ; + } else { + sock = PR_Accept(pds[j].fd, NULL, PR_INTERVAL_NO_TIMEOUT); + if (sock == NULL) { + fprintf(stderr, "PR_Accept() failed\n"); + exit(1); + } + other_pds[nextIndex].fd = sock; + other_pds[nextIndex].in_flags = PR_POLL_READ; + } + nextIndex++; + } else if (pds[j].out_flags & PR_POLL_ERR) { + fprintf(stderr, "PR_Poll() indicates that an fd has error\n"); + exit(1); + } else if (pds[j].out_flags & PR_POLL_NVAL) { + fprintf(stderr, "PR_Poll() indicates that fd %d is invalid\n", + PR_FileDesc2NativeHandle(pds[j].fd)); + exit(1); + } + } + + for (j = 3; j < npds; j++) { + PR_ASSERT((pds[j].out_flags & PR_POLL_WRITE) == 0 + && (pds[j].out_flags & PR_POLL_EXCEPT) == 0); + if (pds[j].out_flags & PR_POLL_READ) { + PRInt32 nBytes; + + nEvents++; + /* XXX: This call is a hack and should be fixed */ + if (PR_GetDescType(pds[j].fd) == (PRDescType) 0) { + nBytes = recv(PR_FileDesc2NativeHandle(pds[j].fd), buf, + sizeof(buf), 0); + if (nBytes == -1) { + fprintf(stderr, "recv() failed\n"); + exit(1); + } + printf("Server read %d bytes from native fd %d\n",nBytes, + PR_FileDesc2NativeHandle(pds[j].fd)); +#ifdef WIN32 + closesocket((SOCKET)PR_FileDesc2NativeHandle(pds[j].fd)); +#else + close(PR_FileDesc2NativeHandle(pds[j].fd)); +#endif + PR_DestroySocketPollFd(pds[j].fd); + } else { + nBytes = PR_Read(pds[j].fd, buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read() failed\n"); + exit(1); + } + PR_Close(pds[j].fd); + } + /* Just to be safe */ + buf[BUF_SIZE - 1] = '\0'; + printf("The server received \"%s\" from a client\n", buf); + } else if (pds[j].out_flags & PR_POLL_ERR) { + fprintf(stderr, "PR_Poll() indicates that an fd has error\n"); + exit(1); + } else if (pds[j].out_flags & PR_POLL_NVAL) { + fprintf(stderr, "PR_Poll() indicates that an fd is invalid\n"); + exit(1); + } else { + other_pds[nextIndex] = pds[j]; + nextIndex++; + } + } + + PR_ASSERT(retVal == nEvents); + /* swap */ + tmp = pds; + pds = other_pds; + other_pds = tmp; + npds = nextIndex; + i += nEvents; + } + PR_DestroySocketPollFd(socket_poll_fd); + + printf("All tests finished\n"); + PR_Cleanup(); + return 0; +} + + +#endif /* ifdef WINCE */ diff --git a/nsprpub/pr/tests/prpollml.c b/nsprpub/pr/tests/prpollml.c new file mode 100644 index 0000000000..118d3674df --- /dev/null +++ b/nsprpub/pr/tests/prpollml.c @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This test exercises the code that allocates and frees the syspoll_list + * array of PRThread in the pthreads version. This test is intended to be + * run under Purify to verify that there is no memory leak. + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define POLL_DESC_COUNT 256 /* This should be greater than the + * STACK_POLL_DESC_COUNT macro in + * ptio.c to cause syspoll_list to + * be created. */ + +static PRPollDesc pd[POLL_DESC_COUNT]; + +static void Test(void) +{ + int i; + PRInt32 rv; + PRIntervalTime timeout; + + timeout = PR_MillisecondsToInterval(10); + /* cause syspoll_list to grow */ + for (i = 1; i <= POLL_DESC_COUNT; i++) { + rv = PR_Poll(pd, i, timeout); + if (rv != 0) { + fprintf(stderr, + "PR_Poll should time out but returns %d (%d, %d)\n", + (int) rv, (int) PR_GetError(), (int) PR_GetOSError()); + exit(1); + } + } + /* syspoll_list should be large enough for all these */ + for (i = POLL_DESC_COUNT; i >= 1; i--) { + rv = PR_Poll(pd, i, timeout); + if (rv != 0) { + fprintf(stderr, "PR_Poll should time out but returns %d\n", + (int) rv); + exit(1); + } + } +} + +static void ThreadFunc(void *arg) +{ + Test(); +} + +int main(int argc, char **argv) +{ + int i; + PRThread *thread; + PRFileDesc *sock; + PRNetAddr addr; + + memset(&addr, 0, sizeof(addr)); + addr.inet.family = PR_AF_INET; + addr.inet.port = PR_htons(0); + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + for (i = 0; i < POLL_DESC_COUNT; i++) { + sock = PR_NewTCPSocket(); + if (sock == NULL) { + fprintf(stderr, "PR_NewTCPSocket failed (%d, %d)\n", + (int) PR_GetError(), (int) PR_GetOSError()); + fprintf(stderr, "Ensure the per process file descriptor limit " + "is greater than %d.", POLL_DESC_COUNT); + exit(1); + } + if (PR_Bind(sock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed (%d, %d)\n", + (int) PR_GetError(), (int) PR_GetOSError()); + exit(1); + } + if (PR_Listen(sock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed (%d, %d)\n", + (int) PR_GetError(), (int) PR_GetOSError()); + exit(1); + } + + pd[i].fd = sock; + pd[i].in_flags = PR_POLL_READ; + } + + /* first run the test on the primordial thread */ + Test(); + + /* then run the test on all three kinds of threads */ + thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == thread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + if (PR_JoinThread(thread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == thread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + if (PR_JoinThread(thread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == thread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + if (PR_JoinThread(thread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + for (i = 0; i < POLL_DESC_COUNT; i++) { + if (PR_Close(pd[i].fd) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + } + PR_Cleanup(); + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/prselect.c b/nsprpub/pr/tests/prselect.c new file mode 100644 index 0000000000..2a81100e87 --- /dev/null +++ b/nsprpub/pr/tests/prselect.c @@ -0,0 +1,368 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1997 - Netscape Communications Corporation +** +** Name: prselect_err.c +** +** Description: tests PR_Select with sockets Error condition functions. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" +#include "prttools.h" + + +#include "prinit.h" +#include "prio.h" +#include "prlog.h" +#include "prprf.h" +#include "prerror.h" +#include "prnetdb.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +/*********************************************************************** +** PRIVATE FUNCTION: Test_Result +** DESCRIPTION: Used in conjunction with the regress tool, prints out the +** status of the test case. +** INPUTS: PASS/FAIL +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: Determine what the status is and print accordingly. +** +***********************************************************************/ + + +static Test_Result (int result) +{ + if (result == PASS) { + printf ("PASS\n"); + } + else { + printf ("FAIL\n"); + } +} + +static void +clientThreadFunc(void *arg) +{ + PRUint16 port = (PRUint16) arg; + PRFileDesc *sock; + PRNetAddr addr; + char buf[128]; + int i; + + addr.inet.family = AF_INET; + addr.inet.port = PR_htons(port); + addr.inet.ip = PR_htonl(INADDR_LOOPBACK); + PR_snprintf(buf, sizeof(buf), "%hu", port); + + for (i = 0; i < 5; i++) { + sock = PR_NewTCPSocket(); + PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); + PR_Write(sock, buf, sizeof(buf)); + PR_Close(sock); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1, *listenSock2; + PRFileDesc *badFD; + PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds; + PRIntn nfds; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + PR_fd_set readFdSet; + char buf[128]; + PRThread *clientThread; + PRInt32 retVal; + PRIntn i, j; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Select with sockets. Timeout, error\n"); + printf("reporting, and normal operation are tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + if (!debug_mode) { + Test_Result(FAIL); + } + exit(1); + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + printf("%s", buf); + + /* Set up the fd set */ + PR_FD_ZERO(&readFdSet); + PR_FD_SET(listenSock1, &readFdSet); + PR_FD_SET(listenSock2, &readFdSet); + + /* Testing timeout */ + if (debug_mode) { + printf("PR_Select should time out in 5 seconds\n"); + } + retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, + PR_SecondsToInterval(5)); + if (retVal != 0) { + PR_snprintf(buf, sizeof(buf), + "PR_Select should time out and return 0, but it returns %ld\n", + retVal); + fprintf(stderr, "%s", buf); + if (retVal == -1) { + fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(), + PR_GetOSError()); + if (!debug_mode) { + Test_Result(FAIL); + } + } + exit(1); + } + if (debug_mode) { + printf("PR_Select timed out. Test passed.\n\n"); + } + else { + Test_Result(PASS); + } + + /* Testing bad fd */ + printf("PR_Select should detect a bad file descriptor\n"); + if ((badFD = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a TCP socket\n"); + exit(1); + } + + PR_FD_SET(listenSock1, &readFdSet); + PR_FD_SET(listenSock2, &readFdSet); + PR_FD_SET(badFD, &readFdSet); + PR_Close(badFD); /* make the fd bad */ + retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, + PR_INTERVAL_NO_TIMEOUT); + if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) { + fprintf(stderr, "Failed to detect the bad fd: " + "PR_Select returns %d\n", retVal); + if (retVal == -1) { + fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(), + PR_GetOSError()); + } + exit(1); + } + printf("PR_Select detected a bad fd. Test passed.\n\n"); + PR_FD_CLR(badFD, &readFdSet); + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort1, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + exit(1); + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort2, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + exit(1); + } + + printf("Two client threads are created. Each of them will\n"); + printf("send data to one of the two ports the server is listening on.\n"); + printf("The data they send is the port number. Each of them send\n"); + printf("the data five times, so you should see ten lines below,\n"); + printf("interleaved in an arbitrary order.\n"); + + /* set up the fd array */ + fds = fds0; + other_fds = fds1; + fds[0] = listenSock1; + fds[1] = listenSock2; + nfds = 2; + PR_FD_SET(listenSock1, &readFdSet); + PR_FD_SET(listenSock2, &readFdSet); + + /* 20 events total */ + i = 0; + while (i < 20) { + PRFileDesc **tmp; + int nextIndex; + int nEvents = 0; + + retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, + PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(retVal != 0); /* no timeout */ + if (retVal == -1) { + fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + exit(1); + } + + nextIndex = 2; + /* the two listening sockets */ + for (j = 0; j < 2; j++) { + other_fds[j] = fds[j]; + if (PR_FD_ISSET(fds[j], &readFdSet)) { + PRFileDesc *sock; + + nEvents++; + sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT); + if (sock == NULL) { + fprintf(stderr, "PR_Accept() failed\n"); + exit(1); + } + other_fds[nextIndex] = sock; + PR_FD_SET(sock, &readFdSet); + nextIndex++; + } + PR_FD_SET(fds[j], &readFdSet); + } + + for (j = 2; j < nfds; j++) { + if (PR_FD_ISSET(fds[j], &readFdSet)) { + PRInt32 nBytes; + + PR_FD_CLR(fds[j], &readFdSet); + nEvents++; + nBytes = PR_Read(fds[j], buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read() failed\n"); + exit(1); + } + /* Just to be safe */ + buf[127] = '\0'; + PR_Close(fds[j]); + printf("The server received \"%s\" from a client\n", buf); + } else { + PR_FD_SET(fds[j], &readFdSet); + other_fds[nextIndex] = fds[j]; + nextIndex++; + } + } + + PR_ASSERT(retVal == nEvents); + /* swap */ + tmp = fds; + fds = other_fds; + other_fds = tmp; + nfds = nextIndex; + i += nEvents; + } + + printf("All tests finished\n"); + PR_Cleanup(); + return 0; +} diff --git a/nsprpub/pr/tests/prttools.h b/nsprpub/pr/tests/prttools.h new file mode 100644 index 0000000000..e3cac5daa9 --- /dev/null +++ b/nsprpub/pr/tests/prttools.h @@ -0,0 +1,11 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used in Regress Tool */ +#define NOSTATUS 2 +#define PASS 1 +#define FAIL 0 + +PRIntn debug_mode=0; diff --git a/nsprpub/pr/tests/pushtop.c b/nsprpub/pr/tests/pushtop.c new file mode 100644 index 0000000000..621a7c7248 --- /dev/null +++ b/nsprpub/pr/tests/pushtop.c @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A regression test for bug 794316 */ + +#include <stdio.h> +#include <stdlib.h> + +#include "prio.h" + +static PRIOMethods dummyMethods; + +int main() +{ + PRDescIdentity topId, middleId, bottomId; + PRFileDesc *top, *middle, *bottom; + PRFileDesc *fd; + + topId = PR_GetUniqueIdentity("top"); + middleId = PR_GetUniqueIdentity("middle"); + bottomId = PR_GetUniqueIdentity("bottom"); + + top = PR_CreateIOLayerStub(topId, &dummyMethods); + middle = PR_CreateIOLayerStub(middleId, &dummyMethods); + bottom = PR_CreateIOLayerStub(bottomId, &dummyMethods); + + fd = bottom; + PR_PushIOLayer(fd, PR_TOP_IO_LAYER, middle); + PR_PushIOLayer(fd, PR_TOP_IO_LAYER, top); + + top = fd; + middle = top->lower; + bottom = middle->lower; + + /* Verify that the higher pointers are correct. */ + if (middle->higher != top) { + fprintf(stderr, "middle->higher is wrong\n"); + fprintf(stderr, "FAILED\n"); + exit(1); + } + if (bottom->higher != middle) { + fprintf(stderr, "bottom->higher is wrong\n"); + fprintf(stderr, "FAILED\n"); + exit(1); + } + + top = PR_PopIOLayer(fd, topId); + top->dtor(top); + + middle = fd; + bottom = middle->lower; + + /* Verify that the higher pointer is correct. */ + if (bottom->higher != middle) { + fprintf(stderr, "bottom->higher is wrong\n"); + fprintf(stderr, "FAILED\n"); + exit(1); + } + + middle = PR_PopIOLayer(fd, middleId); + middle->dtor(middle); + if (fd->identity != bottomId) { + fprintf(stderr, "The bottom layer has the wrong identity\n"); + fprintf(stderr, "FAILED\n"); + exit(1); + } + fd->dtor(fd); + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/randseed.c b/nsprpub/pr/tests/randseed.c new file mode 100644 index 0000000000..2ca14c7af0 --- /dev/null +++ b/nsprpub/pr/tests/randseed.c @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: rngseed.c +** Description: +** Test NSPR's Random Number Seed generator +** +** Initial test: Just make sure it outputs some data. +** +** ... more? ... check some iterations to ensure it is random (no dupes) +** ... more? ... histogram distribution of random numbers +*/ + +#include "plgetopt.h" +#include "nspr.h" +#include "prrng.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* +** Test harness infrastructure +*/ +PRLogModuleInfo *lm; +PRLogModuleLevel msgLevel = PR_LOG_NONE; +PRIntn debug = 0; +PRUint32 failed_already = 0; +/* end Test harness infrastructure */ + +PRIntn optRandCount = 30; +char buf[40]; +PRSize bufSize = sizeof(buf); +PRSize rSize; +PRIntn i; + +/* +** Emit help text for this test +*/ +static void Help( void ) +{ + printf("Template: Help(): display your help message(s) here"); + exit(1); +} /* end Help() */ + +static void PrintRand( void *buf, PRIntn size ) +{ + PRUint32 *rp = buf; + PRIntn i; + + printf("%4.4d--\n", size ); + while (size > 0 ) { + switch( size ) { + case 1 : + printf("%2.2X\n", *(rp++) ); + size -= 4; + break; + case 2 : + printf("%4.4X\n", *(rp++) ); + size -= 4; + break; + case 3 : + printf("%6.6X\n", *(rp++) ); + size -= 4; + break; + default: + while ( size >= 4) { + PRIntn i = 3; + do { + printf("%8.8X ", *(rp++) ); + size -= 4; + } while( i-- ); + i = 3; + printf("\n"); + } + break; + } /* end switch() */ + } /* end while() */ +} /* end PrintRand() */ + + +int main(int argc, char **argv) +{ + { + /* + ** Get command line options + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdv"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug */ + debug = 1; + msgLevel = PR_LOG_ERROR; + break; + case 'v': /* verbose mode */ + msgLevel = PR_LOG_DEBUG; + break; + case 'h': /* help message */ + Help(); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } + + lm = PR_NewLogModule("Test"); /* Initialize logging */ + for ( i = 0; i < optRandCount ; i++ ) { + memset( buf, 0, bufSize ); + rSize = PR_GetRandomNoise( buf, bufSize ); + if (!rSize) { + fprintf(stderr, "Not implemented\n" ); + failed_already = PR_TRUE; + break; + } + if (debug) { + PrintRand( buf, rSize ); + } + } + + if (debug) { + printf("%s\n", (failed_already)? "FAIL" : "PASS"); + } + return( (failed_already == PR_TRUE )? 1 : 0 ); +} /* main() */ +/* end template.c */ + diff --git a/nsprpub/pr/tests/ranfile.c b/nsprpub/pr/tests/ranfile.c new file mode 100644 index 0000000000..a6dcc4ac41 --- /dev/null +++ b/nsprpub/pr/tests/ranfile.c @@ -0,0 +1,433 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Contact: AOF<freier@netscape.com> +** +** Name: ranfile.c +** +** Description: Test to hammer on various components of NSPR +** Modification History: +** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prthread.h" +#include "prlock.h" +#include "prcvar.h" +#include "prmem.h" +#include "prinrval.h" +#include "prio.h" + +#include <string.h> +#include <stdio.h> + +static PRIntn debug_mode = 0; +static PRIntn failed_already=0; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +typedef enum {sg_go, sg_stop, sg_done} Action; +typedef enum {sg_okay, sg_open, sg_close, sg_delete, sg_write, sg_seek} Problem; + +typedef struct Hammer_s { + PRLock *ml; + PRCondVar *cv; + PRUint32 id; + PRUint32 limit; + PRUint32 writes; + PRThread *thread; + PRIntervalTime timein; + Action action; + Problem problem; +} Hammer_t; + +#define DEFAULT_LIMIT 10 +#define DEFAULT_THREADS 2 +#define DEFAULT_LOOPS 1 + +static PRInt32 pageSize = 1024; +static const char* baseName = "./"; +static const char *programName = "Random File"; + +/*********************************************************************** +** PRIVATE FUNCTION: RandomNum +** DESCRIPTION: +** Generate a pseudo-random number +** INPUTS: None +** OUTPUTS: None +** RETURN: A pseudo-random unsigned number, 32-bits wide +** SIDE EFFECTS: +** Updates random seed (a static) +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: +** Uses the current interval timer value, promoted to a 64 bit +** float as a multiplier for a static residue (which begins +** as an uninitialized variable). The result is bits [16..48) +** of the product. Seed is then updated with the return value +** promoted to a float-64. +***********************************************************************/ +static PRUint32 RandomNum(void) +{ + PRUint32 rv; + PRUint64 shift; + static PRFloat64 seed = 0x58a9382; /* Just make sure it isn't 0! */ + PRFloat64 random = seed * (PRFloat64)PR_IntervalNow(); + LL_USHR(shift, *((PRUint64*)&random), 16); + LL_L2UI(rv, shift); + seed = (PRFloat64)rv; + return rv; +} /* RandomNum */ + +/*********************************************************************** +** PRIVATE FUNCTION: Thread +** DESCRIPTION: +** Hammer on the file I/O system +** INPUTS: A pointer to the thread's private data +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** Creates, accesses and deletes a file +** RESTRICTIONS: +** (Currently) must have file create permission in "/usr/tmp". +** MEMORY: NA +** ALGORITHM: +** This function is a root of a thread +** 1) Creates a (hopefully) unique file in /usr/tmp/ +** 2) Writes a zero to a random number of sequential pages +** 3) Closes the file +** 4) Reopens the file +** 5) Seeks to a random page within the file +** 6) Writes a one byte on that page +** 7) Repeat steps [5..6] for each page in the file +** 8) Close and delete the file +** 9) Repeat steps [1..8] until told to stop +** 10) Notify complete and return +***********************************************************************/ +static void PR_CALLBACK Thread(void *arg) +{ + PRUint32 index; + char filename[30]; + const char zero = 0; + PRFileDesc *file = NULL; + PRStatus rv = PR_SUCCESS; + Hammer_t *cd = (Hammer_t*)arg; + + (void)sprintf(filename, "%ssg%04ld.dat", baseName, cd->id); + + if (debug_mode) { + printf("Starting work on %s\n", filename); + } + + while (PR_TRUE) + { + PRUint32 bytes; + PRUint32 minor = (RandomNum() % cd->limit) + 1; + PRUint32 random = (RandomNum() % cd->limit) + 1; + PRUint32 pages = (RandomNum() % cd->limit) + 10; + while (minor-- > 0) + { + cd->problem = sg_okay; + if (cd->action != sg_go) { + goto finished; + } + cd->problem = sg_open; + file = PR_Open(filename, PR_RDWR|PR_CREATE_FILE, 0666); + if (file == NULL) { + goto finished; + } + for (index = 0; index < pages; index++) + { + cd->problem = sg_okay; + if (cd->action != sg_go) { + goto close; + } + cd->problem = sg_seek; + bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET); + if (bytes != pageSize * index) { + goto close; + } + cd->problem = sg_write; + bytes = PR_Write(file, &zero, sizeof(zero)); + if (bytes <= 0) { + goto close; + } + cd->writes += 1; + } + cd->problem = sg_close; + rv = PR_Close(file); + if (rv != PR_SUCCESS) { + goto purge; + } + + cd->problem = sg_okay; + if (cd->action != sg_go) { + goto purge; + } + + cd->problem = sg_open; + file = PR_Open(filename, PR_RDWR, 0666); + for (index = 0; index < pages; index++) + { + cd->problem = sg_okay; + if (cd->action != sg_go) { + goto close; + } + cd->problem = sg_seek; + bytes = PR_Seek(file, pageSize * index, PR_SEEK_SET); + if (bytes != pageSize * index) { + goto close; + } + cd->problem = sg_write; + bytes = PR_Write(file, &zero, sizeof(zero)); + if (bytes <= 0) { + goto close; + } + cd->writes += 1; + random = (random + 511) % pages; + } + cd->problem = sg_close; + rv = PR_Close(file); + if (rv != PR_SUCCESS) { + goto purge; + } + cd->problem = sg_delete; + rv = PR_Delete(filename); + if (rv != PR_SUCCESS) { + goto finished; + } + } + } + +close: + (void)PR_Close(file); +purge: + (void)PR_Delete(filename); +finished: + PR_Lock(cd->ml); + cd->action = sg_done; + PR_NotifyCondVar(cd->cv); + PR_Unlock(cd->ml); + + if (debug_mode) { + printf("Ending work on %s\n", filename); + } + + return; +} /* Thread */ + +static Hammer_t hammer[100]; +static PRCondVar *cv; +/*********************************************************************** +** PRIVATE FUNCTION: main +** DESCRIPTION: +** Hammer on the file I/O system +** INPUTS: The usual argc and argv +** argv[0] - program name (not used) +** argv[1] - the number of times to execute the major loop +** argv[2] - the number of threads to toss into the batch +** argv[3] - the clipping number applied to randoms +** default values: loops = 2, threads = 10, limit = 57 +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** Creates, accesses and deletes lots of files +** RESTRICTIONS: +** (Currently) must have file create permission in "/usr/tmp". +** MEMORY: NA +** ALGORITHM: +** 1) Fork a "Thread()" +** 2) Wait for 'interleave' seconds +** 3) For [0..'threads') repeat [1..2] +** 4) Mark all objects to stop +** 5) Collect the threads, accumulating the results +** 6) For [0..'loops') repeat [1..5] +** 7) Print accumulated results and exit +** +** Characteristic output (from IRIX) +** Random File: Using loops = 2, threads = 10, limit = 57 +** Random File: [min [avg] max] writes/sec average +***********************************************************************/ +int main(int argc, char **argv) +{ + PRLock *ml; + PRUint32 id = 0; + int active, poll; + PRIntervalTime interleave; + PRIntervalTime duration = 0; + int limit = 0, loops = 0, threads = 0, times; + PRUint32 writes, writesMin = 0x7fffffff, writesTot = 0, durationTot = 0, writesMax = 0; + + const char *where[] = {"okay", "open", "close", "delete", "write", "seek"}; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:t:i:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'G': /* global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'l': /* limiting number */ + limit = atoi(opt->value); + break; + case 't': /* number of threads */ + threads = atoi(opt->value); + break; + case 'i': /* iteration counter */ + loops = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + interleave = PR_SecondsToInterval(10); + + ml = PR_NewLock(); + cv = PR_NewCondVar(ml); + + if (loops == 0) { + loops = DEFAULT_LOOPS; + } + if (limit == 0) { + limit = DEFAULT_LIMIT; + } + if (threads == 0) { + threads = DEFAULT_THREADS; + } + + if (debug_mode) printf( + "%s: Using loops = %d, threads = %d, limit = %d and %s threads\n", + programName, loops, threads, limit, + (thread_scope == PR_LOCAL_THREAD) ? "LOCAL" : "GLOBAL"); + + for (times = 0; times < loops; ++times) + { + if (debug_mode) { + printf("%s: Setting concurrency level to %d\n", programName, times + 1); + } + PR_SetConcurrency(times + 1); + for (active = 0; active < threads; active++) + { + hammer[active].ml = ml; + hammer[active].cv = cv; + hammer[active].id = id++; + hammer[active].writes = 0; + hammer[active].action = sg_go; + hammer[active].problem = sg_okay; + hammer[active].limit = (RandomNum() % limit) + 1; + hammer[active].timein = PR_IntervalNow(); + hammer[active].thread = PR_CreateThread( + PR_USER_THREAD, Thread, &hammer[active], + PR_GetThreadPriority(PR_GetCurrentThread()), + thread_scope, PR_JOINABLE_THREAD, 0); + + PR_Lock(ml); + PR_WaitCondVar(cv, interleave); /* start new ones slowly */ + PR_Unlock(ml); + } + + /* + * The last thread started has had the opportunity to run for + * 'interleave' seconds. Now gather them all back in. + */ + PR_Lock(ml); + for (poll = 0; poll < threads; poll++) + { + if (hammer[poll].action == sg_go) { /* don't overwrite done */ + hammer[poll].action = sg_stop; /* ask him to stop */ + } + } + PR_Unlock(ml); + + while (active > 0) + { + for (poll = 0; poll < threads; poll++) + { + PR_Lock(ml); + while (hammer[poll].action < sg_done) { + PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(ml); + + active -= 1; /* this is another one down */ + (void)PR_JoinThread(hammer[poll].thread); + hammer[poll].thread = NULL; + if (hammer[poll].problem == sg_okay) + { + duration = PR_IntervalToMilliseconds( + PR_IntervalNow() - hammer[poll].timein); + writes = hammer[poll].writes * 1000 / duration; + if (writes < writesMin) { + writesMin = writes; + } + if (writes > writesMax) { + writesMax = writes; + } + writesTot += hammer[poll].writes; + durationTot += duration; + } + else if (debug_mode) printf( + "%s: test failed %s after %ld seconds\n", + programName, where[hammer[poll].problem], duration); + else { + failed_already=1; + } + } + } + } + if (debug_mode) printf( + "%s: [%ld [%ld] %ld] writes/sec average\n", + programName, writesMin, writesTot * 1000 / durationTot, writesMax); + + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + + if (failed_already) + { + printf("FAIL\n"); + return 1; + } + else + { + printf("PASS\n"); + return 0; + } +} /* main */ diff --git a/nsprpub/pr/tests/reinit.c b/nsprpub/pr/tests/reinit.c new file mode 100644 index 0000000000..b4dfa4d9b9 --- /dev/null +++ b/nsprpub/pr/tests/reinit.c @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This test verifies that NSPR can be cleaned up and reinitialized. */ + +#include "nspr.h" +#include <stdio.h> + +int main() +{ + PRStatus rv; + + fprintf(stderr, "Init 1\n"); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + fprintf(stderr, "Cleanup 1\n"); + rv = PR_Cleanup(); + if (rv != PR_SUCCESS) { + fprintf(stderr, "FAIL\n"); + return 1; + } + + fprintf(stderr, "Init 2\n"); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + fprintf(stderr, "Cleanup 2\n"); + rv = PR_Cleanup(); + if (rv != PR_SUCCESS) { + fprintf(stderr, "FAIL\n"); + return 1; + } + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/rmdir.c b/nsprpub/pr/tests/rmdir.c new file mode 100644 index 0000000000..c1400d7287 --- /dev/null +++ b/nsprpub/pr/tests/rmdir.c @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: rmdir.c +** Description: Demonstrate bugzilla 80884. +** +** after fix to unix_errors.c, message should report correct +** failure of PR_Rmdir(). +** +** +** +*/ + +#include <prio.h> +#include <stdio.h> +#include <prerror.h> +#include <prlog.h> +#include "plgetopt.h" + +#define DIRNAME "xxxBug80884/" +#define FILENAME "file80883" + +PRBool failed_already = PR_FALSE; +PRBool debug_mode = PR_FALSE; + +PRLogModuleInfo *lm; + +/* +** Help() -- print Usage information +*/ +static void Help( void ) { + fprintf(stderr, "template usage:\n" + "\t-d debug mode\n" + ); +} /* --- end Help() */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dh"); + PRFileDesc* fd; + PRErrorCode err; + + /* parse command line options */ + 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); + + lm = PR_NewLogModule( "testcase" ); + + (void) PR_MkDir( DIRNAME, 0777); + fd = PR_Open( DIRNAME FILENAME, PR_CREATE_FILE|PR_RDWR, 0666); + if (fd == 0) { + PRErrorCode err = PR_GetError(); + fprintf(stderr, "create file fails: %d: %s\n", err, + PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); + failed_already = PR_TRUE; + goto Finished; + } + + PR_Close(fd); + + if (PR_RmDir( DIRNAME ) == PR_SUCCESS) { + fprintf(stderr, "remove directory succeeds\n"); + failed_already = PR_TRUE; + goto Finished; + } + + err = PR_GetError(); + fprintf(stderr, "remove directory fails with: %d: %s\n", err, + PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); + + (void) PR_Delete( DIRNAME FILENAME); + (void) PR_RmDir( DIRNAME ); + + return 0; + +Finished: + if ( debug_mode ) { + printf("%s\n", ( failed_already ) ? "FAILED" : "PASS" ); + } + return( (failed_already)? 1 : 0 ); +} /* --- end main() */ +/* --- end template.c */ diff --git a/nsprpub/pr/tests/runtests.pl b/nsprpub/pr/tests/runtests.pl new file mode 100755 index 0000000000..326c08f862 --- /dev/null +++ b/nsprpub/pr/tests/runtests.pl @@ -0,0 +1,371 @@ +#!/usr/bin/perl +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use POSIX qw(:sys_wait_h); +use POSIX qw(setsid); +use FileHandle; + +# Constants +$WINOS = "MSWin32"; + +$osname = $^O; + +use Cwd; +if ($osname =~ $WINOS) { + # Windows + require Win32::Process; + require Win32; +} + +# Get environment variables. +$output_file = $ENV{NSPR_TEST_LOGFILE}; +$timeout = $ENV{TEST_TIMEOUT}; + +$timeout = 0 if (!defined($timeout)); + +sub getTime { + ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime(); + + $year = 1900 + $yearOffset; + + $theTime = sprintf("%04d-%02d-%02d %02d:%02d:%02d",$year,$month,$dayOfMonth,$hour,$minute,$second); + return $theTime; +} + +sub open_log { + + if (!defined($output_file)) { + print "No output file.\n"; + # null device + if ($osname =~ $WINOS) { + $output_file = "nul"; + } else { + $output_file = "/dev/null"; + } + } + + # use STDOUT for OF (to print summary of test results) + open(OF, ">&STDOUT") or die "Can't reuse STDOUT for OF\n"; + OF->autoflush; + # reassign STDOUT to $output_file (to print details of test results) + open(STDOUT, ">$output_file") or die "Can't open file $output_file for STDOUT\n"; + STDOUT->autoflush; + # redirect STDERR to STDOUT + open(STDERR, ">&STDOUT") or die "Can't redirect STDERR to STDOUT\n"; + STDERR->autoflush; + + # Print header test in summary + $now = getTime; + print OF "\nNSPR Test Results - tests\n"; + print OF "\nBEGIN\t\t\t$now\n"; + print OF "NSPR_TEST_LOGFILE\t$output_file\n"; + print OF "TEST_TIMEOUT\t$timeout\n\n"; + print OF "\nTest\t\t\tResult\n\n"; +} + +sub close_log { + # end of test marker in summary + $now = getTime; + print OF "END\t\t\t$now\n"; + + close(OF) or die "Can't close file OF\n"; + close(STDERR) or die "Can't close STDERR\n"; + close(STDOUT) or die "Can't close STDOUT\n"; +} + +sub print_begin { +$lprog = shift; + + # Summary output + print OF "$prog"; + # Full output + $now = getTime; + print "BEGIN TEST: $lprog ($now)\n\n"; +} + +sub print_end { +($lprog, $exit_status, $exit_signal, $exit_core) = @_; + + if (($exit_status == 0) && ($exit_signal == 0) && ($exit_core == 0)) { + $str_status = "Passed"; + } else { + $str_status = "FAILED"; + } + if ($exit_signal != 0) { + $str_signal = " - signal $exit_signal"; + } else { + $str_signal = ""; + } + if ($exit_core != 0) { + $str_core = " - core dumped"; + } else { + $str_core = ""; + } + $now = getTime; + # Full output + print "\nEND TEST: $lprog ($now)\n"; + print "TEST STATUS: $lprog = $str_status (exit status " . $exit_status . $str_signal . $str_core . ")\n"; + print "--------------------------------------------------\n\n"; + # Summary output + print OF "\t\t\t$str_status\n"; +} + +sub ux_start_prog { +# parameters: +$lprog = shift; # command to run + + # Create a process group for the child + # so we can kill all of it if needed + setsid or die "setsid failed: $!"; + # Start test program + exec("./$lprog"); + # We should not be here unless exec failed. + print "Faild to exec $lprog"; + exit 1 << 8; +} + +sub ux_wait_timeout { +# parameters: +$lpid = shift; # child process id +$ltimeout = shift; # timeout + + if ($ltimeout == 0) { + # No timeout: use blocking wait + $ret = waitpid($lpid,0); + # Exit and don't kill + $lstatus = $?; + $ltimeout = -1; + } else { + while ($ltimeout > 0) { + # Check status of child using non blocking wait + $ret = waitpid($lpid, WNOHANG); + if ($ret == 0) { + # Child still running + # print "Time left=$ltimeout\n"; + sleep 1; + $ltimeout--; + } else { + # Child has ended + $lstatus = $?; + # Exit the wait loop and don't kill + $ltimeout = -1; + } + } + } + + if ($ltimeout == 0) { + # we ran all the timeout: it's time to kill the child + print "Timeout ! Kill child process $lpid\n"; + # Kill the child process and group + kill(-9,$lpid); + $lstatus = 9; + } + + return $lstatus; +} + +sub ux_test_prog { +# parameters: +$prog = shift; # Program to test + + $child_pid = fork; + if ($child_pid == 0) { + # we are in the child process + print_begin($prog); + ux_start_prog($prog); + } else { + # we are in the parent process + $status = ux_wait_timeout($child_pid,$timeout); + # See Perlvar for documentation of $? + # exit status = $status >> 8 + # exit signal = $status & 127 (no signal = 0) + # core dump = $status & 128 (no core = 0) + print_end($prog, $status >> 8, $status & 127, $status & 128); + } + + return $status; +} + +sub win_path { +$lpath = shift; + + # MSYS drive letter = /c/ -> c:/ + $lpath =~ s/^\/(\w)\//$1:\//; + # Cygwin drive letter = /cygdrive/c/ -> c:/ + $lpath =~ s/^\/cygdrive\/(\w)\//$1:\//; + # replace / with \\ + $lpath =~ s/\//\\\\/g; + + return $lpath; +} + +sub win_ErrorReport{ + print Win32::FormatMessage( Win32::GetLastError() ); +} + +sub win_test_prog { +# parameters: +$prog = shift; # Program to test + + $status = 1; + $curdir = getcwd; + $curdir = win_path($curdir); + $prog_path = "$curdir\\$prog.exe"; + + print_begin($prog); + + Win32::Process::Create($ProcessObj, + "$prog_path", + "$prog", + 0, + NORMAL_PRIORITY_CLASS, + ".")|| die win_ErrorReport(); + $retwait = $ProcessObj->Wait($timeout * 1000); + + if ( $retwait == 0) { + # the prog didn't finish after the timeout: kill + $ProcessObj->Kill($status); + print "Timeout ! Process killed with exit status $status\n"; + } else { + # the prog finished before the timeout: get exit status + $ProcessObj->GetExitCode($status); + } + # There is no signal, no core on Windows + print_end($prog, $status, 0, 0); + + return $status +} + +# MAIN --------------- +@progs = ( +"abstract", +"accept", +"acceptread", +"acceptreademu", +"affinity", +"alarm", +"anonfm", +"atomic", +"attach", +"bigfile", +"cleanup", +"cltsrv", +"concur", +"cvar", +"cvar2", +"dlltest", +"dtoa", +"errcodes", +"exit", +"fdcach", +"fileio", +"foreign", +"formattm", +"fsync", +"gethost", +"getproto", +"i2l", +"initclk", +"inrval", +"instrumt", +"intrio", +"intrupt", +"io_timeout", +"ioconthr", +"join", +"joinkk", +"joinku", +"joinuk", +"joinuu", +"layer", +"lazyinit", +"libfilename", +"lltest", +"lock", +"lockfile", +"logfile", +"logger", +"many_cv", +"nameshm1", +"nblayer", +"nonblock", +"ntioto", +"ntoh", +"op_2long", +"op_excl", +"op_filnf", +"op_filok", +"op_nofil", +"parent", +"parsetm", +"peek", +"perf", +"pipeping", +"pipeping2", +"pipeself", +"poll_nm", +"poll_to", +"pollable", +"prftest", +"prfz", +"primblok", +"provider", +"prpollml", +"pushtop", +"ranfile", +"randseed", +"reinit", +"rwlocktest", +"sel_spd", +"selct_er", +"selct_nm", +"selct_to", +"selintr", +"sema", +"semaerr", +"semaping", +"sendzlf", +"server_test", +"servr_kk", +"servr_uk", +"servr_ku", +"servr_uu", +"short_thread", +"sigpipe", +"socket", +"sockopt", +"sockping", +"sprintf", +"stack", +"stdio", +"str2addr", +"strod", +"switch", +"system", +"testbit", +"testfile", +"threads", +"timemac", +"timetest", +"tpd", +"udpsrv", +"vercheck", +"version", +"writev", +"xnotify", +"zerolen"); + +open_log; + +foreach $current_prog (@progs) { + if ($osname =~ $WINOS) { + win_test_prog($current_prog); + } else { + ux_test_prog($current_prog); + } +} + +close_log; diff --git a/nsprpub/pr/tests/runtests.sh b/nsprpub/pr/tests/runtests.sh new file mode 100755 index 0000000000..8d3144019d --- /dev/null +++ b/nsprpub/pr/tests/runtests.sh @@ -0,0 +1,277 @@ +#!/bin/sh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if test -z $1 +then + echo "usage: $0 <path-to-dist>" + exit 1 +fi + +cd $1/lib +ABS_LIB=$PWD +cd - + +export DYLD_LIBRARY_PATH=${ABS_LIB}:${DYLD_LIBRARY_PATH} +export LD_LIBRARY_PATH=${ABS_LIB}:${LD_LIBRARY_PATH} +export PATH=${ABS_LIB}:${PATH} + +# +# runtests.sh +# Bourne shell script for nspr tests +# + +SYSTEM_INFO=`uname -a` +OS_ARCH=`uname -s` + +if [ $OS_ARCH = "Windows_NT" ] || [ $OS_ARCH = "OS/2" ] +then + NULL_DEVICE=nul +else + NULL_DEVICE=/dev/null + FILE_D=`ulimit -n` + if [ $FILE_D -lt 512 ] + then + ulimit -n 512 + fi +fi + +# +# Irrevelant tests +# +#bug1test - used to demonstrate a bug on NT +#bigfile2 - requires 4Gig file creation. See BugZilla #5451 +#bigfile3 - requires 4Gig file creation. See BugZilla #5451 +#dbmalloc - obsolete; originally for testing debug version of nspr's malloc +#dbmalloc1 - obsolete; originally for testing debug version of nspr's malloc +#depend - obsolete; used to test a initial spec for library dependencies +#dceemu - used to tests special functions in NSPR for DCE emulation +#ipv6 - IPV6 not in use by NSPR clients +#mbcs - tests use of multi-byte charset for filenames. See BugZilla #25140 +#io_timeoutk - obsolete; subsumed in io_timeout +#io_timeoutu - obsolete; subsumed in io_timeout +#prftest1 - obsolete; subsumed by prftest +#prftest2 - obsolete; subsumed by prftest +#prselect - obsolete; PR_Select is obsolete +#select2 - obsolete; PR_Select is obsolete +#sem - obsolete; PRSemaphore is obsolete +#stat - for OS2? +#suspend - private interfaces PR_SuspendAll, PR_ResumeAll, etc.. +#thruput - needs to be run manually as client/server +#time - used to measure time with native calls and nspr calls +#tmoacc - should be run with tmocon +#tmocon - should be run with tmoacc +#op_noacc - limited use +#yield - limited use for PR_Yield + +# +# Tests not run (but should) +# + +#forktest (failed on IRIX) +#multiwait - fails on Linux 64bit since NSPR v 4.4 from 2004. +#nbconn - fails on some platforms +#poll_er - fails on some platforms? limited use? +#prpoll - the bad-FD test needs to be moved to a different test +#sleep - specific to OS/2 +# +# all of the following were disabled in 2019 when reenabling CI tests, +# because they failed on at least one of the platforms: +# +# cltsrv +# cvar +# gethost +# getproto +# layer +# logfile +# nameshm1 +# nblayer +# nonblock +# ntioto +# op_2long +# parent +# provider +# ranfile +# socket +# sockopt +# vercheck + +#LOGFILE=${NSPR_TEST_LOGFILE:-$NULL_DEVICE} +LOGFILE=nspr-test.log + +# +# Tests run on all platforms +# + +TESTS=" +abstract +accept +acceptread +acceptreademu +affinity +alarm +anonfm +atomic +attach +bigfile +cleanup +concur +cvar2 +dlltest +dtoa +errcodes +exit +fdcach +fileio +foreign +formattm +fsync +i2l +initclk +inrval +instrumt +intrio +intrupt +io_timeout +ioconthr +join +joinkk +joinku +joinuk +joinuu +lazyinit +libfilename +lltest +lock +lockfile +logger +many_cv +ntoh +op_excl +op_filnf +op_filok +op_nofil +parsetm +peek +perf +pipeping +pipeping2 +pipeself +poll_nm +poll_to +pollable +prftest +prfz +primblok +prpollml +pushtop +randseed +reinit +rwlocktest +sel_spd +selct_er +selct_nm +selct_to +selintr +sema +semaerr +semaping +sendzlf +server_test +servr_kk +servr_uk +servr_ku +servr_uu +short_thread +sigpipe +sockping +sprintf +stack +stdio +str2addr +strod +switch +system +testbit +testfile +threads +timemac +timetest +tpd +udpsrv +version +writev +xnotify +zerolen" + +rval=0 + + +# +# When set, value of the environment variable TEST_TIMEOUT is the maximum +# time (secs) allowed for a test program beyond which it is terminated. +# If TEST_TIMEOUT is not set or if it's value is 0, then test programs +# don't timeout. +# +# Running runtests.ksh under MKS toolkit on NT, 95, 98 does not cause +# timeout detection correctly. For these platforms, do not attempt timeout +# test. (lth). +# +# + +OS_PLATFORM=`uname` +OBJDIR=`basename $PWD` +printf "\nNSPR Test Results - $OBJDIR\n\n" +printf "BEGIN\t\t\t`date`\n" +printf "NSPR_TEST_LOGFILE\t${LOGFILE}\n\n" +printf "Test\t\t\tResult\n\n" +if [ $OS_PLATFORM = "Windows_95" ] || [ $OS_PLATFORM = "Windows_98" ] || [ $OS_PLATFORM = "Windows_NT" ] || [ $OS_PLATFORM = "OS/2" ] ; then + for prog in $TESTS + do + printf "$prog (`date +%T`)" + printf "\nBEGIN TEST: $prog\n\n" >> ${LOGFILE} 2>&1 + ./$prog >> ${LOGFILE} 2>&1 + if [ 0 = $? ] ; then + printf "\t\t\tPassed\n"; + else + printf "\t\t\tFAILED\n"; + rval=1 + fi; + printf "\nEND TEST: $prog\n\n" >> ${LOGFILE} 2>&1 + done +else + for prog in $TESTS + do + printf "$prog (`date +%T`)" + printf "\nBEGIN TEST: $prog\n\n" >> ${LOGFILE} 2>&1 + export test_rval + ./$prog >> ${LOGFILE} 2>&1 & + test_pid=$! + sleep_pid=0 + if test -n "$TEST_TIMEOUT" && test "$TEST_TIMEOUT" -gt 0 + then + (sleep $TEST_TIMEOUT; kill $test_pid >/dev/null 2>&1 ) & + sleep_pid=$! + fi + wait $test_pid + test_rval=$? + [ $sleep_pid -eq 0 ] || kill $sleep_pid >/dev/null 2>&1 + if [ 0 = $test_rval ] ; then + printf "\t\t\tPassed\n"; + else + printf "\t\t\tFAILED\n"; + rval=1 + fi; + printf "\nEND TEST: $prog\n\n" >> ${LOGFILE} 2>&1 + done +fi; + +if [ $rval -ne 0 ]; then + cat ${LOGFILE} +fi + +printf "END\t\t\t`date`\n" +exit $rval + diff --git a/nsprpub/pr/tests/runy2ktests.ksh b/nsprpub/pr/tests/runy2ktests.ksh new file mode 100644 index 0000000000..56f7d3c5e3 --- /dev/null +++ b/nsprpub/pr/tests/runy2ktests.ksh @@ -0,0 +1,237 @@ +#!/bin/ksh +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# 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" ] || [ $OS_ARCH = "Windows_95" ] +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"` + ;; + Windows_95) + 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"` + ;; + Windows_95) + 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 + ;; + Windows_95) + 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/nsprpub/pr/tests/rwlockrank.c b/nsprpub/pr/tests/rwlockrank.c new file mode 100644 index 0000000000..d77eb9680a --- /dev/null +++ b/nsprpub/pr/tests/rwlockrank.c @@ -0,0 +1,122 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * RWLock rank tests + */ + +#include "nspr.h" +#include "plgetopt.h" + +static int _debug_on; +static PRRWLock *rwlock0; +static PRRWLock *rwlock1; +static PRRWLock *rwlock2; + +static void rwtest(void *args) +{ + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Unlock(rwlock1); + + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Unlock(rwlock1); + + /* Test correct lock rank. */ + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Rlock(rwlock2); + PR_RWLock_Unlock(rwlock2); + PR_RWLock_Unlock(rwlock1); + + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Rlock(rwlock2); + PR_RWLock_Unlock(rwlock1); + PR_RWLock_Unlock(rwlock2); + + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Rlock(rwlock0); + PR_RWLock_Rlock(rwlock2); + PR_RWLock_Unlock(rwlock2); + PR_RWLock_Unlock(rwlock0); + PR_RWLock_Unlock(rwlock1); + +#if 0 + /* Test incorrect lock rank. */ + PR_RWLock_Rlock(rwlock2); + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Unlock(rwlock1); + PR_RWLock_Unlock(rwlock2); + + PR_RWLock_Rlock(rwlock2); + PR_RWLock_Rlock(rwlock0); + PR_RWLock_Rlock(rwlock1); + PR_RWLock_Unlock(rwlock1); + PR_RWLock_Unlock(rwlock0); + PR_RWLock_Unlock(rwlock2); +#endif +} + +int main(int argc, char **argv) +{ + PRStatus rc; + PRThread *thread; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) { + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + rwlock0 = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "Lock 0"); + if (rwlock0 == NULL) { + fprintf(stderr, "PR_NewRWLock failed - error %d\n", + (int)PR_GetError()); + return 1; + } + rwlock1 = PR_NewRWLock(1, "Lock 1"); + if (rwlock1 == NULL) { + fprintf(stderr, "PR_NewRWLock failed - error %d\n", + (int)PR_GetError()); + return 1; + } + rwlock2 = PR_NewRWLock(2, "Lock 2"); + if (rwlock2 == NULL) { + fprintf(stderr, "PR_NewRWLock failed - error %d\n", + (int)PR_GetError()); + return 1; + } + + thread = PR_CreateThread(PR_USER_THREAD, rwtest, NULL, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (thread == NULL) { + fprintf(stderr, "PR_CreateThread failed - error %d\n", + (int)PR_GetError()); + PR_ProcessExit(2); + } + if (_debug_on) { + printf("%s: created thread = %p\n", argv[0], thread); + } + + rc = PR_JoinThread(thread); + PR_ASSERT(rc == PR_SUCCESS); + + PR_DestroyRWLock(rwlock0); + rwlock0 = NULL; + PR_DestroyRWLock(rwlock1); + rwlock1 = NULL; + PR_DestroyRWLock(rwlock2); + rwlock2 = NULL; + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/rwlocktest.c b/nsprpub/pr/tests/rwlocktest.c new file mode 100644 index 0000000000..396103c341 --- /dev/null +++ b/nsprpub/pr/tests/rwlocktest.c @@ -0,0 +1,213 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * RWLock tests + * + * Several threads are created to access and modify data arrays using + * PRRWLocks for synchronization. Two data arrays, array_A and array_B, are + * initialized with random data and a third array, array_C, is initialized + * with the sum of the first 2 arrays. + * + * Each one of the threads acquires a read lock to verify that the sum of + * the arrays A and B is equal to array C, and acquires a write lock to + * consistently update arrays A and B so that their is equal to array C. + * + */ + +#include "nspr.h" +#include "plgetopt.h" +#include "prrwlock.h" + +static int _debug_on; +static void rwtest(void *args); +static PRInt32 *array_A,*array_B,*array_C; +static void update_array(void); +static void check_array(void); + +typedef struct thread_args { + PRRWLock *rwlock; + PRInt32 loop_cnt; +} thread_args; + +PRFileDesc *output; +PRFileDesc *errhandle; + +#define DEFAULT_THREAD_CNT 4 +#define DEFAULT_LOOP_CNT 100 +#define TEST_ARRAY_SIZE 100 + +int main(int argc, char **argv) +{ + PRInt32 cnt; + PRStatus rc; + PRInt32 i; + + PRInt32 thread_cnt = DEFAULT_THREAD_CNT; + PRInt32 loop_cnt = DEFAULT_LOOP_CNT; + PRThread **threads; + thread_args *params; + PRRWLock *rwlock1; + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + case 't': /* thread count */ + thread_cnt = atoi(opt->value); + break; + case 'c': /* loop count */ + loop_cnt = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_SetConcurrency(4); + + output = PR_GetSpecialFD(PR_StandardOutput); + errhandle = PR_GetSpecialFD(PR_StandardError); + + rwlock1 = PR_NewRWLock(0,"Lock 1"); + if (rwlock1 == NULL) { + PR_fprintf(errhandle, "PR_NewRWLock failed - error %d\n", + PR_GetError()); + return 1; + } + + threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt); + params = (thread_args *) PR_CALLOC(sizeof(thread_args) * thread_cnt); + + /* + * allocate and initialize data arrays + */ + array_A =(PRInt32 *) PR_MALLOC(sizeof(PRInt32) * TEST_ARRAY_SIZE); + array_B =(PRInt32 *) PR_MALLOC(sizeof(PRInt32) * TEST_ARRAY_SIZE); + array_C =(PRInt32 *) PR_MALLOC(sizeof(PRInt32) * TEST_ARRAY_SIZE); + cnt = 0; + for (i=0; i < TEST_ARRAY_SIZE; i++) { + array_A[i] = cnt++; + array_B[i] = cnt++; + array_C[i] = array_A[i] + array_B[i]; + } + + if (_debug_on) + PR_fprintf(output,"%s: thread_cnt = %d loop_cnt = %d\n", argv[0], + thread_cnt, loop_cnt); + for(cnt = 0; cnt < thread_cnt; cnt++) { + PRThreadScope scope; + + params[cnt].rwlock = rwlock1; + params[cnt].loop_cnt = loop_cnt; + + /* + * create LOCAL and GLOBAL threads alternately + */ + if (cnt & 1) { + scope = PR_LOCAL_THREAD; + } + else { + scope = PR_GLOBAL_THREAD; + } + + threads[cnt] = PR_CreateThread(PR_USER_THREAD, + rwtest, ¶ms[cnt], + PR_PRIORITY_NORMAL, + scope, + PR_JOINABLE_THREAD, + 0); + if (threads[cnt] == NULL) { + PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n", + PR_GetError()); + PR_ProcessExit(2); + } + if (_debug_on) + PR_fprintf(output,"%s: created thread = %p\n", argv[0], + threads[cnt]); + } + + for(cnt = 0; cnt < thread_cnt; cnt++) { + rc = PR_JoinThread(threads[cnt]); + PR_ASSERT(rc == PR_SUCCESS); + + } + + PR_DELETE(threads); + PR_DELETE(params); + + PR_DELETE(array_A); + PR_DELETE(array_B); + PR_DELETE(array_C); + + PR_DestroyRWLock(rwlock1); + + + printf("PASS\n"); + return 0; +} + +static void rwtest(void *args) +{ + PRInt32 index; + thread_args *arg = (thread_args *) args; + + + for (index = 0; index < arg->loop_cnt; index++) { + + /* + * verify sum, update arrays and verify sum again + */ + + PR_RWLock_Rlock(arg->rwlock); + check_array(); + PR_RWLock_Unlock(arg->rwlock); + + PR_RWLock_Wlock(arg->rwlock); + update_array(); + PR_RWLock_Unlock(arg->rwlock); + + PR_RWLock_Rlock(arg->rwlock); + check_array(); + PR_RWLock_Unlock(arg->rwlock); + } + if (_debug_on) + PR_fprintf(output, + "Thread[0x%x] lock = 0x%x exiting\n", + PR_GetCurrentThread(), arg->rwlock); + +} + +static void check_array(void) +{ + PRInt32 i; + + for (i=0; i < TEST_ARRAY_SIZE; i++) + if (array_C[i] != (array_A[i] + array_B[i])) { + PR_fprintf(output, "Error - data check failed\n"); + PR_ProcessExit(1); + } +} + +static void update_array(void) +{ + PRInt32 i; + + for (i=0; i < TEST_ARRAY_SIZE; i++) { + array_A[i] += i; + array_B[i] -= i; + } +} diff --git a/nsprpub/pr/tests/sel_spd.c b/nsprpub/pr/tests/sel_spd.c new file mode 100644 index 0000000000..6e3f66fedd --- /dev/null +++ b/nsprpub/pr/tests/sel_spd.c @@ -0,0 +1,534 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test the speed of select within NSPR + * + */ + +#include "nspr.h" +#include "prpriv.h" + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#if defined(XP_UNIX) +#include <unistd.h> +#endif + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define PORT_BASE 19000 PORT_INC_DO PORT_INC_3264 + +typedef struct timer_slot_t { + unsigned long d_connect; + unsigned long d_cl_data; + unsigned long d_sv_data; + unsigned long d_close; + unsigned long d_total; + unsigned long requests; +} timer_slot_t; + +static long _iterations = 5; +static long _client_data = 8192; + +static long _server_data = (128*1024); +static long _threads_max = 10, _threads = 10; + +static int verbose=0; +static PRMonitor *exit_cv; +static long _thread_exit_count; +static timer_slot_t *timer_data; +static PRThreadScope scope1, scope2; + +void tally_results(int); + +/* return the diff in microseconds */ +unsigned long _delta(PRIntervalTime *start, PRIntervalTime *stop) +{ + /* + * Will C do the right thing with unsigned arithemtic? + */ + return PR_IntervalToMicroseconds(*stop - *start); +} + +int _readn(PRFileDesc *sock, char *buf, int len) +{ + int rem; + int bytes; + + for (rem=len; rem; rem -= bytes) { + bytes = PR_Recv(sock, buf+len-rem, rem, 0, PR_INTERVAL_NO_TIMEOUT); + if (bytes <= 0) { + return -1; + } + } + return len; +} + +void +_thread_exit(int id) +{ + PR_EnterMonitor(exit_cv); +#ifdef DEBUG + fprintf(stdout, "Thread %d EXIT\n", id); +#endif + + _thread_exit_count--; + if (_thread_exit_count == 0) { +#ifdef DEBUG + fprintf(stdout, "Thread %d EXIT triggered notify\n", id); +#endif + PR_Notify(exit_cv); + } + PR_ExitMonitor(exit_cv); +} + +void +_server_thread(void *arg_id) +{ + void _client_thread(void *); + int *id = (int *)arg_id; + PRFileDesc *sock; + PRSocketOptionData sockopt; + PRNetAddr sa; + PRFileDesc * newsock; + char *data_buffer = NULL; + int data_buffer_size; + int index; + PRIntervalTime start, + connect_done, + read_done, + write_done, + close_done; + + +#ifdef DEBUG + fprintf(stdout, "server thread %d alive\n", *id); +#endif + + data_buffer_size = (_client_data>_server_data?_client_data:_server_data); + + if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL ) { + fprintf(stderr, "Error creating buffer in server thread %d\n", *id); + goto done; + } + + + if ( (sock = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Error creating socket in server thread %d\n", *id); + goto done; + } + + sockopt.option = PR_SockOpt_Reuseaddr; + sockopt.value.reuse_addr = PR_TRUE; + if ( PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) { + fprintf(stderr, "Error setting socket option in server thread %d\n", *id); + goto done; + } + + memset(&sa, 0, sizeof(sa)); + sa.inet.family = PR_AF_INET; + sa.inet.port = PR_htons(PORT_BASE + *id); + sa.inet.ip = PR_htonl(PR_INADDR_ANY); + + if ( PR_Bind(sock, &sa) < 0) { + fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", *id, errno); + goto done; + } + + if ( PR_Listen(sock, 32) < 0 ) { + fprintf(stderr, "Error listening to socket in server thread %d\n", *id); + goto done; + } + + /* Tell the client to start */ + if ( PR_CreateThread(PR_USER_THREAD, + _client_thread, + id, + PR_PRIORITY_NORMAL, + scope2, + PR_UNJOINABLE_THREAD, + 0) == NULL) { + fprintf(stderr, "Error creating client thread %d\n", *id); + } + + for (index = 0; index< _iterations; index++) { + +#ifdef DEBUG + fprintf(stdout, "server thread %d loop %d\n", *id, index); +#endif + + start = PR_IntervalNow(); + + if ( (newsock = PR_Accept(sock, &sa, + PR_INTERVAL_NO_TIMEOUT)) == NULL) { + fprintf(stderr, "Error accepting connection %d in server thread %d\n", + index, *id); + goto done; + } +#ifdef DEBUG + fprintf(stdout, "server thread %d got connection %d\n", *id, newsock); +#endif + + + connect_done = PR_IntervalNow(); + + if ( _readn(newsock, data_buffer, _client_data) < _client_data) { + fprintf(stderr, "Error reading client data for iteration %d in server thread %d\n", index, *id ); + goto done; + } + +#ifdef DEBUG + fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data); +#endif + read_done = PR_IntervalNow(); + + if ( PR_Send(newsock, data_buffer, _server_data, 0, + PR_INTERVAL_NO_TIMEOUT) < _server_data) { + fprintf(stderr, "Error sending client data for iteration %d in server thread %d\n", index, *id ); + goto done; + } + +#ifdef DEBUG + fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data); +#endif + + write_done = PR_IntervalNow(); + + PR_Close(newsock); + + close_done = PR_IntervalNow(); + + timer_data[2*(*id)].d_connect += _delta(&start, &connect_done); + timer_data[2*(*id)].d_cl_data += _delta(&connect_done, &read_done); + timer_data[2*(*id)].d_sv_data += _delta(&read_done, &write_done); + timer_data[2*(*id)].d_close += _delta(&write_done, &close_done); + timer_data[2*(*id)].d_total += _delta(&start, &close_done); + timer_data[2*(*id)].requests++; + + +#ifdef DEBUG + fprintf(stdout, "server: %d %d %d %d %d\n", + _delta(&start, &connect_done), _delta(&connect_done, &read_done), + _delta(&read_done, &write_done), _delta(&write_done, &close_done), + _delta(&start, &close_done)); +#endif + } + +done: + if (data_buffer != NULL) { + PR_Free (data_buffer); + } + if (sock) { + PR_Close(sock); + } + _thread_exit(*id); + return; +} + +void +_client_thread(void *arg_id) +{ + int *id = (int *)arg_id; + int index; + PRNetAddr sa; + PRFileDesc *sock_h; + char *data_buffer = NULL; + int data_buffer_size; + int bytes; + PRIntervalTime start, + connect_done, + read_done, + write_done, + close_done; + PRStatus rv; + +#ifdef DEBUG + fprintf(stdout, "client thread %d alive\n", *id); +#endif + + data_buffer_size = (_client_data>_server_data?_client_data:_server_data); + + if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL) { + fprintf(stderr, "Error creating buffer in server thread %d\n", *id); + goto done; + } + + memset(&sa, 0, sizeof(sa)); + rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa); + PR_ASSERT(PR_SUCCESS == rv); + + for (index = 0; index< _iterations; index++) { + +#ifdef DEBUG + fprintf(stdout, "client thread %d loop %d\n", *id, index); +#endif + + start = PR_IntervalNow(); + if ( (sock_h = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Error creating socket %d in client thread %d\n", + index, *id); + goto done; + } + +#ifdef DEBUG + fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h); +#endif + + if ( PR_Connect(sock_h, &sa, + PR_INTERVAL_NO_TIMEOUT) < 0) { + fprintf(stderr, "Error accepting connection %d in client thread %d\n", + index, *id); + goto done; + } + +#ifdef DEBUG + fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h); +#endif + + connect_done = PR_IntervalNow(); + if ( PR_Send(sock_h, data_buffer, _client_data, 0, + PR_INTERVAL_NO_TIMEOUT) < _client_data) { + fprintf(stderr, "Error sending client data for iteration %d in client thread %d\n", index, *id ); + goto done; + } + +#ifdef DEBUG + fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data); +#endif + + write_done = PR_IntervalNow(); + if ( (bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) { + fprintf(stderr, "Error reading server data for iteration %d in client thread %d (read %d bytes)\n", index, *id, bytes ); + goto done; + } + +#ifdef DEBUG + fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data); +#endif + + read_done = PR_IntervalNow(); + PR_Close(sock_h); + close_done = PR_IntervalNow(); + + timer_data[2*(*id)+1].d_connect += _delta(&start, &connect_done); + timer_data[2*(*id)+1].d_cl_data += _delta(&connect_done, &write_done); + timer_data[2*(*id)+1].d_sv_data += _delta(&write_done, &read_done); + timer_data[2*(*id)+1].d_close += _delta(&read_done, &close_done); + timer_data[2*(*id)+1].d_total += _delta(&start, &close_done); + timer_data[2*(*id)+1].requests++; + } +done: + if (data_buffer != NULL) { + PR_Free (data_buffer); + } + _thread_exit(*id); + + return; +} + +static +void do_work(void) +{ + int index; + + _thread_exit_count = _threads * 2; + for (index=0; index<_threads; index++) { + int *id = (int *)PR_Malloc(sizeof(int)); + + *id = index; + + if ( PR_CreateThread(PR_USER_THREAD, + _server_thread, + id, + PR_PRIORITY_NORMAL, + scope1, + PR_UNJOINABLE_THREAD, + 0) == NULL) { + fprintf(stderr, "Error creating server thread %d\n", index); + } + } + + PR_EnterMonitor(exit_cv); + while (_thread_exit_count > 0) { + PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(exit_cv); + + fprintf(stdout, "TEST COMPLETE!\n"); + + tally_results(verbose); + +} + +static void do_workUU(void) +{ + scope1 = PR_LOCAL_THREAD; + scope2 = PR_LOCAL_THREAD; + do_work(); +} + +static void do_workUK(void) +{ + scope1 = PR_LOCAL_THREAD; + scope2 = PR_GLOBAL_THREAD; + do_work(); +} + +static void do_workKU(void) +{ + scope1 = PR_GLOBAL_THREAD; + scope2 = PR_LOCAL_THREAD; + do_work(); +} + +static void do_workKK(void) +{ + scope1 = PR_GLOBAL_THREAD; + scope2 = PR_GLOBAL_THREAD; + do_work(); +} + + + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + printf("%40s: %6.2f usec\n", msg, d / _iterations); +} + + +int main(int argc, char **argv) +{ +#if defined(XP_UNIX) || defined(XP_OS2) + int opt; + PR_IMPORT_DATA(char *) optarg; +#endif + +#if defined(XP_UNIX) || defined(XP_OS2) + while ( (opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) { + switch(opt) { + case 'i': + _iterations = atoi(optarg); + break; + case 't': + _threads_max = _threads = atoi(optarg); + break; + case 'c': + _client_data = atoi(optarg); + break; + case 's': + _server_data = atoi(optarg); + break; + case 'v': + verbose = 1; + break; + default: + break; + } + } +#endif + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + fprintf(stdout, "Running test for %d iterations with %d simultaneous threads.\n", + _iterations, _threads); + fprintf(stdout, "\tWill send %d bytes of client data and %d bytes of server data\n", + _client_data, _server_data); + + if ( (exit_cv = PR_NewMonitor()) == NULL) { + fprintf(stderr, "Error creating monitor for exit cv\n"); + } + if ( (timer_data = (timer_slot_t *)PR_Malloc(2*_threads * sizeof(timer_slot_t))) == NULL) { + fprintf(stderr, "error allocating thread time results array\n"); + } + memset(timer_data, 0, 2*_threads*sizeof(timer_slot_t)); + + Measure(do_workUU, "select loop user/user"); + Measure(do_workUK, "select loop user/kernel"); + Measure(do_workKU, "select loop kernel/user"); + Measure(do_workKK, "select loop kernel/kernel"); + + + return 0; +} + +void +tally_results(int verbose) +{ + int index; + unsigned long tot_connect = 0; + unsigned long tot_cl_data = 0; + unsigned long tot_sv_data = 0; + unsigned long tot_close = 0; + unsigned long tot_all = 0; + unsigned long tot_requests = 0; + + fprintf(stdout, "Server results:\n\n"); + for (index=0; index<_threads_max*2; index+=2) { + + if (verbose) + fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", + index, timer_data[index].requests, timer_data[index].d_connect, + timer_data[index].d_cl_data, timer_data[index].d_sv_data, + timer_data[index].d_close, timer_data[index].d_total); + + tot_connect += timer_data[index].d_connect / _threads; + tot_cl_data += timer_data[index].d_cl_data / _threads; + tot_sv_data += timer_data[index].d_sv_data / _threads; + tot_close += timer_data[index].d_close / _threads; + tot_all += timer_data[index].d_total / _threads; + tot_requests += timer_data[index].requests / _threads; + } + fprintf(stdout, "----------\n"); + fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n", + tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close); + fprintf(stdout, "server per thread elapsed time %u\n", tot_all); + fprintf(stdout, "----------\n"); + + tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 0; + fprintf(stdout, "Client results:\n\n"); + for (index=1; index<_threads_max*2; index+=2) { + + if (verbose) + fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", + index, timer_data[index].requests, timer_data[index].d_connect, + timer_data[index].d_cl_data, timer_data[index].d_sv_data, + timer_data[index].d_close, timer_data[index].d_total); + + tot_connect += timer_data[index].d_connect / _threads; + tot_cl_data += timer_data[index].d_cl_data / _threads; + tot_sv_data += timer_data[index].d_sv_data / _threads; + tot_close += timer_data[index].d_close / _threads; + tot_all += timer_data[index].d_total / _threads; + tot_requests += timer_data[index].requests / _threads; + } + fprintf(stdout, "----------\n"); + fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n", + tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close); + fprintf(stdout, "client per thread elapsed time %u\n", tot_all); +} + diff --git a/nsprpub/pr/tests/selct_er.c b/nsprpub/pr/tests/selct_er.c new file mode 100644 index 0000000000..0c5c46e08d --- /dev/null +++ b/nsprpub/pr/tests/selct_er.c @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1997 - Netscape Communications Corporation +** +** Name: prselect_err.c +** +** Description: tests PR_Select with sockets Error condition functions. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "primpl.h" +#include "pprio.h" +#include "prnetdb.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + + +PRIntn failed_already=0; +PRIntn debug_mode; + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1, *listenSock2; + PRFileDesc *badFD; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + PR_fd_set readFdSet; + char buf[128]; + PRInt32 retVal; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Select with sockets. Error\n"); + printf("reporting operations are tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = AF_INET; + addr.inet.ip = PR_htonl(INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + if (debug_mode) { + printf("%s", buf); + } + + /* Set up the fd set */ + PR_FD_ZERO(&readFdSet); + PR_FD_SET(listenSock1, &readFdSet); + PR_FD_SET(listenSock2, &readFdSet); + + + /* Testing bad fd */ + if (debug_mode) { + printf("PR_Select should detect a bad file descriptor\n"); + } + if ((badFD = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a TCP socket\n"); + failed_already=1; + goto exit_now; + } + + PR_FD_SET(badFD, &readFdSet); + /* + * Make the fd invalid + */ +#if defined(XP_UNIX) + close(PR_FileDesc2NativeHandle(badFD)); +#elif defined(XP_OS2) + soclose(PR_FileDesc2NativeHandle(badFD)); +#elif defined(WIN32) || defined(WIN16) + closesocket(PR_FileDesc2NativeHandle(badFD)); +#else +#error "Unknown architecture" +#endif + + retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, + PR_INTERVAL_NO_TIMEOUT); + if (retVal != -1 || PR_GetError() != PR_BAD_DESCRIPTOR_ERROR) { + fprintf(stderr, "Failed to detect the bad fd: " + "PR_Select returns %d\n", retVal); + if (retVal == -1) { + fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(), + PR_GetOSError()); + failed_already=1; + } + goto exit_now; + } + if (debug_mode) { + printf("PR_Select detected a bad fd. Test passed.\n\n"); + } + PR_FD_CLR(badFD, &readFdSet); + + PR_Cleanup(); + goto exit_now; +exit_now: + if(failed_already) { + return 1; + } + else { + return 0; + } + +} + diff --git a/nsprpub/pr/tests/selct_nm.c b/nsprpub/pr/tests/selct_nm.c new file mode 100644 index 0000000000..a698f0fa42 --- /dev/null +++ b/nsprpub/pr/tests/selct_nm.c @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1997 - Netscape Communications Corporation +** +** Name: prselect_norm.c +** +** Description: tests PR_Select with sockets - Normal operations. +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prio.h" +#include "prlog.h" +#include "prprf.h" +#include "prerror.h" +#include "prnetdb.h" + +#include "obsolete/probslet.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +static void +clientThreadFunc(void *arg) +{ + PRUintn port = (PRUintn) arg; + PRFileDesc *sock; + PRNetAddr addr; + char buf[128]; + int i; + + addr.inet.family = PR_AF_INET; + addr.inet.port = PR_htons((PRUint16)port); + addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + PR_snprintf(buf, sizeof(buf), "%hu", addr.inet.port); + + for (i = 0; i < 5; i++) { + sock = PR_NewTCPSocket(); + PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT); + PR_Write(sock, buf, sizeof(buf)); + PR_Close(sock); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1, *listenSock2; + PRFileDesc *fds0[10], *fds1[10], **fds, **other_fds; + PRIntn nfds; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + PR_fd_set readFdSet; + char buf[128]; + PRThread *clientThread; + PRInt32 retVal; + PRIntn i, j; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Select with sockets. \n"); + printf(" Normal operation are tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + if (debug_mode) { + printf("%s", buf); + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort1, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + failed_already=1; + goto exit_now; + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + clientThreadFunc, (void *) listenPort2, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (clientThread == NULL) { + fprintf(stderr, "can't create thread\n"); + failed_already=1; + goto exit_now; + } + + if (debug_mode) { + printf("Two client threads are created. Each of them will\n"); + printf("send data to one of the two ports the server is listening on.\n"); + printf("The data they send is the port number. Each of them send\n"); + printf("the data five times, so you should see ten lines below,\n"); + printf("interleaved in an arbitrary order.\n"); + } + /* set up the fd array */ + fds = fds0; + other_fds = fds1; + fds[0] = listenSock1; + fds[1] = listenSock2; + nfds = 2; + /* Set up the fd set */ + PR_FD_ZERO(&readFdSet); + PR_FD_SET(listenSock1, &readFdSet); + PR_FD_SET(listenSock2, &readFdSet); + + /* 20 events total */ + i = 0; + while (i < 20) { + PRFileDesc **tmp; + int nextIndex; + int nEvents = 0; + + retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, + PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(retVal != 0); /* no timeout */ + if (retVal == -1) { + fprintf(stderr, "PR_Select failed (%d, %d)\n", PR_GetError(), + PR_GetOSError()); + failed_already=1; + goto exit_now; + } + + nextIndex = 2; + /* the two listening sockets */ + for (j = 0; j < 2; j++) { + other_fds[j] = fds[j]; + if (PR_FD_ISSET(fds[j], &readFdSet)) { + PRFileDesc *sock; + + nEvents++; + sock = PR_Accept(fds[j], NULL, PR_INTERVAL_NO_TIMEOUT); + if (sock == NULL) { + fprintf(stderr, "PR_Accept() failed\n"); + failed_already=1; + goto exit_now; + } + other_fds[nextIndex] = sock; + PR_FD_SET(sock, &readFdSet); + nextIndex++; + } + PR_FD_SET(fds[j], &readFdSet); + } + + for (j = 2; j < nfds; j++) { + if (PR_FD_ISSET(fds[j], &readFdSet)) { + PRInt32 nBytes; + + PR_FD_CLR(fds[j], &readFdSet); + nEvents++; + nBytes = PR_Read(fds[j], buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read() failed\n"); + failed_already=1; + goto exit_now; + } + /* Just to be safe */ + buf[127] = '\0'; + PR_Close(fds[j]); + if (debug_mode) { + printf("The server received \"%s\" from a client\n", buf); + } + } else { + PR_FD_SET(fds[j], &readFdSet); + other_fds[nextIndex] = fds[j]; + nextIndex++; + } + } + + PR_ASSERT(retVal == nEvents); + /* swap */ + tmp = fds; + fds = other_fds; + other_fds = tmp; + nfds = nextIndex; + i += nEvents; + } + + if (debug_mode) { + printf("Test passed\n"); + } + + PR_Cleanup(); + goto exit_now; +exit_now: + if(failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/selct_to.c b/nsprpub/pr/tests/selct_to.c new file mode 100644 index 0000000000..76ac99b2e1 --- /dev/null +++ b/nsprpub/pr/tests/selct_to.c @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** 1997 - Netscape Communications Corporation +** +** Name: prselect_to.c +** +** Description: tests PR_Select with sockets. Time out functions +** +** Modification History: +** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prio.h" +#include "prlog.h" +#include "prprf.h" +#include "prnetdb.h" + +#include "obsolete/probslet.h" + +#include "prerror.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock1, *listenSock2; + PRUint16 listenPort1, listenPort2; + PRNetAddr addr; + PR_fd_set readFdSet; + char buf[128]; + PRInt32 retVal; + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (debug_mode) { + printf("This program tests PR_Select with sockets. Timeout \n"); + printf("operations are tested.\n\n"); + } + + /* Create two listening sockets */ + if ((listenSock1 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock1, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort1 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock1, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + + if ((listenSock2 = PR_NewTCPSocket()) == NULL) { + fprintf(stderr, "Can't create a new TCP socket\n"); + failed_already=1; + goto exit_now; + } + addr.inet.family = PR_AF_INET; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + addr.inet.port = PR_htons(0); + if (PR_Bind(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "Can't bind socket\n"); + failed_already=1; + goto exit_now; + } + if (PR_GetSockName(listenSock2, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + failed_already=1; + goto exit_now; + } + listenPort2 = PR_ntohs(addr.inet.port); + if (PR_Listen(listenSock2, 5) == PR_FAILURE) { + fprintf(stderr, "Can't listen on a socket\n"); + failed_already=1; + goto exit_now; + } + PR_snprintf(buf, sizeof(buf), + "The server thread is listening on ports %hu and %hu\n\n", + listenPort1, listenPort2); + if (debug_mode) { + printf("%s", buf); + } + + /* Set up the fd set */ + PR_FD_ZERO(&readFdSet); + PR_FD_SET(listenSock1, &readFdSet); + PR_FD_SET(listenSock2, &readFdSet); + + /* Testing timeout */ + if (debug_mode) { + printf("PR_Select should time out in 5 seconds\n"); + } + retVal = PR_Select(0 /* unused */, &readFdSet, NULL, NULL, + PR_SecondsToInterval(5)); + if (retVal != 0) { + PR_snprintf(buf, sizeof(buf), + "PR_Select should time out and return 0, but it returns %ld\n", + retVal); + fprintf(stderr, "%s", buf); + if (retVal == -1) { + fprintf(stderr, "Error %d, oserror %d\n", PR_GetError(), + PR_GetOSError()); + failed_already=1; + } + goto exit_now; + } + if (debug_mode) { + printf("PR_Select timed out. Test passed.\n\n"); + } + + PR_Cleanup(); + +exit_now: + if(failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/select2.c b/nsprpub/pr/tests/select2.c new file mode 100644 index 0000000000..da7360b025 --- /dev/null +++ b/nsprpub/pr/tests/select2.c @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: select2.c +** +** Description: Measure PR_Select and Empty_Select performance. +** +** Modification History: +** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" +#include "prttools.h" +#include "primpl.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if defined(OS2) +#include <sys/time.h> +#endif + +#define PORT 8000 +#define DEFAULT_COUNT 10 +PRInt32 count; + + +/*********************************************************************** +** PRIVATE FUNCTION: Test_Result +** DESCRIPTION: Used in conjunction with the regress tool, prints out the +** status of the test case. +** INPUTS: PASS/FAIL +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: Determine what the status is and print accordingly. +** +***********************************************************************/ + + +static void Test_Result (int result) +{ + switch (result) + { + case PASS: + printf ("PASS\n"); + break; + case FAIL: + printf ("FAIL\n"); + break; + default: + printf ("NOSTATUS\n"); + break; + } +} + +static void EmptyPRSelect(void) +{ + PRInt32 index = count; + PRInt32 rv; + + for (; index--;) { + rv = PR_Select(0, NULL, NULL, NULL, PR_INTERVAL_NO_WAIT); + } +} + +static void EmptyNativeSelect(void) +{ + PRInt32 rv; + PRInt32 index = count; + struct timeval timeout; + + timeout.tv_sec = timeout.tv_usec = 0; + for (; index--;) { + rv = select(0, NULL, NULL, NULL, &timeout); + } +} + +static void PRSelectTest(void) +{ + PRFileDesc *listenSocket; + PRNetAddr serverAddr; + + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + return; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(INADDR_ANY); + + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error binding to server address\n"); + } + PR_Close(listenSocket); + return; + } + + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + PR_Close(listenSocket); + return; + } + if (debug_mode) { + printf("Listening on port %d\n", PORT); + } + + { + PRFileDesc *newSock; + PRNetAddr rAddr; + PRInt32 loops = 0; + PR_fd_set rdset; + PRInt32 rv; + PRInt32 bytesRead; + char buf[11]; + + loops++; + + if (debug_mode) { + printf("Going into accept\n"); + } + + newSock = PR_Accept(listenSocket, + &rAddr, + PR_INTERVAL_NO_TIMEOUT); + + if (newSock) { + if (debug_mode) { + printf("Got connection!\n"); + } + } else { + if (debug_mode) { + printf("PR_Accept failed: error code %d\n", PR_GetError()); + } + else { + Test_Result (FAIL); + } + } + + PR_FD_ZERO(&rdset); + PR_FD_SET(newSock, &rdset); + + if (debug_mode) { + printf("Going into select \n"); + } + + rv = PR_Select(0, &rdset, 0, 0, PR_INTERVAL_NO_TIMEOUT); + + if (debug_mode) { + printf("return from select is %d\n", rv); + } + + if (PR_FD_ISSET(newSock, &rdset)) { + if (debug_mode) { + printf("I can't believe it- the socket is ready okay!\n"); + } + } else { + if (debug_mode) { + printf("Damn; the select test failed...\n"); + } + else { + Test_Result (FAIL); + } + } + + strcpy(buf, "XXXXXXXXXX"); + bytesRead = PR_Recv(newSock, buf, 10, 0, PR_INTERVAL_NO_TIMEOUT); + buf[10] = '\0'; + + if (debug_mode) { + printf("Recv completed with %d bytes, %s\n", bytesRead, buf); + } + + PR_Close(newSock); + } + +} + +#if defined(XP_UNIX) + +static void NativeSelectTest(void) +{ + PRFileDesc *listenSocket; + PRNetAddr serverAddr; + + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + return; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(INADDR_ANY); + + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error binding to server address\n"); + } + PR_Close(listenSocket); + return; + } + + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + PR_Close(listenSocket); + return; + } + if (debug_mode) { + printf("Listening on port %d\n", PORT); + } + + { + PRIntn osfd; + char buf[11]; + fd_set rdset; + PRNetAddr rAddr; + PRFileDesc *newSock; + struct timeval timeout; + PRInt32 bytesRead, rv, loops = 0; + + loops++; + + if (debug_mode) { + printf("Going into accept\n"); + } + + newSock = PR_Accept(listenSocket, &rAddr, PR_INTERVAL_NO_TIMEOUT); + + if (newSock) { + if (debug_mode) { + printf("Got connection!\n"); + } + } else { + if (debug_mode) { + printf("PR_Accept failed: error code %d\n", PR_GetError()); + } + else { + Test_Result (FAIL); + } + } + + osfd = PR_FileDesc2NativeHandle(newSock); + FD_ZERO(&rdset); + FD_SET(osfd, &rdset); + + if (debug_mode) { + printf("Going into select \n"); + } + + + timeout.tv_sec = 2; timeout.tv_usec = 0; + rv = select(osfd + 1, &rdset, NULL, NULL, &timeout); + + if (debug_mode) { + printf("return from select is %d\n", rv); + } + + if (FD_ISSET(osfd, &rdset)) { + if (debug_mode) { + printf("I can't believe it- the socket is ready okay!\n"); + } + } else { + if (debug_mode) { + printf("Damn; the select test failed...\n"); + } + else { + Test_Result (FAIL); + } + } + + strcpy(buf, "XXXXXXXXXX"); + bytesRead = PR_Recv(newSock, buf, 10, 0, PR_INTERVAL_NO_TIMEOUT); + buf[10] = '\0'; + + if (debug_mode) { + printf("Recv completed with %d bytes, %s\n", bytesRead, buf); + } + + PR_Close(newSock); + } + +} /* NativeSelectTest */ + +#endif /* defined(XP_UNIX) */ + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + PRInt32 tot; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + tot = PR_IntervalToMilliseconds(stop-start); + + if (debug_mode) { + printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot); + } +} + +int main(int argc, char **argv) +{ + + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (argc > 2) { + count = atoi(argv[2]); + } else { + count = DEFAULT_COUNT; + } + +#if defined(XP_UNIX) + Measure(NativeSelectTest, "time to call 1 element select()"); +#endif + Measure(EmptyPRSelect, "time to call Empty PR_select()"); + Measure(EmptyNativeSelect, "time to call Empty select()"); + Measure(PRSelectTest, "time to call 1 element PR_select()"); + + if (!debug_mode) { + Test_Result (NOSTATUS); + } + PR_Cleanup(); + + +} diff --git a/nsprpub/pr/tests/selintr.c b/nsprpub/pr/tests/selintr.c new file mode 100644 index 0000000000..84513f6887 --- /dev/null +++ b/nsprpub/pr/tests/selintr.c @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test whether classic NSPR's select() wrapper properly blocks + * the periodic SIGALRM clocks. On some platforms (such as + * HP-UX and SINIX) an interrupted select() system call is + * restarted with the originally specified timeout, ignoring + * the time that has elapsed. If a select() call is interrupted + * repeatedly, it will never time out. (See Bugzilla bug #39674.) + */ + +#if !defined(XP_UNIX) + +/* + * This test is applicable to Unix only. + */ + +int main() +{ + return 0; +} + +#else /* XP_UNIX */ + +#include "nspr.h" + +#include <sys/time.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + struct timeval timeout; + int rv; + + PR_SetError(0, 0); /* force NSPR to initialize */ + PR_EnableClockInterrupts(); + + /* 2 seconds timeout */ + timeout.tv_sec = 2; + timeout.tv_usec = 0; + rv = select(1, NULL, NULL, NULL, &timeout); + printf("select returned %d\n", rv); + return 0; +} + +#endif /* XP_UNIX */ diff --git a/nsprpub/pr/tests/sem.c b/nsprpub/pr/tests/sem.c new file mode 100644 index 0000000000..80db76d2e9 --- /dev/null +++ b/nsprpub/pr/tests/sem.c @@ -0,0 +1,222 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: sem.c +** +** Description: Tests Semaphonre functions. +** +** Modification History: +** 20-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "prpriv.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRIntn failed_already=0; +PRIntn debug_mode; + +/* + Since we don't have stdin, stdout everywhere, we will fake + it with our in-memory buffers called stdin and stdout. +*/ + +#define SBSIZE 1024 + +#include "obsolete/prsem.h" + +static char stdinBuf[SBSIZE]; +static char stdoutBuf[SBSIZE]; + +static PRUintn stdinBufIdx = 0; +static PRUintn stdoutBufIdx = 0; +static PRStatus finalResult = PR_SUCCESS; + + +static size_t dread (PRUintn device, char *buf, size_t bufSize) +{ + PRUintn i; + + /* during first read call, initialize the stdinBuf buffer*/ + if (stdinBufIdx == 0) { + for (i=0; i<SBSIZE; i++) { + stdinBuf[i] = i; + } + } + + /* now copy data from stdinBuf to the given buffer upto bufSize */ + for (i=0; i<bufSize; i++) { + if (stdinBufIdx == SBSIZE) { + break; + } + buf[i] = stdinBuf[stdinBufIdx++]; + } + + return i; +} + +static size_t dwrite (PRUintn device, char *buf, size_t bufSize) +{ + PRUintn i, j; + + /* copy data from the given buffer upto bufSize to stdoutBuf */ + for (i=0; i<bufSize; i++) { + if (stdoutBufIdx == SBSIZE) { + break; + } + stdoutBuf[stdoutBufIdx++] = buf[i]; + } + + /* during last write call, compare the two buffers */ + if (stdoutBufIdx == SBSIZE) + for (j=0; j<SBSIZE; j++) + if (stdinBuf[j] != stdoutBuf[j]) { + if (debug_mode) { + printf("data mismatch for index= %d \n", j); + } + finalResult = PR_FAILURE; + } + + return i; +} + +/*------------------ Following is the real test program ---------*/ +/* + Program to copy standard input to standard output. The program + uses two threads. One reads the input and puts the data in a + double buffer. The other reads the buffer contents and writes + it to standard output. +*/ + +PRSemaphore *emptyBufs; /* number of empty buffers */ +PRSemaphore *fullBufs; /* number of buffers that are full */ + +#define BSIZE 100 + +struct { + char data[BSIZE]; + PRUintn nbytes; /* number of bytes in this buffer */ +} buf[2]; + +static void PR_CALLBACK reader(void *arg) +{ + PRUintn i = 0; + size_t nbytes; + + do { + (void) PR_WaitSem(emptyBufs); + nbytes = dread(0, buf[i].data, BSIZE); + buf[i].nbytes = nbytes; + PR_PostSem(fullBufs); + i = (i + 1) % 2; + } while (nbytes > 0); +} + +static void writer(void) +{ + PRUintn i = 0; + size_t nbytes; + + do { + (void) PR_WaitSem(fullBufs); + nbytes = buf[i].nbytes; + if (nbytes > 0) { + nbytes = dwrite(1, buf[i].data, nbytes); + PR_PostSem(emptyBufs); + i = (i + 1) % 2; + } + } while (nbytes > 0); +} + +int main(int argc, char **argv) +{ + PRThread *r; + + PR_STDIO_INIT(); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + { + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } + + /* main test */ + + emptyBufs = PR_NewSem(2); /* two empty buffers */ + + fullBufs = PR_NewSem(0); /* zero full buffers */ + + /* create the reader thread */ + + r = PR_CreateThread(PR_USER_THREAD, + reader, 0, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + + /* Do the writer operation in this thread */ + writer(); + + PR_DestroySem(emptyBufs); + PR_DestroySem(fullBufs); + + if (finalResult == PR_SUCCESS) { + if (debug_mode) { + printf("sem Test Passed.\n"); + } + } + else { + if (debug_mode) { + printf("sem Test Failed.\n"); + } + failed_already=1; + } + PR_Cleanup(); + if(failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/sema.c b/nsprpub/pr/tests/sema.c new file mode 100644 index 0000000000..81ccd01b7a --- /dev/null +++ b/nsprpub/pr/tests/sema.c @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#ifdef DEBUG +#define SEM_D "D" +#else +#define SEM_D +#endif +#ifdef IS_64 +#define SEM_64 "64" +#else +#define SEM_64 +#endif + +#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64 +#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64 +#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_PostSemaphore 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_PostSemaphore 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/nsprpub/pr/tests/semaerr.c b/nsprpub/pr/tests/semaerr.c new file mode 100644 index 0000000000..f3c253b4f4 --- /dev/null +++ b/nsprpub/pr/tests/semaerr.c @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#ifdef DEBUG +#define SEM_D "D" +#else +#define SEM_D +#endif +#ifdef IS_64 +#define SEM_64 "64" +#else +#define SEM_64 +#endif + +#define NO_SUCH_SEM_NAME "/tmp/nosuchsem.sem" SEM_D SEM_64 +#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64 +#define EXE_NAME "semaerr1" +#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++ = EXE_NAME; + 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/nsprpub/pr/tests/semaerr1.c b/nsprpub/pr/tests/semaerr1.c new file mode 100644 index 0000000000..9271663d69 --- /dev/null +++ b/nsprpub/pr/tests/semaerr1.c @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#ifdef DEBUG +#define SEM_D "D" +#else +#define SEM_D +#endif +#ifdef IS_64 +#define SEM_64 "64" +#else +#define SEM_64 +#endif + +#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64 +#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64 +#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/nsprpub/pr/tests/semaping.c b/nsprpub/pr/tests/semaping.c new file mode 100644 index 0000000000..3b653d524e --- /dev/null +++ b/nsprpub/pr/tests/semaping.c @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#ifdef DEBUG +#define SEM_D "D" +#else +#define SEM_D +#endif +#ifdef IS_64 +#define SEM_64 "64" +#else +#define SEM_64 +#endif + +#define SHM_NAME "/tmp/counter" SEM_D SEM_64 +#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64 +#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64 +#define EXE_NAME "semapong" +#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 (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + 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 (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + + child_arg = &child_argv[0]; + *child_arg++ = EXE_NAME; + 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_PostSemaphore 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/nsprpub/pr/tests/semapong.c b/nsprpub/pr/tests/semapong.c new file mode 100644 index 0000000000..fd24824e31 --- /dev/null +++ b/nsprpub/pr/tests/semapong.c @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#ifdef DEBUG +#define SEM_D "D" +#else +#define SEM_D +#endif +#ifdef IS_64 +#define SEM_64 "64" +#else +#define SEM_64 +#endif + +#define SHM_NAME "/tmp/counter" SEM_D SEM_64 +#define SEM_NAME1 "/tmp/foo.sem" SEM_D SEM_64 +#define SEM_NAME2 "/tmp/bar.sem" SEM_D SEM_64 +#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 (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + 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 (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + 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_PostSemaphore 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/nsprpub/pr/tests/sendzlf.c b/nsprpub/pr/tests/sendzlf.c new file mode 100644 index 0000000000..a65f6cd6b4 --- /dev/null +++ b/nsprpub/pr/tests/sendzlf.c @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test: sendzlf.c + * + * Description: send a zero-length file with PR_SendFile and + * PR_TransmitFile. + */ + +#define ZERO_LEN_FILE_NAME "zerolen.tmp" +#define HEADER_STR "Header" +#define HEADER_LEN 6 /* length of HEADER_STR, not counting the null byte */ +#define TRAILER_STR "Trailer" +#define TRAILER_LEN 7 /* length of TRAILER_STR, not counting the null byte */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void ClientThread(void *arg) +{ + PRFileDesc *sock; + PRNetAddr addr; + PRUint16 port = (PRUint16) arg; + char buf[1024]; + char *bufPtr; + PRInt32 nbytes; + PRInt32 ntotal; + PRInt32 nexpected; + + sock = PR_NewTCPSocket(); + if (NULL == sock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + if (PR_InitializeNetAddr(PR_IpAddrLoopback, port, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + if (PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + fprintf(stderr, "PR_Connect failed\n"); + exit(1); + } + ntotal = 0; + bufPtr = buf; + while ((nbytes = PR_Read(sock, bufPtr, sizeof(buf)-ntotal)) > 0) { + ntotal += nbytes; + bufPtr += nbytes; + } + if (-1 == nbytes) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + nexpected = HEADER_LEN+TRAILER_LEN+TRAILER_LEN+HEADER_LEN+HEADER_LEN; + if (ntotal != nexpected) { + fprintf(stderr, "total bytes read should be %d but is %d\n", + nexpected, ntotal); + exit(1); + } + if (memcmp(buf, HEADER_STR TRAILER_STR TRAILER_STR HEADER_STR HEADER_STR, + nexpected) != 0) { + fprintf(stderr, "wrong data is received\n"); + exit(1); + } + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +static void ServerThread(void *arg) +{ + PRFileDesc *listenSock = (PRFileDesc *) arg; + PRFileDesc *acceptSock; + PRFileDesc *file; + PRSendFileData sfd; + char header[1024], trailer[1024]; + PRInt32 nbytes; + + /* Create a zero-length file */ + file = PR_Open(ZERO_LEN_FILE_NAME, + PR_CREATE_FILE|PR_TRUNCATE|PR_RDWR, 0666); + if (NULL == file) { + fprintf(stderr, "PR_Open failed\n"); + exit(1); + } + sfd.fd = file; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + memcpy(header, HEADER_STR, HEADER_LEN); + memcpy(trailer, TRAILER_STR, TRAILER_LEN); + sfd.header = header; + sfd.hlen = HEADER_LEN; + sfd.trailer = trailer; + sfd.tlen = TRAILER_LEN; + acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (NULL == acceptSock) { + fprintf(stderr, "PR_Accept failed\n"); + exit(1); + } + /* Send both header and trailer */ + nbytes = PR_SendFile(acceptSock, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + if (HEADER_LEN+TRAILER_LEN != nbytes) { + fprintf(stderr, "PR_SendFile should return %d but returned %d\n", + HEADER_LEN+TRAILER_LEN, nbytes); + exit(1); + } + /* Trailer only, no header */ + sfd.hlen = 0; + nbytes = PR_SendFile(acceptSock, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + if (TRAILER_LEN != nbytes) { + fprintf(stderr, "PR_SendFile should return %d but returned %d\n", + TRAILER_LEN, nbytes); + exit(1); + } + /* Header only, no trailer */ + sfd.hlen = HEADER_LEN; + sfd.tlen = 0; + nbytes = PR_SendFile(acceptSock, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + if (HEADER_LEN != nbytes) { + fprintf(stderr, "PR_SendFile should return %d but returned %d\n", + HEADER_LEN, nbytes); + exit(1); + } + /* Try PR_TransmitFile */ + nbytes = PR_TransmitFile(acceptSock, file, header, HEADER_LEN, + PR_TRANSMITFILE_KEEP_OPEN, PR_INTERVAL_NO_TIMEOUT); + if (HEADER_LEN != nbytes) { + fprintf(stderr, "PR_TransmitFile should return %d but returned %d\n", + HEADER_LEN, nbytes); + exit(1); + } + if (PR_Close(acceptSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (PR_Close(file) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (PR_Delete(ZERO_LEN_FILE_NAME) == PR_FAILURE) { + fprintf(stderr, "PR_Delete failed\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + PRFileDesc *listenSock; + PRThread *clientThread; + PRThread *serverThread; + PRNetAddr addr; + PRThreadScope scope = PR_GLOBAL_THREAD; + + listenSock = PR_NewTCPSocket(); + if (NULL == listenSock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + if (PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + if (PR_Bind(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + /* Find out what port number we are bound to. */ + if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + + clientThread = PR_CreateThread(PR_USER_THREAD, + ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)), + PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0); + if (NULL == clientThread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + serverThread = PR_CreateThread(PR_USER_THREAD, + ServerThread, listenSock, + PR_PRIORITY_NORMAL, scope, PR_JOINABLE_THREAD, 0); + if (NULL == serverThread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + if (PR_JoinThread(clientThread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + if (PR_JoinThread(serverThread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + if (PR_Close(listenSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/server_test.c b/nsprpub/pr/tests/server_test.c new file mode 100644 index 0000000000..93ffe4aff2 --- /dev/null +++ b/nsprpub/pr/tests/server_test.c @@ -0,0 +1,692 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** This server simulates a server running in loopback mode. +** +** The idea is that a single server is created. The server initially creates +** a number of worker threads. Then, with the server running, a number of +** clients are created which start requesting service from the server. +** +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "pprthred.h" + +#include <string.h> + +#define PORT 15004 +#define THREAD_STACKSIZE 0 + +#define PASS 0 +#define FAIL 1 +static int debug_mode = 0; +static int failed_already = 0; + +static int _iterations = 1000; +static int _clients = 1; +static int _client_data = 250; +static int _server_data = (8*1024); + +static PRThreadScope ServerScope, ClientScope; + +#define SERVER "Server" +#define MAIN "Main" + +#define SERVER_STATE_STARTUP 0 +#define SERVER_STATE_READY 1 +#define SERVER_STATE_DYING 2 +#define SERVER_STATE_DEAD 4 +int ServerState; +PRLock *ServerStateCVLock; +PRCondVar *ServerStateCV; + +#undef DEBUGPRINTS +#ifdef DEBUGPRINTS +#define DPRINTF printf +#else +#define DPRINTF +#endif + + +/*********************************************************************** +** PRIVATE FUNCTION: Test_Result +** DESCRIPTION: Used in conjunction with the regress tool, prints out the +** status of the test case. +** INPUTS: PASS/FAIL +** OUTPUTS: None +** RETURN: None +** SIDE EFFECTS: +** +** RESTRICTIONS: +** None +** MEMORY: NA +** ALGORITHM: Determine what the status is and print accordingly. +** +***********************************************************************/ + + +static void Test_Result (int result) +{ + switch (result) + { + case PASS: + printf ("PASS\n"); + break; + case FAIL: + printf ("FAIL\n"); + failed_already = 1; + break; + default: + break; + } +} + +static void do_work(void); + +/* --- Server state functions --------------------------------------------- */ +void +SetServerState(char *waiter, PRInt32 state) +{ + PR_Lock(ServerStateCVLock); + ServerState = state; + PR_NotifyCondVar(ServerStateCV); + + if (debug_mode) { + DPRINTF("\t%s changed state to %d\n", waiter, state); + } + + PR_Unlock(ServerStateCVLock); +} + +int +WaitServerState(char *waiter, PRInt32 state) +{ + PRInt32 rv; + + PR_Lock(ServerStateCVLock); + + if (debug_mode) { + DPRINTF("\t%s waiting for state %d\n", waiter, state); + } + + while(!(ServerState & state)) { + PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); + } + rv = ServerState; + + if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", + waiter, state, ServerState); + PR_Unlock(ServerStateCVLock); + + return rv; +} + +/* --- Server Functions ------------------------------------------- */ + +PRLock *workerThreadsLock; +PRInt32 workerThreads; +PRInt32 workerThreadsBusy; + +void +WorkerThreadFunc(void *_listenSock) +{ + PRFileDesc *listenSock = (PRFileDesc *)_listenSock; + PRInt32 bytesRead; + PRInt32 bytesWritten; + char *dataBuf; + char *sendBuf; + + if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", + _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); + dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); + if (!dataBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + + if (debug_mode) { + DPRINTF("\tServer worker thread running\n"); + } + + while(1) { + PRInt32 bytesToRead = _client_data; + PRInt32 bytesToWrite = _server_data; + PRFileDesc *newSock; + PRNetAddr *rAddr; + PRInt32 loops = 0; + + loops++; + + if (debug_mode) { + DPRINTF("\tServer thread going into accept\n"); + } + + bytesRead = PR_AcceptRead(listenSock, + &newSock, + &rAddr, + dataBuf, + bytesToRead, + PR_INTERVAL_NO_TIMEOUT); + + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error in accept (%d)\n", bytesRead); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); + } + + PR_AtomicIncrement(&workerThreadsBusy); + if (workerThreadsBusy == workerThreads) { + PR_Lock(workerThreadsLock); + if (workerThreadsBusy == workerThreads) { + PRThread *WorkerThread; + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSock, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("Error creating client thread %d\n", workerThreads); + } + } else { + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer creates worker (%d)\n", workerThreads); + } + } + } + PR_Unlock(workerThreadsLock); + } + + bytesToRead -= bytesRead; + while (bytesToRead) { + bytesRead = PR_Recv(newSock, + dataBuf, + bytesToRead, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error receiving data (%d)\n", bytesRead); + } + continue; + } + if (debug_mode) { + DPRINTF("\tServer received %d bytes\n", bytesRead); + } + } + + bytesWritten = PR_Send(newSock, + sendBuf, + bytesToWrite, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesWritten != _server_data) { + if (debug_mode) printf("\tError sending data to client (%d, %d)\n", + bytesWritten, PR_GetOSError()); + } else { + if (debug_mode) { + DPRINTF("\tServer sent %d bytes\n", bytesWritten); + } + } + + PR_Close(newSock); + PR_AtomicDecrement(&workerThreadsBusy); + } +} + +PRFileDesc * +ServerSetup(void) +{ + PRFileDesc *listenSocket; + PRSocketOptionData sockOpt; + PRNetAddr serverAddr; + PRThread *WorkerThread; + + if ((listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + else { + Test_Result(FAIL); + } + return NULL; + } + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + if (PR_SetSocketOption(listenSocket, &sockOpt) != PR_SUCCESS) { + if (debug_mode) printf("\tServer error setting socket option: OS error %d\n", + PR_GetOSError()); + else { + Test_Result(FAIL); + } + PR_Close(listenSocket); + return NULL; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + + if (PR_Bind(listenSocket, &serverAddr) != PR_SUCCESS) { + if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", + PR_GetOSError()); + else { + Test_Result(FAIL); + } + PR_Close(listenSocket); + return NULL; + } + + if (PR_Listen(listenSocket, 128) != PR_SUCCESS) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + else { + Test_Result(FAIL); + } + PR_Close(listenSocket); + + return NULL; + } + + /* Create Clients */ + workerThreads = 0; + workerThreadsBusy = 0; + + workerThreadsLock = PR_NewLock(); + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSocket, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("error creating working thread\n"); + } + PR_Close(listenSocket); + return NULL; + } + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer created primordial worker thread\n"); + } + + return listenSocket; +} + +/* The main server loop */ +void +ServerThreadFunc(void *unused) +{ + PRFileDesc *listenSocket; + + /* Do setup */ + listenSocket = ServerSetup(); + + if (!listenSocket) { + SetServerState(SERVER, SERVER_STATE_DEAD); + } else { + + if (debug_mode) { + DPRINTF("\tServer up\n"); + } + + /* Tell clients they can start now. */ + SetServerState(SERVER, SERVER_STATE_READY); + + /* Now wait for server death signal */ + WaitServerState(SERVER, SERVER_STATE_DYING); + + /* Cleanup */ + SetServerState(SERVER, SERVER_STATE_DEAD); + } +} + +/* --- Client Functions ------------------------------------------- */ + +PRInt32 numRequests; +PRInt32 numClients; +PRMonitor *clientMonitor; + +void +ClientThreadFunc(void *unused) +{ + PRNetAddr serverAddr; + PRFileDesc *clientSocket; + char *sendBuf; + char *recvBuf; + PRInt32 rv; + PRInt32 bytesNeeded; + + sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); + if (!recvBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + + while(numRequests > 0) { + + if ( (numRequests % 10) == 0 ) + if (debug_mode) { + printf("."); + } + if (debug_mode) { + DPRINTF("\tClient starting request %d\n", numRequests); + } + + clientSocket = PR_NewTCPSocket(); + if (!clientSocket) { + if (debug_mode) printf("Client error creating socket: OS error %d\n", + PR_GetOSError()); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connecting\n"); + } + + rv = PR_Connect(clientSocket, + &serverAddr, + PR_INTERVAL_NO_TIMEOUT); + if (!clientSocket) { + if (debug_mode) { + printf("\tClient error connecting\n"); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connected\n"); + } + + rv = PR_Send(clientSocket, + sendBuf, + _client_data, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv != _client_data) { + if (debug_mode) { + printf("Client error sending data (%d)\n", rv); + } + PR_Close(clientSocket); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient sent %d bytes\n", rv); + } + + bytesNeeded = _server_data; + while(bytesNeeded) { + rv = PR_Recv(clientSocket, + recvBuf, + bytesNeeded, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv <= 0) { + if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", + rv, (_server_data - bytesNeeded), _server_data); + break; + } + if (debug_mode) { + DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); + } + bytesNeeded -= rv; + } + + PR_Close(clientSocket); + + PR_AtomicDecrement(&numRequests); + } + + PR_EnterMonitor(clientMonitor); + --numClients; + PR_Notify(clientMonitor); + PR_ExitMonitor(clientMonitor); + + PR_DELETE(sendBuf); + PR_DELETE(recvBuf); +} + +void +RunClients(void) +{ + PRInt32 index; + + numRequests = _iterations; + numClients = _clients; + clientMonitor = PR_NewMonitor(); + + for (index=0; index<_clients; index++) { + PRThread *clientThread; + + + clientThread = PR_CreateThread( + PR_USER_THREAD, + ClientThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ClientScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!clientThread) { + if (debug_mode) { + printf("\terror creating client thread %d\n", index); + } + } else if (debug_mode) { + DPRINTF("\tMain created client %d/%d\n", index+1, _clients); + } + + } + + PR_EnterMonitor(clientMonitor); + while(numClients) { + PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(clientMonitor); +} + +/* --- Main Function ---------------------------------------------- */ + +static +void do_work() +{ + PRThread *ServerThread; + PRInt32 state; + + SetServerState(MAIN, SERVER_STATE_STARTUP); + ServerThread = PR_CreateThread( + PR_USER_THREAD, + ServerThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ServerScope, + PR_JOINABLE_THREAD, + THREAD_STACKSIZE); + if (!ServerThread) { + if (debug_mode) { + printf("error creating main server thread\n"); + } + return; + } + + /* Wait for server to be ready */ + state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); + + if (!(state & SERVER_STATE_DEAD)) { + /* Run Test Clients */ + RunClients(); + + /* Send death signal to server */ + SetServerState(MAIN, SERVER_STATE_DYING); + } + + PR_JoinThread(ServerThread); +} + +static void do_workUU(void) +{ + ServerScope = PR_LOCAL_THREAD; + ClientScope = PR_LOCAL_THREAD; + do_work(); +} + +static void do_workUK(void) +{ + ServerScope = PR_LOCAL_THREAD; + ClientScope = PR_GLOBAL_THREAD; + do_work(); +} + +static void do_workKU(void) +{ + ServerScope = PR_GLOBAL_THREAD; + ClientScope = PR_LOCAL_THREAD; + do_work(); +} + +static void do_workKK(void) +{ + ServerScope = PR_GLOBAL_THREAD; + ClientScope = PR_GLOBAL_THREAD; + do_work(); +} + + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + if (debug_mode) { + printf("\n%40s: %6.2f usec\n", msg, d / _iterations); + } +} + + +int main(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + if (debug_mode) { + printf("Enter number of iterations: \n"); + scanf("%d", &_iterations); + printf("Enter number of clients : \n"); + scanf("%d", &_clients); + printf("Enter size of client data : \n"); + scanf("%d", &_client_data); + printf("Enter size of server data : \n"); + scanf("%d", &_server_data); + } + else + { + + _iterations = 10; + _clients = 1; + _client_data = 10; + _server_data = 10; + } + + if (debug_mode) { + printf("\n\n%d iterations with %d client threads.\n", + _iterations, _clients); + printf("Sending %d bytes of client data and %d bytes of server data\n", + _client_data, _server_data); + } + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + ServerStateCVLock = PR_NewLock(); + ServerStateCV = PR_NewCondVar(ServerStateCVLock); + + Measure(do_workUU, "server loop user/user"); +#if 0 + Measure(do_workUK, "server loop user/kernel"); + Measure(do_workKU, "server loop kernel/user"); + Measure(do_workKK, "server loop kernel/kernel"); +#endif + + PR_Cleanup(); + + return failed_already; +} diff --git a/nsprpub/pr/tests/servr_kk.c b/nsprpub/pr/tests/servr_kk.c new file mode 100644 index 0000000000..06f4fae117 --- /dev/null +++ b/nsprpub/pr/tests/servr_kk.c @@ -0,0 +1,659 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** This server simulates a server running in loopback mode. +** +** The idea is that a single server is created. The server initially creates +** a number of worker threads. Then, with the server running, a number of +** clients are created which start requesting service from the server. +** +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "pprthred.h" + +#include <string.h> + +#define PORT 15004 +#define THREAD_STACKSIZE 0 + +static int _iterations = 1000; +static int _clients = 1; +static int _client_data = 250; +static int _server_data = (8*1024); + +static PRThreadScope ServerScope, ClientScope; + +#define SERVER "Server" +#define MAIN "Main" + +#define SERVER_STATE_STARTUP 0 +#define SERVER_STATE_READY 1 +#define SERVER_STATE_DYING 2 +#define SERVER_STATE_DEAD 4 +int ServerState; +PRLock *ServerStateCVLock; +PRCondVar *ServerStateCV; + +#ifdef DEBUGPRINTS +#define DPRINTF printf +#else +#define DPRINTF +#endif + +PRIntn failed_already=0; +PRIntn debug_mode; +static void do_work(void); + +/* --- Server state functions --------------------------------------------- */ +void +SetServerState(char *waiter, PRInt32 state) +{ + PR_Lock(ServerStateCVLock); + ServerState = state; + PR_NotifyCondVar(ServerStateCV); + + if (debug_mode) { + DPRINTF("\t%s changed state to %d\n", waiter, state); + } + + PR_Unlock(ServerStateCVLock); +} + +int +WaitServerState(char *waiter, PRInt32 state) +{ + PRInt32 rv; + + PR_Lock(ServerStateCVLock); + + if (debug_mode) { + DPRINTF("\t%s waiting for state %d\n", waiter, state); + } + + while(!(ServerState & state)) { + PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); + } + rv = ServerState; + + if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", + waiter, state, ServerState); + PR_Unlock(ServerStateCVLock); + + return rv; +} + +/* --- Server Functions ------------------------------------------- */ + +PRLock *workerThreadsLock; +PRInt32 workerThreads; +PRInt32 workerThreadsBusy; + +void +WorkerThreadFunc(void *_listenSock) +{ + PRFileDesc *listenSock = (PRFileDesc *)_listenSock; + PRInt32 bytesRead; + PRInt32 bytesWritten; + char *dataBuf; + char *sendBuf; + + if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", + _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); + dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); + if (!dataBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + + if (debug_mode) { + DPRINTF("\tServer worker thread running\n"); + } + + while(1) { + PRInt32 bytesToRead = _client_data; + PRInt32 bytesToWrite = _server_data; + PRFileDesc *newSock; + PRNetAddr *rAddr; + PRInt32 loops = 0; + + loops++; + + if (debug_mode) { + DPRINTF("\tServer thread going into accept\n"); + } + + bytesRead = PR_AcceptRead(listenSock, + &newSock, + &rAddr, + dataBuf, + bytesToRead, + PR_INTERVAL_NO_TIMEOUT); + + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error in accept (%d)\n", bytesRead); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); + } + + PR_AtomicIncrement(&workerThreadsBusy); + if (workerThreadsBusy == workerThreads) { + PR_Lock(workerThreadsLock); + if (workerThreadsBusy == workerThreads) { + PRThread *WorkerThread; + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSock, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("Error creating client thread %d\n", workerThreads); + } + } else { + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer creates worker (%d)\n", workerThreads); + } + } + } + PR_Unlock(workerThreadsLock); + } + + bytesToRead -= bytesRead; + while (bytesToRead) { + bytesRead = PR_Recv(newSock, + dataBuf, + bytesToRead, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error receiving data (%d)\n", bytesRead); + } + continue; + } + if (debug_mode) { + DPRINTF("\tServer received %d bytes\n", bytesRead); + } + } + + bytesWritten = PR_Send(newSock, + sendBuf, + bytesToWrite, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesWritten != _server_data) { + if (debug_mode) printf("\tError sending data to client (%d, %d)\n", + bytesWritten, PR_GetOSError()); + } else { + if (debug_mode) { + DPRINTF("\tServer sent %d bytes\n", bytesWritten); + } + } + + PR_Close(newSock); + PR_AtomicDecrement(&workerThreadsBusy); + } +} + +PRFileDesc * +ServerSetup(void) +{ + PRFileDesc *listenSocket; + PRSocketOptionData sockOpt; + PRNetAddr serverAddr; + PRThread *WorkerThread; + + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + else { + failed_already=1; + } + return NULL; + } + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) { + if (debug_mode) printf("\tServer error setting socket option: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { + if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + else { + failed_already=1; + } + PR_Close(listenSocket); + + return NULL; + } + + /* Create Clients */ + workerThreads = 0; + workerThreadsBusy = 0; + + workerThreadsLock = PR_NewLock(); + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSocket, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("error creating working thread\n"); + } + PR_Close(listenSocket); + return NULL; + } + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer created primordial worker thread\n"); + } + + return listenSocket; +} + +/* The main server loop */ +void +ServerThreadFunc(void *unused) +{ + PRFileDesc *listenSocket; + + /* Do setup */ + listenSocket = ServerSetup(); + + if (!listenSocket) { + SetServerState(SERVER, SERVER_STATE_DEAD); + } else { + + if (debug_mode) { + DPRINTF("\tServer up\n"); + } + + /* Tell clients they can start now. */ + SetServerState(SERVER, SERVER_STATE_READY); + + /* Now wait for server death signal */ + WaitServerState(SERVER, SERVER_STATE_DYING); + + /* Cleanup */ + SetServerState(SERVER, SERVER_STATE_DEAD); + } +} + +/* --- Client Functions ------------------------------------------- */ + +PRInt32 numRequests; +PRInt32 numClients; +PRMonitor *clientMonitor; + +void +ClientThreadFunc(void *unused) +{ + PRNetAddr serverAddr; + PRFileDesc *clientSocket; + char *sendBuf; + char *recvBuf; + PRInt32 rv; + PRInt32 bytesNeeded; + + sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); + if (!recvBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + + while(numRequests > 0) { + + if ( (numRequests % 10) == 0 ) + if (debug_mode) { + printf("."); + } + if (debug_mode) { + DPRINTF("\tClient starting request %d\n", numRequests); + } + + clientSocket = PR_NewTCPSocket(); + if (!clientSocket) { + if (debug_mode) printf("Client error creating socket: OS error %d\n", + PR_GetOSError()); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connecting\n"); + } + + rv = PR_Connect(clientSocket, + &serverAddr, + PR_INTERVAL_NO_TIMEOUT); + if (!clientSocket) { + if (debug_mode) { + printf("\tClient error connecting\n"); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connected\n"); + } + + rv = PR_Send(clientSocket, + sendBuf, + _client_data, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv != _client_data) { + if (debug_mode) { + printf("Client error sending data (%d)\n", rv); + } + PR_Close(clientSocket); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient sent %d bytes\n", rv); + } + + bytesNeeded = _server_data; + while(bytesNeeded) { + rv = PR_Recv(clientSocket, + recvBuf, + bytesNeeded, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv <= 0) { + if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", + rv, (_server_data - bytesNeeded), _server_data); + break; + } + if (debug_mode) { + DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); + } + bytesNeeded -= rv; + } + + PR_Close(clientSocket); + + PR_AtomicDecrement(&numRequests); + } + + PR_EnterMonitor(clientMonitor); + --numClients; + PR_Notify(clientMonitor); + PR_ExitMonitor(clientMonitor); + + PR_DELETE(sendBuf); + PR_DELETE(recvBuf); +} + +void +RunClients(void) +{ + PRInt32 index; + + numRequests = _iterations; + numClients = _clients; + clientMonitor = PR_NewMonitor(); + + for (index=0; index<_clients; index++) { + PRThread *clientThread; + + + clientThread = PR_CreateThread( + PR_USER_THREAD, + ClientThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ClientScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!clientThread) { + if (debug_mode) { + printf("\terror creating client thread %d\n", index); + } + } else if (debug_mode) { + DPRINTF("\tMain created client %d/%d\n", index+1, _clients); + } + + } + + PR_EnterMonitor(clientMonitor); + while(numClients) { + PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(clientMonitor); +} + +/* --- Main Function ---------------------------------------------- */ + +static +void do_work() +{ + PRThread *ServerThread; + PRInt32 state; + + SetServerState(MAIN, SERVER_STATE_STARTUP); + ServerThread = PR_CreateThread( + PR_USER_THREAD, + ServerThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ServerScope, + PR_JOINABLE_THREAD, + THREAD_STACKSIZE); + if (!ServerThread) { + if (debug_mode) { + printf("error creating main server thread\n"); + } + return; + } + + /* Wait for server to be ready */ + state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); + + if (!(state & SERVER_STATE_DEAD)) { + /* Run Test Clients */ + RunClients(); + + /* Send death signal to server */ + SetServerState(MAIN, SERVER_STATE_DYING); + } + + PR_JoinThread(ServerThread); +} + +static void do_workUU(void) +{ + ServerScope = PR_LOCAL_THREAD; + ClientScope = PR_LOCAL_THREAD; + do_work(); +} + +static void do_workUK(void) +{ + ServerScope = PR_LOCAL_THREAD; + ClientScope = PR_GLOBAL_THREAD; + do_work(); +} + +static void do_workKU(void) +{ + ServerScope = PR_GLOBAL_THREAD; + ClientScope = PR_LOCAL_THREAD; + do_work(); +} + +static void do_workKK(void) +{ + ServerScope = PR_GLOBAL_THREAD; + ClientScope = PR_GLOBAL_THREAD; + do_work(); +} + + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + if (debug_mode) { + printf("\n%40s: %6.2f usec\n", msg, d / _iterations); + } +} + + +int main(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + if (debug_mode) { + printf("Enter number of iterations: \n"); + scanf("%d", &_iterations); + printf("Enter number of clients : \n"); + scanf("%d", &_clients); + printf("Enter size of client data : \n"); + scanf("%d", &_client_data); + printf("Enter size of server data : \n"); + scanf("%d", &_server_data); + } + else + { + _iterations = 7; + _clients = 7; + _client_data = 100; + _server_data = 100; + } + + if (debug_mode) { + printf("\n\n%d iterations with %d client threads.\n", + _iterations, _clients); + printf("Sending %d bytes of client data and %d bytes of server data\n", + _client_data, _server_data); + } + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetThreadRecycleMode(64); + + ServerStateCVLock = PR_NewLock(); + ServerStateCV = PR_NewCondVar(ServerStateCVLock); + + + Measure(do_workKK, "server loop kernel/kernel"); + + PR_Cleanup(); + + if(failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/servr_ku.c b/nsprpub/pr/tests/servr_ku.c new file mode 100644 index 0000000000..d0c446f52f --- /dev/null +++ b/nsprpub/pr/tests/servr_ku.c @@ -0,0 +1,639 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** This server simulates a server running in loopback mode. +** +** The idea is that a single server is created. The server initially creates +** a number of worker threads. Then, with the server running, a number of +** clients are created which start requesting service from the server. +** +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "pprthred.h" + +#include <string.h> + +#define PORT 15004 +#define THREAD_STACKSIZE 0 + +static int _iterations = 1000; +static int _clients = 1; +static int _client_data = 250; +static int _server_data = (8*1024); + +static PRThreadScope ServerScope, ClientScope; + +#define SERVER "Server" +#define MAIN "Main" + +#define SERVER_STATE_STARTUP 0 +#define SERVER_STATE_READY 1 +#define SERVER_STATE_DYING 2 +#define SERVER_STATE_DEAD 4 +int ServerState; +PRLock *ServerStateCVLock; +PRCondVar *ServerStateCV; + +#ifdef DEBUGPRINTS +#define DPRINTF printf +#else +#define DPRINTF +#endif + +PRIntn failed_already=0; +PRIntn debug_mode; + +static void do_work(void); + +/* --- Server state functions --------------------------------------------- */ +void +SetServerState(char *waiter, PRInt32 state) +{ + PR_Lock(ServerStateCVLock); + ServerState = state; + PR_NotifyCondVar(ServerStateCV); + + if (debug_mode) { + DPRINTF("\t%s changed state to %d\n", waiter, state); + } + + PR_Unlock(ServerStateCVLock); +} + +int +WaitServerState(char *waiter, PRInt32 state) +{ + PRInt32 rv; + + PR_Lock(ServerStateCVLock); + + if (debug_mode) { + DPRINTF("\t%s waiting for state %d\n", waiter, state); + } + + while(!(ServerState & state)) { + PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); + } + rv = ServerState; + + if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", + waiter, state, ServerState); + PR_Unlock(ServerStateCVLock); + + return rv; +} + +/* --- Server Functions ------------------------------------------- */ + +PRLock *workerThreadsLock; +PRInt32 workerThreads; +PRInt32 workerThreadsBusy; + +void +WorkerThreadFunc(void *_listenSock) +{ + PRFileDesc *listenSock = (PRFileDesc *)_listenSock; + PRInt32 bytesRead; + PRInt32 bytesWritten; + char *dataBuf; + char *sendBuf; + + if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", + _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); + dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); + if (!dataBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + + if (debug_mode) { + DPRINTF("\tServer worker thread running\n"); + } + + while(1) { + PRInt32 bytesToRead = _client_data; + PRInt32 bytesToWrite = _server_data; + PRFileDesc *newSock; + PRNetAddr *rAddr; + PRInt32 loops = 0; + + loops++; + + if (debug_mode) { + DPRINTF("\tServer thread going into accept\n"); + } + + bytesRead = PR_AcceptRead(listenSock, + &newSock, + &rAddr, + dataBuf, + bytesToRead, + PR_INTERVAL_NO_TIMEOUT); + + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error in accept (%d)\n", bytesRead); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); + } + + PR_AtomicIncrement(&workerThreadsBusy); + if (workerThreadsBusy == workerThreads) { + PR_Lock(workerThreadsLock); + if (workerThreadsBusy == workerThreads) { + PRThread *WorkerThread; + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSock, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("Error creating client thread %d\n", workerThreads); + } + } else { + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer creates worker (%d)\n", workerThreads); + } + } + } + PR_Unlock(workerThreadsLock); + } + + bytesToRead -= bytesRead; + while (bytesToRead) { + bytesRead = PR_Recv(newSock, + dataBuf, + bytesToRead, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error receiving data (%d)\n", bytesRead); + } + continue; + } + if (debug_mode) { + DPRINTF("\tServer received %d bytes\n", bytesRead); + } + } + + bytesWritten = PR_Send(newSock, + sendBuf, + bytesToWrite, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesWritten != _server_data) { + if (debug_mode) printf("\tError sending data to client (%d, %d)\n", + bytesWritten, PR_GetOSError()); + } else { + if (debug_mode) { + DPRINTF("\tServer sent %d bytes\n", bytesWritten); + } + } + + PR_Close(newSock); + PR_AtomicDecrement(&workerThreadsBusy); + } +} + +PRFileDesc * +ServerSetup(void) +{ + PRFileDesc *listenSocket; + PRSocketOptionData sockOpt; + PRNetAddr serverAddr; + PRThread *WorkerThread; + + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + else { + failed_already=1; + } + return NULL; + } + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) { + if (debug_mode) printf("\tServer error setting socket option: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { + if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + else { + failed_already=1; + } + PR_Close(listenSocket); + + return NULL; + } + + /* Create Clients */ + workerThreads = 0; + workerThreadsBusy = 0; + + workerThreadsLock = PR_NewLock(); + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSocket, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("error creating working thread\n"); + } + PR_Close(listenSocket); + return NULL; + } + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer created primordial worker thread\n"); + } + + return listenSocket; +} + +/* The main server loop */ +void +ServerThreadFunc(void *unused) +{ + PRFileDesc *listenSocket; + + /* Do setup */ + listenSocket = ServerSetup(); + + if (!listenSocket) { + SetServerState(SERVER, SERVER_STATE_DEAD); + } else { + + if (debug_mode) { + DPRINTF("\tServer up\n"); + } + + /* Tell clients they can start now. */ + SetServerState(SERVER, SERVER_STATE_READY); + + /* Now wait for server death signal */ + WaitServerState(SERVER, SERVER_STATE_DYING); + + /* Cleanup */ + SetServerState(SERVER, SERVER_STATE_DEAD); + } +} + +/* --- Client Functions ------------------------------------------- */ + +PRInt32 numRequests; +PRInt32 numClients; +PRMonitor *clientMonitor; + +void +ClientThreadFunc(void *unused) +{ + PRNetAddr serverAddr; + PRFileDesc *clientSocket; + char *sendBuf; + char *recvBuf; + PRInt32 rv; + PRInt32 bytesNeeded; + + sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); + if (!recvBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + + while(numRequests > 0) { + + if ( (numRequests % 10) == 0 ) + if (debug_mode) { + printf("."); + } + if (debug_mode) { + DPRINTF("\tClient starting request %d\n", numRequests); + } + + clientSocket = PR_NewTCPSocket(); + if (!clientSocket) { + if (debug_mode) printf("Client error creating socket: OS error %d\n", + PR_GetOSError()); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connecting\n"); + } + + rv = PR_Connect(clientSocket, + &serverAddr, + PR_INTERVAL_NO_TIMEOUT); + if (!clientSocket) { + if (debug_mode) { + printf("\tClient error connecting\n"); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connected\n"); + } + + rv = PR_Send(clientSocket, + sendBuf, + _client_data, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv != _client_data) { + if (debug_mode) { + printf("Client error sending data (%d)\n", rv); + } + PR_Close(clientSocket); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient sent %d bytes\n", rv); + } + + bytesNeeded = _server_data; + while(bytesNeeded) { + rv = PR_Recv(clientSocket, + recvBuf, + bytesNeeded, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv <= 0) { + if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", + rv, (_server_data - bytesNeeded), _server_data); + break; + } + if (debug_mode) { + DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); + } + bytesNeeded -= rv; + } + + PR_Close(clientSocket); + + PR_AtomicDecrement(&numRequests); + } + + PR_EnterMonitor(clientMonitor); + --numClients; + PR_Notify(clientMonitor); + PR_ExitMonitor(clientMonitor); + + PR_DELETE(sendBuf); + PR_DELETE(recvBuf); +} + +void +RunClients(void) +{ + PRInt32 index; + + numRequests = _iterations; + numClients = _clients; + clientMonitor = PR_NewMonitor(); + + for (index=0; index<_clients; index++) { + PRThread *clientThread; + + + clientThread = PR_CreateThread( + PR_USER_THREAD, + ClientThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ClientScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!clientThread) { + if (debug_mode) { + printf("\terror creating client thread %d\n", index); + } + } else if (debug_mode) { + DPRINTF("\tMain created client %d/%d\n", index+1, _clients); + } + + } + + PR_EnterMonitor(clientMonitor); + while(numClients) { + PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(clientMonitor); +} + +/* --- Main Function ---------------------------------------------- */ + +static +void do_work() +{ + PRThread *ServerThread; + PRInt32 state; + + SetServerState(MAIN, SERVER_STATE_STARTUP); + ServerThread = PR_CreateThread( + PR_USER_THREAD, + ServerThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ServerScope, + PR_JOINABLE_THREAD, + THREAD_STACKSIZE); + if (!ServerThread) { + if (debug_mode) { + printf("error creating main server thread\n"); + } + return; + } + + /* Wait for server to be ready */ + state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); + + if (!(state & SERVER_STATE_DEAD)) { + /* Run Test Clients */ + RunClients(); + + /* Send death signal to server */ + SetServerState(MAIN, SERVER_STATE_DYING); + } + + PR_JoinThread(ServerThread); +} + + +static void do_workKU(void) +{ + ServerScope = PR_GLOBAL_THREAD; + ClientScope = PR_LOCAL_THREAD; + do_work(); +} + + + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + if (debug_mode) { + printf("\n%40s: %6.2f usec\n", msg, d / _iterations); + } +} + + +int main(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + if (debug_mode) { + printf("Enter number of iterations: \n"); + scanf("%d", &_iterations); + printf("Enter number of clients : \n"); + scanf("%d", &_clients); + printf("Enter size of client data : \n"); + scanf("%d", &_client_data); + printf("Enter size of server data : \n"); + scanf("%d", &_server_data); + } + else + { + _iterations = 7; + _clients = 7; + _client_data = 100; + _server_data = 100; + } + + if (debug_mode) { + printf("\n\n%d iterations with %d client threads.\n", + _iterations, _clients); + printf("Sending %d bytes of client data and %d bytes of server data\n", + _client_data, _server_data); + } + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetThreadRecycleMode(64); + + ServerStateCVLock = PR_NewLock(); + ServerStateCV = PR_NewCondVar(ServerStateCVLock); + + Measure(do_workKU, "server loop kernel/user"); + + PR_Cleanup(); + if(failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/servr_uk.c b/nsprpub/pr/tests/servr_uk.c new file mode 100644 index 0000000000..bd97c8f35a --- /dev/null +++ b/nsprpub/pr/tests/servr_uk.c @@ -0,0 +1,640 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** This server simulates a server running in loopback mode. +** +** The idea is that a single server is created. The server initially creates +** a number of worker threads. Then, with the server running, a number of +** clients are created which start requesting service from the server. +** +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "pprthred.h" + +#include <string.h> + +#define PORT 15004 +#define THREAD_STACKSIZE 0 + +static int _iterations = 1000; +static int _clients = 1; +static int _client_data = 250; +static int _server_data = (8*1024); + +static PRThreadScope ServerScope, ClientScope; + +#define SERVER "Server" +#define MAIN "Main" + +#define SERVER_STATE_STARTUP 0 +#define SERVER_STATE_READY 1 +#define SERVER_STATE_DYING 2 +#define SERVER_STATE_DEAD 4 +int ServerState; +PRLock *ServerStateCVLock; +PRCondVar *ServerStateCV; + +#ifdef DEBUGPRINTS +#define DPRINTF printf +#else +#define DPRINTF +#endif + +PRIntn failed_already=0; +PRIntn debug_mode; + + + +static void do_work(void); + +/* --- Server state functions --------------------------------------------- */ +void +SetServerState(char *waiter, PRInt32 state) +{ + PR_Lock(ServerStateCVLock); + ServerState = state; + PR_NotifyCondVar(ServerStateCV); + + if (debug_mode) { + DPRINTF("\t%s changed state to %d\n", waiter, state); + } + + PR_Unlock(ServerStateCVLock); +} + +int +WaitServerState(char *waiter, PRInt32 state) +{ + PRInt32 rv; + + PR_Lock(ServerStateCVLock); + + if (debug_mode) { + DPRINTF("\t%s waiting for state %d\n", waiter, state); + } + + while(!(ServerState & state)) { + PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); + } + rv = ServerState; + + if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", + waiter, state, ServerState); + PR_Unlock(ServerStateCVLock); + + return rv; +} + +/* --- Server Functions ------------------------------------------- */ + +PRLock *workerThreadsLock; +PRInt32 workerThreads; +PRInt32 workerThreadsBusy; + +void +WorkerThreadFunc(void *_listenSock) +{ + PRFileDesc *listenSock = (PRFileDesc *)_listenSock; + PRInt32 bytesRead; + PRInt32 bytesWritten; + char *dataBuf; + char *sendBuf; + + if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", + _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); + dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); + if (!dataBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + + if (debug_mode) { + DPRINTF("\tServer worker thread running\n"); + } + + while(1) { + PRInt32 bytesToRead = _client_data; + PRInt32 bytesToWrite = _server_data; + PRFileDesc *newSock; + PRNetAddr *rAddr; + PRInt32 loops = 0; + + loops++; + + if (debug_mode) { + DPRINTF("\tServer thread going into accept\n"); + } + + bytesRead = PR_AcceptRead(listenSock, + &newSock, + &rAddr, + dataBuf, + bytesToRead, + PR_INTERVAL_NO_TIMEOUT); + + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error in accept (%d)\n", bytesRead); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); + } + + PR_AtomicIncrement(&workerThreadsBusy); + if (workerThreadsBusy == workerThreads) { + PR_Lock(workerThreadsLock); + if (workerThreadsBusy == workerThreads) { + PRThread *WorkerThread; + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSock, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("Error creating client thread %d\n", workerThreads); + } + } else { + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer creates worker (%d)\n", workerThreads); + } + } + } + PR_Unlock(workerThreadsLock); + } + + bytesToRead -= bytesRead; + while (bytesToRead) { + bytesRead = PR_Recv(newSock, + dataBuf, + bytesToRead, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error receiving data (%d)\n", bytesRead); + } + continue; + } + if (debug_mode) { + DPRINTF("\tServer received %d bytes\n", bytesRead); + } + } + + bytesWritten = PR_Send(newSock, + sendBuf, + bytesToWrite, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesWritten != _server_data) { + if (debug_mode) printf("\tError sending data to client (%d, %d)\n", + bytesWritten, PR_GetOSError()); + } else { + if (debug_mode) { + DPRINTF("\tServer sent %d bytes\n", bytesWritten); + } + } + + PR_Close(newSock); + PR_AtomicDecrement(&workerThreadsBusy); + } +} + +PRFileDesc * +ServerSetup(void) +{ + PRFileDesc *listenSocket; + PRSocketOptionData sockOpt; + PRNetAddr serverAddr; + PRThread *WorkerThread; + + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + else { + return NULL; + } + } + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) { + if (debug_mode) printf("\tServer error setting socket option: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { + if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + else { + failed_already=1; + } + PR_Close(listenSocket); + + return NULL; + } + + /* Create Clients */ + workerThreads = 0; + workerThreadsBusy = 0; + + workerThreadsLock = PR_NewLock(); + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSocket, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("error creating working thread\n"); + } + PR_Close(listenSocket); + return NULL; + } + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer created primordial worker thread\n"); + } + + return listenSocket; +} + +/* The main server loop */ +void +ServerThreadFunc(void *unused) +{ + PRFileDesc *listenSocket; + + /* Do setup */ + listenSocket = ServerSetup(); + + if (!listenSocket) { + SetServerState(SERVER, SERVER_STATE_DEAD); + } else { + + if (debug_mode) { + DPRINTF("\tServer up\n"); + } + + /* Tell clients they can start now. */ + SetServerState(SERVER, SERVER_STATE_READY); + + /* Now wait for server death signal */ + WaitServerState(SERVER, SERVER_STATE_DYING); + + /* Cleanup */ + SetServerState(SERVER, SERVER_STATE_DEAD); + } +} + +/* --- Client Functions ------------------------------------------- */ + +PRInt32 numRequests; +PRInt32 numClients; +PRMonitor *clientMonitor; + +void +ClientThreadFunc(void *unused) +{ + PRNetAddr serverAddr; + PRFileDesc *clientSocket; + char *sendBuf; + char *recvBuf; + PRInt32 rv; + PRInt32 bytesNeeded; + + sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); + if (!recvBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + + while(numRequests > 0) { + + if ( (numRequests % 10) == 0 ) + if (debug_mode) { + printf("."); + } + if (debug_mode) { + DPRINTF("\tClient starting request %d\n", numRequests); + } + + clientSocket = PR_NewTCPSocket(); + if (!clientSocket) { + if (debug_mode) printf("Client error creating socket: OS error %d\n", + PR_GetOSError()); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connecting\n"); + } + + rv = PR_Connect(clientSocket, + &serverAddr, + PR_INTERVAL_NO_TIMEOUT); + if (!clientSocket) { + if (debug_mode) { + printf("\tClient error connecting\n"); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connected\n"); + } + + rv = PR_Send(clientSocket, + sendBuf, + _client_data, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv != _client_data) { + if (debug_mode) { + printf("Client error sending data (%d)\n", rv); + } + PR_Close(clientSocket); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient sent %d bytes\n", rv); + } + + bytesNeeded = _server_data; + while(bytesNeeded) { + rv = PR_Recv(clientSocket, + recvBuf, + bytesNeeded, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv <= 0) { + if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", + rv, (_server_data - bytesNeeded), _server_data); + break; + } + if (debug_mode) { + DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); + } + bytesNeeded -= rv; + } + + PR_Close(clientSocket); + + PR_AtomicDecrement(&numRequests); + } + + PR_EnterMonitor(clientMonitor); + --numClients; + PR_Notify(clientMonitor); + PR_ExitMonitor(clientMonitor); + + PR_DELETE(sendBuf); + PR_DELETE(recvBuf); +} + +void +RunClients(void) +{ + PRInt32 index; + + numRequests = _iterations; + numClients = _clients; + clientMonitor = PR_NewMonitor(); + + for (index=0; index<_clients; index++) { + PRThread *clientThread; + + + clientThread = PR_CreateThread( + PR_USER_THREAD, + ClientThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ClientScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!clientThread) { + if (debug_mode) { + printf("\terror creating client thread %d\n", index); + } + } else if (debug_mode) { + DPRINTF("\tMain created client %d/%d\n", index+1, _clients); + } + + } + + PR_EnterMonitor(clientMonitor); + while(numClients) { + PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(clientMonitor); +} + +/* --- Main Function ---------------------------------------------- */ + +static +void do_work() +{ + PRThread *ServerThread; + PRInt32 state; + + SetServerState(MAIN, SERVER_STATE_STARTUP); + ServerThread = PR_CreateThread( + PR_USER_THREAD, + ServerThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ServerScope, + PR_JOINABLE_THREAD, + THREAD_STACKSIZE); + if (!ServerThread) { + if (debug_mode) { + printf("error creating main server thread\n"); + } + return; + } + + /* Wait for server to be ready */ + state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); + + if (!(state & SERVER_STATE_DEAD)) { + /* Run Test Clients */ + RunClients(); + + /* Send death signal to server */ + SetServerState(MAIN, SERVER_STATE_DYING); + } + + PR_JoinThread(ServerThread); +} + + +static void do_workUK(void) +{ + ServerScope = PR_LOCAL_THREAD; + ClientScope = PR_GLOBAL_THREAD; + do_work(); +} + + + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + if (debug_mode) { + printf("\n%40s: %6.2f usec\n", msg, d / _iterations); + } +} + + +int main(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + if (debug_mode) { + printf("Enter number of iterations: \n"); + scanf("%d", &_iterations); + printf("Enter number of clients : \n"); + scanf("%d", &_clients); + printf("Enter size of client data : \n"); + scanf("%d", &_client_data); + printf("Enter size of server data : \n"); + scanf("%d", &_server_data); + } + else + { + _iterations = 7; + _clients = 7; + _client_data = 100; + _server_data = 100; + } + + if (debug_mode) { + printf("\n\n%d iterations with %d client threads.\n", + _iterations, _clients); + printf("Sending %d bytes of client data and %d bytes of server data\n", + _client_data, _server_data); + } + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetThreadRecycleMode(64); + + ServerStateCVLock = PR_NewLock(); + ServerStateCV = PR_NewCondVar(ServerStateCVLock); + + Measure(do_workUK, "server loop user/kernel"); + + PR_Cleanup(); + + if(failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/servr_uu.c b/nsprpub/pr/tests/servr_uu.c new file mode 100644 index 0000000000..8e77d8fa82 --- /dev/null +++ b/nsprpub/pr/tests/servr_uu.c @@ -0,0 +1,640 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** This server simulates a server running in loopback mode. +** +** The idea is that a single server is created. The server initially creates +** a number of worker threads. Then, with the server running, a number of +** clients are created which start requesting service from the server. +** +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "pprthred.h" + +#include <string.h> + +#define PORT 15004 +#define THREAD_STACKSIZE 0 + +static int _iterations = 1000; +static int _clients = 1; +static int _client_data = 250; +static int _server_data = (8*1024); + +static PRThreadScope ServerScope, ClientScope; + +#define SERVER "Server" +#define MAIN "Main" + +#define SERVER_STATE_STARTUP 0 +#define SERVER_STATE_READY 1 +#define SERVER_STATE_DYING 2 +#define SERVER_STATE_DEAD 4 +int ServerState; +PRLock *ServerStateCVLock; +PRCondVar *ServerStateCV; + +#ifdef DEBUGPRINTS +#define DPRINTF printf +#else +#define DPRINTF +#endif + +PRIntn failed_already=0; +PRIntn debug_mode; + +static void do_work(void); + +/* --- Server state functions --------------------------------------------- */ +void +SetServerState(char *waiter, PRInt32 state) +{ + PR_Lock(ServerStateCVLock); + ServerState = state; + PR_NotifyCondVar(ServerStateCV); + + if (debug_mode) { + DPRINTF("\t%s changed state to %d\n", waiter, state); + } + + PR_Unlock(ServerStateCVLock); +} + +int +WaitServerState(char *waiter, PRInt32 state) +{ + PRInt32 rv; + + PR_Lock(ServerStateCVLock); + + if (debug_mode) { + DPRINTF("\t%s waiting for state %d\n", waiter, state); + } + + while(!(ServerState & state)) { + PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT); + } + rv = ServerState; + + if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n", + waiter, state, ServerState); + PR_Unlock(ServerStateCVLock); + + return rv; +} + +/* --- Server Functions ------------------------------------------- */ + +PRLock *workerThreadsLock; +PRInt32 workerThreads; +PRInt32 workerThreadsBusy; + +void +WorkerThreadFunc(void *_listenSock) +{ + PRFileDesc *listenSock = (PRFileDesc *)_listenSock; + PRInt32 bytesRead; + PRInt32 bytesWritten; + char *dataBuf; + char *sendBuf; + + if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n", + _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32); + dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32); + if (!dataBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tServer could not malloc space!?\n"); + } + + if (debug_mode) { + DPRINTF("\tServer worker thread running\n"); + } + + while(1) { + PRInt32 bytesToRead = _client_data; + PRInt32 bytesToWrite = _server_data; + PRFileDesc *newSock; + PRNetAddr *rAddr; + PRInt32 loops = 0; + + loops++; + + if (debug_mode) { + DPRINTF("\tServer thread going into accept\n"); + } + + bytesRead = PR_AcceptRead(listenSock, + &newSock, + &rAddr, + dataBuf, + bytesToRead, + PR_INTERVAL_NO_TIMEOUT); + + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error in accept (%d)\n", bytesRead); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead); + } + + PR_AtomicIncrement(&workerThreadsBusy); + if (workerThreadsBusy == workerThreads) { + + PR_Lock(workerThreadsLock); + if (workerThreadsBusy == workerThreads) { + PRThread *WorkerThread; + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSock, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("Error creating client thread %d\n", workerThreads); + } + } else { + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer creates worker (%d)\n", workerThreads); + } + } + } + PR_Unlock(workerThreadsLock); + } + + bytesToRead -= bytesRead; + while (bytesToRead) { + bytesRead = PR_Recv(newSock, + dataBuf, + bytesToRead, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesRead < 0) { + if (debug_mode) { + printf("\tServer error receiving data (%d)\n", bytesRead); + } + continue; + } + if (debug_mode) { + DPRINTF("\tServer received %d bytes\n", bytesRead); + } + } + + bytesWritten = PR_Send(newSock, + sendBuf, + bytesToWrite, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (bytesWritten != _server_data) { + if (debug_mode) printf("\tError sending data to client (%d, %d)\n", + bytesWritten, PR_GetOSError()); + } else { + if (debug_mode) { + DPRINTF("\tServer sent %d bytes\n", bytesWritten); + } + } + + PR_Close(newSock); + PR_AtomicDecrement(&workerThreadsBusy); + } +} + +PRFileDesc * +ServerSetup(void) +{ + PRFileDesc *listenSocket; + PRSocketOptionData sockOpt; + PRNetAddr serverAddr; + PRThread *WorkerThread; + + if ( (listenSocket = PR_NewTCPSocket()) == NULL) { + if (debug_mode) { + printf("\tServer error creating listen socket\n"); + } + else { + failed_already=1; + } + return NULL; + } + + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + if ( PR_SetSocketOption(listenSocket, &sockOpt) == PR_FAILURE) { + if (debug_mode) printf("\tServer error setting socket option: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY); + + if ( PR_Bind(listenSocket, &serverAddr) == PR_FAILURE) { + if (debug_mode) printf("\tServer error binding to server address: OS error %d\n", + PR_GetOSError()); + else { + failed_already=1; + } + PR_Close(listenSocket); + return NULL; + } + + if ( PR_Listen(listenSocket, 128) == PR_FAILURE) { + if (debug_mode) { + printf("\tServer error listening to server socket\n"); + } + else { + failed_already=1; + } + PR_Close(listenSocket); + + return NULL; + } + + /* Create Clients */ + workerThreads = 0; + workerThreadsBusy = 0; + + workerThreadsLock = PR_NewLock(); + + WorkerThread = PR_CreateThread( + PR_SYSTEM_THREAD, + WorkerThreadFunc, + listenSocket, + PR_PRIORITY_NORMAL, + ServerScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!WorkerThread) { + if (debug_mode) { + printf("error creating working thread\n"); + } + PR_Close(listenSocket); + return NULL; + } + PR_AtomicIncrement(&workerThreads); + if (debug_mode) { + DPRINTF("\tServer created primordial worker thread\n"); + } + + return listenSocket; +} + +/* The main server loop */ +void +ServerThreadFunc(void *unused) +{ + PRFileDesc *listenSocket; + + /* Do setup */ + listenSocket = ServerSetup(); + + if (!listenSocket) { + SetServerState(SERVER, SERVER_STATE_DEAD); + } else { + + if (debug_mode) { + DPRINTF("\tServer up\n"); + } + + /* Tell clients they can start now. */ + SetServerState(SERVER, SERVER_STATE_READY); + + /* Now wait for server death signal */ + WaitServerState(SERVER, SERVER_STATE_DYING); + + /* Cleanup */ + SetServerState(SERVER, SERVER_STATE_DEAD); + } +} + +/* --- Client Functions ------------------------------------------- */ + +PRInt32 numRequests; +PRInt32 numClients; +PRMonitor *clientMonitor; + +void +ClientThreadFunc(void *unused) +{ + PRNetAddr serverAddr; + PRFileDesc *clientSocket; + char *sendBuf; + char *recvBuf; + PRInt32 rv; + PRInt32 bytesNeeded; + + sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char)); + if (!sendBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char)); + if (!recvBuf) + if (debug_mode) { + printf("\tClient could not malloc space!?\n"); + } + + memset(&serverAddr, 0, sizeof(PRNetAddr)); + serverAddr.inet.family = PR_AF_INET; + serverAddr.inet.port = PR_htons(PORT); + serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + + while(numRequests > 0) { + + if ( (numRequests % 10) == 0 ) + if (debug_mode) { + printf("."); + } + if (debug_mode) { + DPRINTF("\tClient starting request %d\n", numRequests); + } + + clientSocket = PR_NewTCPSocket(); + if (!clientSocket) { + if (debug_mode) printf("Client error creating socket: OS error %d\n", + PR_GetOSError()); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connecting\n"); + } + + rv = PR_Connect(clientSocket, + &serverAddr, + PR_INTERVAL_NO_TIMEOUT); + if (!clientSocket) { + if (debug_mode) { + printf("\tClient error connecting\n"); + } + continue; + } + + if (debug_mode) { + DPRINTF("\tClient connected\n"); + } + + rv = PR_Send(clientSocket, + sendBuf, + _client_data, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv != _client_data) { + if (debug_mode) { + printf("Client error sending data (%d)\n", rv); + } + PR_Close(clientSocket); + continue; + } + + if (debug_mode) { + DPRINTF("\tClient sent %d bytes\n", rv); + } + + bytesNeeded = _server_data; + while(bytesNeeded) { + rv = PR_Recv(clientSocket, + recvBuf, + bytesNeeded, + 0, + PR_INTERVAL_NO_TIMEOUT); + if (rv <= 0) { + if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n", + rv, (_server_data - bytesNeeded), _server_data); + break; + } + if (debug_mode) { + DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv); + } + bytesNeeded -= rv; + } + + PR_Close(clientSocket); + + PR_AtomicDecrement(&numRequests); + } + + PR_EnterMonitor(clientMonitor); + --numClients; + PR_Notify(clientMonitor); + PR_ExitMonitor(clientMonitor); + + PR_DELETE(sendBuf); + PR_DELETE(recvBuf); +} + +void +RunClients(void) +{ + PRInt32 index; + + numRequests = _iterations; + numClients = _clients; + clientMonitor = PR_NewMonitor(); + + for (index=0; index<_clients; index++) { + PRThread *clientThread; + + + clientThread = PR_CreateThread( + PR_USER_THREAD, + ClientThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ClientScope, + PR_UNJOINABLE_THREAD, + THREAD_STACKSIZE); + + if (!clientThread) { + if (debug_mode) { + printf("\terror creating client thread %d\n", index); + } + } else if (debug_mode) { + DPRINTF("\tMain created client %d/%d\n", index+1, _clients); + } + + } + + PR_EnterMonitor(clientMonitor); + while(numClients) { + PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(clientMonitor); +} + +/* --- Main Function ---------------------------------------------- */ + +static +void do_work() +{ + PRThread *ServerThread; + PRInt32 state; + + SetServerState(MAIN, SERVER_STATE_STARTUP); + ServerThread = PR_CreateThread( + PR_USER_THREAD, + ServerThreadFunc, + NULL, + PR_PRIORITY_NORMAL, + ServerScope, + PR_JOINABLE_THREAD, + THREAD_STACKSIZE); + if (!ServerThread) { + if (debug_mode) { + printf("error creating main server thread\n"); + } + return; + } + + /* Wait for server to be ready */ + state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD); + + if (!(state & SERVER_STATE_DEAD)) { + /* Run Test Clients */ + RunClients(); + + /* Send death signal to server */ + SetServerState(MAIN, SERVER_STATE_DYING); + } + + PR_JoinThread(ServerThread); +} + +static void do_workUU(void) +{ + ServerScope = PR_LOCAL_THREAD; + ClientScope = PR_LOCAL_THREAD; + do_work(); +} + + + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + + if (debug_mode) { + printf("\n%40s: %6.2f usec\n", msg, d / _iterations); + } +} + + +int main(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + if (debug_mode) { + printf("Enter number of iterations: \n"); + scanf("%d", &_iterations); + printf("Enter number of clients : \n"); + scanf("%d", &_clients); + printf("Enter size of client data : \n"); + scanf("%d", &_client_data); + printf("Enter size of server data : \n"); + scanf("%d", &_server_data); + } + else + { + _iterations = 7; + _clients = 7; + _client_data = 100; + _server_data = 100; + } + + if (debug_mode) { + printf("\n\n%d iterations with %d client threads.\n", + _iterations, _clients); + printf("Sending %d bytes of client data and %d bytes of server data\n", + _client_data, _server_data); + } + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetThreadRecycleMode(64); + + ServerStateCVLock = PR_NewLock(); + ServerStateCV = PR_NewCondVar(ServerStateCVLock); + + Measure(do_workUU, "server loop user/user"); + + PR_Cleanup(); + + if(failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/short_thread.c b/nsprpub/pr/tests/short_thread.c new file mode 100644 index 0000000000..de069e8967 --- /dev/null +++ b/nsprpub/pr/tests/short_thread.c @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdio.h> +#include "nspr.h" +#include "plgetopt.h" + +/* + * Create a thread that exits right away; useful for testing race conditions in thread + * creation + */ + +int _debug_on = 0; +#define DPRINTF(arg) if (_debug_on) printf arg + +static void housecleaning(void *cur_time); + +int main (int argc, char **argv) +{ + static PRIntervalTime thread_start_time; + static PRThread *housekeeping_tid = NULL; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (( housekeeping_tid = + PR_CreateThread (PR_USER_THREAD, housecleaning, (void*)&thread_start_time, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0)) + == NULL ) { + fprintf(stderr, + "simple_test: Error - PR_CreateThread failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + exit( 1 ); + } + PR_Cleanup(); + return(0); +} + +static void +housecleaning (void *cur_time) +{ + DPRINTF(("Child Thread exiting\n")); +} diff --git a/nsprpub/pr/tests/sigpipe.c b/nsprpub/pr/tests/sigpipe.c new file mode 100644 index 0000000000..02ba9114f2 --- /dev/null +++ b/nsprpub/pr/tests/sigpipe.c @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + ************************************************************************* + * + * Test: sigpipe.c + * + * Test the SIGPIPE handler in NSPR. This test applies to Unix only. + * + ************************************************************************* + */ + +#if !defined(XP_UNIX) && !defined(XP_OS2) + +int main(void) +{ + /* This test applies to Unix and OS/2. */ + return 0; +} + +#else /* XP_UNIX && OS/2 */ + +#include "nspr.h" + +#ifdef XP_OS2 +#define INCL_DOSQUEUES +#define INCL_DOSERRORS +#include <os2.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +static void Test(void *arg) +{ +#ifdef XP_OS2 + HFILE pipefd[2]; +#else + int pipefd[2]; +#endif + int rv; + char c = '\0'; + +#ifdef XP_OS2 + if (DosCreatePipe(&pipefd[0], &pipefd[1], 4096) != 0) { +#else + if (pipe(pipefd) == -1) { +#endif + fprintf(stderr, "cannot create pipe: %d\n", errno); + exit(1); + } + close(pipefd[0]); + + rv = write(pipefd[1], &c, 1); + if (rv != -1) { + fprintf(stderr, "write to broken pipe should have failed with EPIPE but returned %d\n", rv); + exit(1); + } + if (errno != EPIPE) { + fprintf(stderr, "write to broken pipe failed but with wrong errno: %d\n", errno); + exit(1); + } + close(pipefd[1]); + printf("write to broken pipe failed with EPIPE, as expected\n"); +} + +int main(int argc, char **argv) +{ + PRThread *thread; + + /* This initializes NSPR. */ + PR_SetError(0, 0); + + thread = PR_CreateThread(PR_USER_THREAD, Test, NULL, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (thread == NULL) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + if (PR_JoinThread(thread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + Test(NULL); + + printf("PASSED\n"); + return 0; +} + +#endif /* XP_UNIX */ diff --git a/nsprpub/pr/tests/sleep.c b/nsprpub/pr/tests/sleep.c new file mode 100644 index 0000000000..3e00acab01 --- /dev/null +++ b/nsprpub/pr/tests/sleep.c @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +#if defined(XP_UNIX) || defined(XP_OS2) + +#include <stdio.h> + +#ifndef XP_OS2 +#include <unistd.h> +#endif +#include <sys/time.h> + +#if defined(HAVE_SVID_GETTOD) +#define GTOD(_a) gettimeofday(_a) +#else +#define GTOD(_a) gettimeofday((_a), NULL) +#endif + +static PRIntn rv = 0; + +static void Other(void *unused) +{ + PRIntn didit = 0; + while (PR_SUCCESS == PR_Sleep(PR_MillisecondsToInterval(250))) + { + fprintf(stderr, "."); + didit += 1; + } + if (didit < 5) { + rv = 1; + } +} + +int main(int argc, char **argv) +{ + PRUint32 elapsed; + PRThread *thread; + struct timeval timein, timeout; + PRInt32 onePercent = 3000000UL / 100UL; + + fprintf (stderr, "First sleep will sleep 3 seconds.\n"); + fprintf (stderr, " sleep 1 begin\n"); + (void)GTOD(&timein); + sleep (3); + (void)GTOD(&timeout); + fprintf (stderr, " sleep 1 end\n"); + elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec); + elapsed += (timeout.tv_usec - timein.tv_usec); + fprintf(stderr, "elapsed %u usecs\n", elapsed); + if (labs(elapsed - 3000000UL) > onePercent) { + rv = 1; + } + + PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 100); + PR_STDIO_INIT(); + + fprintf (stderr, "Second sleep should do the same (does it?).\n"); + fprintf (stderr, " sleep 2 begin\n"); + (void)GTOD(&timein); + sleep (3); + (void)GTOD(&timeout); + fprintf (stderr, " sleep 2 end\n"); + elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec); + elapsed += (timeout.tv_usec - timein.tv_usec); + fprintf(stderr, "elapsed %u usecs\n", elapsed); + if (labs(elapsed - 3000000UL) > onePercent) { + rv = 1; + } + + fprintf (stderr, "What happens to other threads?\n"); + fprintf (stderr, "You should see dots every quarter second.\n"); + fprintf (stderr, "If you don't, you're probably running on classic NSPR.\n"); + thread = PR_CreateThread( + PR_USER_THREAD, Other, NULL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + fprintf (stderr, " sleep 2 begin\n"); + (void)GTOD(&timein); + sleep (3); + (void)GTOD(&timeout); + fprintf (stderr, " sleep 2 end\n"); + PR_Interrupt(thread); + PR_JoinThread(thread); + elapsed = 1000000UL * (timeout.tv_sec - timein.tv_sec); + elapsed += (timeout.tv_usec - timein.tv_usec); + fprintf(stderr, "elapsed %u usecs\n", elapsed); + if (labs(elapsed - 3000000UL) > onePercent) { + rv = 1; + } + fprintf(stderr, "%s\n", (0 == rv) ? "PASSED" : "FAILED"); + return rv; +} + +#else /* defined(XP_UNIX) */ + +PRIntn main() +{ + return 2; +} + +#endif /* defined(XP_UNIX) */ + diff --git a/nsprpub/pr/tests/socket.c b/nsprpub/pr/tests/socket.c new file mode 100644 index 0000000000..232898f69e --- /dev/null +++ b/nsprpub/pr/tests/socket.c @@ -0,0 +1,2286 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: socket.c +** +** Description: Test socket functionality. +** +** Modification History: +*/ +#include "primpl.h" + +#include "plgetopt.h" + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#ifdef XP_UNIX +#include <sys/mman.h> +#endif +#if defined(_PR_PTHREADS) +#include <pthread.h> +#endif + +#ifdef WIN32 +#include <process.h> +#endif + +static int _debug_on = 0; +static int test_cancelio = 0; + +#include "obsolete/prsem.h" + +#ifdef XP_PC +#define mode_t int +#endif + +#define DPRINTF(arg) if (_debug_on) printf arg + +#ifdef XP_PC +char *TEST_DIR = "prdir"; +char *SMALL_FILE_NAME = "prsmallf"; +char *LARGE_FILE_NAME = "prlargef"; +#else +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 (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_OFFSET_4 PR_GetPageSize() +#define LARGE_FILE_LEN_4 769 +#define LARGE_FILE_HEADER_SIZE (512) +#define LARGE_FILE_TRAILER_SIZE (64) + +#define BUF_DATA_SIZE (2 * 1024) +#define TCP_MESG_SIZE 1024 +/* + * set UDP datagram size small enough that datagrams sent to a port on the + * local host will not be lost + */ +#define UDP_DGRAM_SIZE 128 +#define NUM_TCP_CLIENTS 5 /* for a listen queue depth of 5 */ +#define NUM_UDP_CLIENTS 10 + +#define NUM_TRANSMITFILE_CLIENTS 4 + +#define NUM_TCP_CONNECTIONS_PER_CLIENT 5 +#define NUM_TCP_MESGS_PER_CONNECTION 10 +#define NUM_UDP_DATAGRAMS_PER_CLIENT 5 +#define TCP_SERVER_PORT 10000 +#define UDP_SERVER_PORT TCP_SERVER_PORT +#define SERVER_MAX_BIND_COUNT 100 + +#ifdef WINCE +#define perror(s) +#endif + +static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS; +static PRInt32 num_udp_clients = NUM_UDP_CLIENTS; +static PRInt32 num_transmitfile_clients = NUM_TRANSMITFILE_CLIENTS; +static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT; +static PRInt32 tcp_mesg_size = TCP_MESG_SIZE; +static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION; +static PRInt32 num_udp_datagrams_per_client = NUM_UDP_DATAGRAMS_PER_CLIENT; +static PRInt32 udp_datagram_size = UDP_DGRAM_SIZE; + +static PRInt32 thread_count; +PRUint16 server_domain = PR_AF_INET, client_domain = PR_AF_INET; + +/* an I/O layer that uses the emulated senfile method */ +static PRDescIdentity emuSendFileIdentity; +static PRIOMethods emuSendFileMethods; + +int failed_already=0; +typedef struct buffer { + char data[BUF_DATA_SIZE]; +} buffer; + +PRNetAddr tcp_server_addr, udp_server_addr; + +typedef struct Serve_Client_Param { + PRFileDesc *sockfd; /* socket to read from/write to */ + PRInt32 datalen; /* bytes of data transfered in each read/write */ +} Serve_Client_Param; + +typedef struct Server_Param { + PRSemaphore *addr_sem; /* sem to post on, after setting up the address */ + PRMonitor *exit_mon; /* monitor to signal on exit */ + PRInt32 *exit_counter; /* counter to decrement, before exit */ + PRInt32 datalen; /* bytes of data transfered in each read/write */ +} Server_Param; + + +typedef struct Client_Param { + PRNetAddr server_addr; + PRMonitor *exit_mon; /* monitor to signal on exit */ + PRInt32 *exit_counter; /* counter to decrement, before exit */ + PRInt32 datalen; + PRInt32 udp_connect; /* if set clients connect udp sockets */ +} Client_Param; + +/* the sendfile method in emuSendFileMethods */ +static PRInt32 PR_CALLBACK +emu_SendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + return PR_EmulateSendFile(sd, sfd, flags, timeout); +} + +/* the transmitfile method in emuSendFileMethods */ +static PRInt32 PR_CALLBACK +emu_TransmitFile(PRFileDesc *sd, PRFileDesc *fd, const void *headers, + PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + PRSendFileData sfd; + + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; + return emu_SendFile(sd, &sfd, flags, timeout); +} + +/* + * readn + * read data from sockfd into buf + */ +static PRInt32 +readn(PRFileDesc *sockfd, char *buf, int len) +{ + int rem; + int bytes; + int offset = 0; + int err; + 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, + timeout); + DPRINTF(("thread = 0x%lx: returning from PR_Recv, bytes = %d\n", + PR_GetCurrentThread(), bytes)); + 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; +} + +/* + * writen + * write data from buf to sockfd + */ +static PRInt32 +writen(PRFileDesc *sockfd, char *buf, int len) +{ + int rem; + int bytes; + int offset = 0; + + for (rem=len; rem; offset += bytes, rem -= bytes) { + DPRINTF(("thread = 0x%lx: calling PR_Send, bytes = %d\n", + PR_GetCurrentThread(), rem)); + bytes = PR_Send(sockfd, buf + offset, rem, 0, + PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("thread = 0x%lx: returning from PR_Send, bytes = %d\n", + PR_GetCurrentThread(), bytes)); + if (bytes <= 0) { + return -1; + } + } + return len; +} + +/* + * Serve_Client + * Thread, started by the server, for serving a client connection. + * Reads data from socket and writes it back, unmodified, and + * closes the socket + */ +static void PR_CALLBACK +Serve_Client(void *arg) +{ + Serve_Client_Param *scp = (Serve_Client_Param *) arg; + PRFileDesc *sockfd; + buffer *in_buf; + PRInt32 bytes, j; + + sockfd = scp->sockfd; + bytes = scp->datalen; + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); + failed_already=1; + goto exit; + } + + + for (j = 0; j < num_tcp_mesgs_per_connection; j++) { + /* + * Read data from client and send it back to the client unmodified + */ + if (readn(sockfd, in_buf->data, bytes) < bytes) { + fprintf(stderr,"prsocket_test: ERROR - Serve_Client:readn\n"); + failed_already=1; + goto exit; + } + /* Shutdown only RCV will cause error on Symbian OS */ + /* + * shutdown reads, after the last read + */ + if (j == num_tcp_mesgs_per_connection - 1) + if (PR_Shutdown(sockfd, PR_SHUTDOWN_RCV) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n"); + } + DPRINTF(("Serve_Client [0x%lx]: inbuf[0] = 0x%lx\n",PR_GetCurrentThread(), + (*((int *) in_buf->data)))); + if (writen(sockfd, in_buf->data, bytes) < bytes) { + fprintf(stderr,"prsocket_test: ERROR - Serve_Client:writen\n"); + failed_already=1; + goto exit; + } + } + /* + * shutdown reads and writes + */ + if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n"); + failed_already=1; + } + +exit: + PR_Close(sockfd); + if (in_buf) { + PR_DELETE(in_buf); + } +} + +PRThread* create_new_thread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, PRInt32 index) +{ + PRInt32 native_thread = 0; + + PR_ASSERT(state == PR_UNJOINABLE_THREAD); +#if defined(_PR_PTHREADS) || defined(WIN32) + switch(index % 4) { + case 0: + scope = (PR_LOCAL_THREAD); + break; + case 1: + scope = (PR_GLOBAL_THREAD); + break; + case 2: + scope = (PR_GLOBAL_BOUND_THREAD); + break; + case 3: + native_thread = 1; + break; + default: + PR_NOT_REACHED("Invalid scope"); + break; + } + if (native_thread) { +#if defined(_PR_PTHREADS) + pthread_t tid; + if (!pthread_create(&tid, NULL, (void * (*)(void *)) start, arg)) { + return((PRThread *) tid); + } + else { + return (NULL); + } +#else + HANDLE thandle; + unsigned tid; + + thandle = (HANDLE) _beginthreadex( + NULL, + stackSize, + (unsigned (__stdcall *)(void *))start, + arg, + STACK_SIZE_PARAM_IS_A_RESERVATION, + &tid); + return((PRThread *) thandle); +#endif + } else { + return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize)); + } +#else + return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize)); +#endif +} + +/* + * TCP Server + * Server Thread + * Bind an address to a socket and listen for incoming connections + * Start a Serve_Client thread for each incoming connection. + */ +static void PR_CALLBACK +TCP_Server(void *arg) +{ + PRThread *t; + Server_Param *sp = (Server_Param *) arg; + Serve_Client_Param *scp; + PRFileDesc *sockfd, *newsockfd; + PRNetAddr netaddr; + PRInt32 i; + /* + * Create a tcp socket + */ + if ((sockfd = PR_OpenTCPSocket(server_domain)) == NULL) { + fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n"); + goto exit; + } + memset(&netaddr, 0, sizeof(netaddr)); + + if (PR_SetNetAddr(PR_IpAddrAny, server_domain, TCP_SERVER_PORT, + &netaddr) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n"); + goto exit; + } + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + + while (PR_Bind(sockfd, &netaddr) < 0) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + netaddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n"); + perror("PR_Bind"); + failed_already=1; + goto exit; + } + + if (PR_Listen(sockfd, 32) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_Listen failed\n"); + failed_already=1; + goto exit; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n"); + failed_already=1; + goto exit; + } + + DPRINTF(("TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", + netaddr.inet.ip, netaddr.inet.port)); + if (PR_SetNetAddr(PR_IpAddrLoopback, client_domain, + PR_ntohs(PR_NetAddrInetPort(&netaddr)), + &tcp_server_addr) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n"); + goto exit; + } + if ((client_domain == PR_AF_INET6) && (server_domain == PR_AF_INET)) + PR_ConvertIPv4AddrToIPv6(PR_htonl(INADDR_LOOPBACK), + &tcp_server_addr.ipv6.ip); + + /* + * Wake up parent thread because server address is bound and made + * available in the global variable 'tcp_server_addr' + */ + PR_PostSem(sp->addr_sem); + + for (i = 0; i < (num_tcp_clients * num_tcp_connections_per_client); i++) { + /* test both null and non-null 'addr' argument to PR_Accept */ + PRNetAddr *addrp = (i%2 ? &netaddr: NULL); + + DPRINTF(("TCP_Server: Accepting connection\n")); + if ((newsockfd = PR_Accept(sockfd, addrp, + PR_INTERVAL_NO_TIMEOUT)) == NULL) { + fprintf(stderr,"prsocket_test: ERROR - PR_Accept failed\n"); + goto exit; + } + DPRINTF(("TCP_Server: Accepted connection\n")); + scp = PR_NEW(Serve_Client_Param); + if (scp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + goto exit; + } + + /* + * Start a Serve_Client thread for each incoming connection + */ + scp->sockfd = newsockfd; + scp->datalen = sp->datalen; + + t = create_new_thread(PR_USER_THREAD, + Serve_Client, (void *)scp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, i); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + goto exit; + } + DPRINTF(("TCP_Server: Created Serve_Client = 0x%lx\n", t)); + } + +exit: + if (sockfd) { + PR_Close(sockfd); + } + + /* + * Decrement exit_counter and notify parent thread + */ + + PR_EnterMonitor(sp->exit_mon); + --(*sp->exit_counter); + PR_Notify(sp->exit_mon); + PR_ExitMonitor(sp->exit_mon); + DPRINTF(("TCP_Server [0x%lx] exiting\n", PR_GetCurrentThread())); +} + +/* + * UDP Server + * Server Thread + * Bind an address to a socket, read data from clients and send data + * back to clients + */ +static void PR_CALLBACK +UDP_Server(void *arg) +{ + Server_Param *sp = (Server_Param *) arg; + PRFileDesc *sockfd; + buffer *in_buf; + PRNetAddr netaddr; + PRInt32 bytes, i, rv = 0; + + + bytes = sp->datalen; + /* + * Create a udp socket + */ + if ((sockfd = PR_OpenUDPSocket(server_domain)) == NULL) { + fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n"); + failed_already=1; + return; + } + memset(&netaddr, 0, sizeof(netaddr)); + if (PR_SetNetAddr(PR_IpAddrAny, server_domain, UDP_SERVER_PORT, + &netaddr) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n"); + failed_already=1; + return; + } + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + while (PR_Bind(sockfd, &netaddr) < 0) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + netaddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n"); + perror("PR_Bind"); + failed_already=1; + return; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n"); + 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)); + /* + * We can't use the IP address returned by PR_GetSockName in + * netaddr.inet.ip because netaddr.inet.ip is returned + * as 0 (= PR_INADDR_ANY). + */ + + if (PR_SetNetAddr(PR_IpAddrLoopback, client_domain, + PR_ntohs(PR_NetAddrInetPort(&netaddr)), + &udp_server_addr) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n"); + failed_already=1; + return; + } + if ((client_domain == PR_AF_INET6) && (server_domain == PR_AF_INET)) + PR_ConvertIPv4AddrToIPv6(PR_htonl(INADDR_LOOPBACK), + &udp_server_addr.ipv6.ip); + + /* + * Wake up parent thread because server address is bound and made + * available in the global variable 'udp_server_addr' + */ + PR_PostSem(sp->addr_sem); + + bytes = sp->datalen; + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); + failed_already=1; + return; + } + /* + * Receive datagrams from clients and send them back, unmodified, to the + * clients + */ + memset(&netaddr, 0, sizeof(netaddr)); + for (i = 0; i < (num_udp_clients * num_udp_datagrams_per_client); i++) { + DPRINTF(("UDP_Server: calling PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n", + netaddr.inet.ip, netaddr.inet.port, bytes, in_buf->data, + in_buf->data[0])); + + rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr, + PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("UDP_Server: PR_RecvFrom client - ip = 0x%lx, port = %d bytes = %d inbuf = 0x%lx, inbuf[0] = 0x%lx\n", + netaddr.inet.ip, netaddr.inet.port, rv, in_buf->data, + in_buf->data[0])); + if (rv != bytes) { + return; + } + rv = PR_SendTo(sockfd, in_buf->data, bytes, 0, &netaddr, + PR_INTERVAL_NO_TIMEOUT); + if (rv != bytes) { + return; + } + } + + PR_DELETE(in_buf); + PR_Close(sockfd); + + /* + * Decrement exit_counter and notify parent thread + */ + PR_EnterMonitor(sp->exit_mon); + --(*sp->exit_counter); + PR_Notify(sp->exit_mon); + PR_ExitMonitor(sp->exit_mon); + DPRINTF(("UDP_Server [0x%x] exiting\n", PR_GetCurrentThread())); +} + +/* + * TCP_Client + * Client Thread + * Connect to the server at the address specified in the argument. + * Fill in a buffer, write data to server, read it back and check + * for data corruption. + * Close the socket for server connection + */ +static void PR_CALLBACK +TCP_Client(void *arg) +{ + Client_Param *cp = (Client_Param *) arg; + PRFileDesc *sockfd; + buffer *in_buf, *out_buf; + union PRNetAddr netaddr; + PRInt32 bytes, i, j; + + + bytes = cp->datalen; + out_buf = PR_NEW(buffer); + if (out_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); + failed_already=1; + return; + } + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); + failed_already=1; + return; + } + netaddr = cp->server_addr; + + for (i = 0; i < num_tcp_connections_per_client; i++) { + if ((sockfd = PR_OpenTCPSocket(client_domain)) == 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, "PR_Connect failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + return; + } + for (j = 0; j < num_tcp_mesgs_per_connection; j++) { + /* + * fill in random data + */ + memset(out_buf->data, ((PRInt32) (&netaddr)) + i + j, bytes); + /* + * 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; + return; + } + DPRINTF(("TCP Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n", + PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data)))); + if (readn(sockfd, in_buf->data, bytes) < bytes) { + fprintf(stderr,"prsocket_test: ERROR - TCP_Client:readn\n"); + failed_already=1; + return; + } + /* + * verify the data read + */ + if (memcmp(in_buf->data, out_buf->data, bytes) != 0) { + fprintf(stderr,"prsocket_test: ERROR - data corruption\n"); + failed_already=1; + return; + } + } + /* + * shutdown reads and writes + */ + if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_Shutdown\n"); + failed_already=1; + } + PR_Close(sockfd); + } + + PR_DELETE(out_buf); + PR_DELETE(in_buf); + + /* + * Decrement exit_counter and notify parent thread + */ + + PR_EnterMonitor(cp->exit_mon); + --(*cp->exit_counter); + PR_Notify(cp->exit_mon); + PR_ExitMonitor(cp->exit_mon); + DPRINTF(("TCP_Client [0x%x] exiting\n", PR_GetCurrentThread())); +} + +/* + * UDP_Client + * Client Thread + * Create a socket and bind an address + * Communicate with the server at the address specified in the argument. + * Fill in a buffer, write data to server, read it back and check + * for data corruption. + * Close the socket + */ +static void PR_CALLBACK +UDP_Client(void *arg) +{ + Client_Param *cp = (Client_Param *) arg; + PRFileDesc *sockfd; + buffer *in_buf, *out_buf; + union PRNetAddr netaddr; + PRInt32 bytes, i, rv; + + + bytes = cp->datalen; + out_buf = PR_NEW(buffer); + if (out_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); + failed_already=1; + return; + } + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer struct\n"); + failed_already=1; + return; + } + if ((sockfd = PR_OpenUDPSocket(client_domain)) == NULL) { + fprintf(stderr,"prsocket_test: PR_OpenUDPSocket failed\n"); + failed_already=1; + return; + } + + /* + * bind an address for the client, let the system chose the port + * number + */ + memset(&netaddr, 0, sizeof(netaddr)); + if (PR_SetNetAddr(PR_IpAddrAny, client_domain, 0, + &netaddr) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: PR_SetNetAddr failed\n"); + failed_already=1; + return; + } + if (PR_Bind(sockfd, &netaddr) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n"); + perror("PR_Bind"); + return; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_GetSockName failed\n"); + failed_already=1; + return; + } + + DPRINTF(("PR_Bind: UDP Client netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", + netaddr.inet.ip, netaddr.inet.port)); + + netaddr = cp->server_addr; + + if (cp->udp_connect) { + if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) { + fprintf(stderr,"prsocket_test: PR_Connect failed\n"); + failed_already=1; + return; + } + } + + for (i = 0; i < num_udp_datagrams_per_client; i++) { + /* + * fill in random data + */ + DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx bytes = 0x%lx\n", + PR_GetCurrentThread(), out_buf->data, bytes)); + memset(out_buf->data, ((PRInt32) (&netaddr)) + i, bytes); + /* + * write to server + */ + if (cp->udp_connect) + rv = PR_Send(sockfd, out_buf->data, bytes, 0, + PR_INTERVAL_NO_TIMEOUT); + else + rv = PR_SendTo(sockfd, out_buf->data, bytes, 0, &netaddr, + PR_INTERVAL_NO_TIMEOUT); + if (rv != bytes) { + return; + } + DPRINTF(("UDP_Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n", + PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data)))); + if (cp->udp_connect) + rv = PR_Recv(sockfd, in_buf->data, bytes, 0, + PR_INTERVAL_NO_TIMEOUT); + else + rv = PR_RecvFrom(sockfd, in_buf->data, bytes, 0, &netaddr, + PR_INTERVAL_NO_TIMEOUT); + if (rv != bytes) { + return; + } + DPRINTF(("UDP_Client [0x%lx]: in_buf = 0x%lx in_buf[0] = 0x%lx\n", + PR_GetCurrentThread(), in_buf, (*((int *) in_buf->data)))); + /* + * verify the data read + */ + if (memcmp(in_buf->data, out_buf->data, bytes) != 0) { + fprintf(stderr,"prsocket_test: ERROR - UDP data corruption\n"); + failed_already=1; + return; + } + } + PR_Close(sockfd); + + PR_DELETE(in_buf); + PR_DELETE(out_buf); + + /* + * Decrement exit_counter and notify parent thread + */ + + PR_EnterMonitor(cp->exit_mon); + --(*cp->exit_counter); + PR_Notify(cp->exit_mon); + PR_ExitMonitor(cp->exit_mon); + PR_DELETE(cp); + DPRINTF(("UDP_Client [0x%x] exiting\n", PR_GetCurrentThread())); +} + +/* + * TCP_Socket_Client_Server_Test - concurrent server test + * + * One server and several clients are started + * Each client connects to the server and sends a chunk of data + * For each connection, server starts another thread to read the data + * from the client and send it back to the client, unmodified. + * Each client checks that data received from server is same as the + * data it sent to the server. + * + */ + +static PRInt32 +TCP_Socket_Client_Server_Test(void) +{ + int i; + PRThread *t; + PRSemaphore *server_sem; + Server_Param *sparamp; + Client_Param *cparamp; + PRMonitor *mon2; + PRInt32 datalen; + + + datalen = tcp_mesg_size; + thread_count = 0; + /* + * start the server thread + */ + sparamp = PR_NEW(Server_Param); + if (sparamp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + return -1; + } + server_sem = PR_NewSem(0); + if (server_sem == NULL) { + fprintf(stderr,"prsocket_test: PR_NewSem failed\n"); + failed_already=1; + return -1; + } + mon2 = PR_NewMonitor(); + if (mon2 == NULL) { + fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n"); + failed_already=1; + return -1; + } + PR_EnterMonitor(mon2); + + sparamp->addr_sem = server_sem; + sparamp->exit_mon = mon2; + sparamp->exit_counter = &thread_count; + sparamp->datalen = datalen; + t = PR_CreateThread(PR_USER_THREAD, + TCP_Server, (void *)sparamp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + return -1; + } + DPRINTF(("Created TCP server = 0x%lx\n", t)); + thread_count++; + + /* + * wait till the server address is setup + */ + PR_WaitSem(server_sem); + + /* + * Now start a bunch of client threads + */ + + cparamp = PR_NEW(Client_Param); + if (cparamp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + return -1; + } + cparamp->server_addr = tcp_server_addr; + cparamp->exit_mon = mon2; + cparamp->exit_counter = &thread_count; + cparamp->datalen = datalen; + for (i = 0; i < num_tcp_clients; i++) { + t = create_new_thread(PR_USER_THREAD, + TCP_Client, (void *) cparamp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, i); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + return -1; + } + DPRINTF(("Created TCP client = 0x%lx\n", t)); + thread_count++; + } + /* Wait for server and client threads to exit */ + while (thread_count) { + PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("TCP Server - thread_count = %d\n", thread_count)); + } + PR_ExitMonitor(mon2); + printf("%30s","TCP_Socket_Client_Server_Test:"); + printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l, + num_tcp_clients, num_tcp_connections_per_client); + printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":", + num_tcp_mesgs_per_connection, tcp_mesg_size); + + return 0; +} + +/* + * UDP_Socket_Client_Server_Test - iterative server test + * + * One server and several clients are started + * Each client connects to the server and sends a chunk of data + * For each connection, server starts another thread to read the data + * from the client and send it back to the client, unmodified. + * Each client checks that data received from server is same as the + * data it sent to the server. + * + */ + +static PRInt32 +UDP_Socket_Client_Server_Test(void) +{ + int i; + PRThread *t; + PRSemaphore *server_sem; + Server_Param *sparamp; + Client_Param *cparamp; + PRMonitor *mon2; + PRInt32 datalen; + PRInt32 udp_connect = 1; + + + datalen = udp_datagram_size; + thread_count = 0; + /* + * start the server thread + */ + sparamp = PR_NEW(Server_Param); + if (sparamp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + return -1; + } + server_sem = PR_NewSem(0); + if (server_sem == NULL) { + fprintf(stderr,"prsocket_test: PR_NewSem failed\n"); + failed_already=1; + return -1; + } + mon2 = PR_NewMonitor(); + if (mon2 == NULL) { + fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n"); + failed_already=1; + return -1; + } + PR_EnterMonitor(mon2); + + sparamp->addr_sem = server_sem; + sparamp->exit_mon = mon2; + sparamp->exit_counter = &thread_count; + sparamp->datalen = datalen; + DPRINTF(("Creating UDP server")); + t = PR_CreateThread(PR_USER_THREAD, + UDP_Server, (void *)sparamp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + return -1; + } + thread_count++; + + /* + * wait till the server address is setup + */ + PR_WaitSem(server_sem); + + /* + * Now start a bunch of client threads + */ + + for (i = 0; i < num_udp_clients; i++) { + cparamp = PR_NEW(Client_Param); + if (cparamp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + return -1; + } + cparamp->server_addr = udp_server_addr; + cparamp->exit_mon = mon2; + cparamp->exit_counter = &thread_count; + cparamp->datalen = datalen; + /* + * Cause every other client thread to connect udp sockets + */ + cparamp->udp_connect = udp_connect; + if (udp_connect) { + udp_connect = 0; + } + else { + udp_connect = 1; + } + DPRINTF(("Creating UDP client %d\n", i)); + t = PR_CreateThread(PR_USER_THREAD, + UDP_Client, (void *) cparamp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + return -1; + } + thread_count++; + } + /* Wait for server and client threads to exit */ + while (thread_count) { + PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("UDP Server - thread_count = %d\n", thread_count)); + } + PR_ExitMonitor(mon2); + printf("%30s","UDP_Socket_Client_Server_Test: "); + printf("%2ld Server %2ld Clients\n",1l, num_udp_clients); + printf("%30s %2ld datagrams_per_client %4ld bytes_per_datagram\n",":", + num_udp_datagrams_per_client, udp_datagram_size); + + return 0; +} + +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 + */ +static void +TransmitFile_Client(void *arg) +{ + PRFileDesc *sockfd; + 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_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_FILE_HEADER_SIZE + + LARGE_FILE_TRAILER_SIZE); + if (large_buf == NULL) { + fprintf(stderr,"prsocket_test: failed to alloc buffer\n"); + failed_already=1; + return; + } + netaddr.inet.family = cp->server_addr.inet.family; + netaddr.inet.port = cp->server_addr.inet.port; + netaddr.inet.ip = cp->server_addr.inet.ip; + + if ((sockfd = PR_NewTCPSocket()) == NULL) { + fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n"); + failed_already=1; + return; + } + + if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) { + fprintf(stderr,"prsocket_test: PR_Connect failed\n"); + failed_already=1; + return; + } + /* + * read the small file and verify the data + */ + if (readn(sockfd, small_buf, SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE) + != (SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE)) { + fprintf(stderr, + "prsocket_test: TransmitFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#if defined(XP_UNIX) + /* File transmission test can not be done because of large file's size */ + if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0) { + fprintf(stderr, + "prsocket_test: TransmitFile_Client ERROR - small file header data corruption\n"); + failed_already=1; + return; + } + if (memcmp(small_file_addr, small_buf + SMALL_FILE_HEADER_SIZE, + SMALL_FILE_SIZE) != 0) { + fprintf(stderr, + "prsocket_test: TransmitFile_Client ERROR - small file data corruption\n"); + failed_already=1; + return; + } +#endif + /* + * read the large file and verify the data + */ + if (readn(sockfd, large_buf, LARGE_FILE_SIZE) != LARGE_FILE_SIZE) { + fprintf(stderr, + "prsocket_test: TransmitFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#if defined(XP_UNIX) + if (memcmp(large_file_addr, large_buf, LARGE_FILE_SIZE) != 0) { + fprintf(stderr, + "prsocket_test: TransmitFile_Client ERROR - large file data corruption\n"); + 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; + } +#if defined(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; + } +#if defined(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; + } +#if defined(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; + } +#if defined(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; + } +#if defined(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; + } +#if defined(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 header + */ + 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; + } +#if defined(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 + /* + * case 8: partial large file at non-zero, page-aligned offset, with + * header and trailer + */ + rlen = LARGE_FILE_LEN_4 + 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; + } +#if defined(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((char *)large_file_addr + LARGE_FILE_OFFSET_4, + large_buf + LARGE_FILE_HEADER_SIZE, + LARGE_FILE_LEN_4) != 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_4, + LARGE_FILE_TRAILER_SIZE) != 0) { + fprintf(stderr, + "SendFile 2. ERROR - large file trailer 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 + */ + + PR_EnterMonitor(cp->exit_mon); + --(*cp->exit_counter); + PR_Notify(cp->exit_mon); + PR_ExitMonitor(cp->exit_mon); + DPRINTF(("TransmitFile_Client [0x%lx] exiting\n", PR_GetCurrentThread())); +} + +/* + * Serve_TransmitFile_Client + * Thread, started by the server, for serving a client connection. + * Trasmits a small file, with a header, and a large file, without + * a header + */ +static void +Serve_TransmitFile_Client(void *arg) +{ + Serve_Client_Param *scp = (Serve_Client_Param *) arg; + PRFileDesc *sockfd; + 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); + + if (local_small_file_fd == NULL) { + fprintf(stderr,"prsocket_test failed to open file for transmitting %s\n", + SMALL_FILE_NAME); + failed_already=1; + goto done; + } + local_large_file_fd = PR_Open(LARGE_FILE_NAME, PR_RDONLY,0); + + if (local_large_file_fd == NULL) { + fprintf(stderr,"prsocket_test failed to open file for transmitting %s\n", + LARGE_FILE_NAME); + failed_already=1; + goto done; + } + bytes = PR_TransmitFile(sockfd, local_small_file_fd, small_file_header, + SMALL_FILE_HEADER_SIZE, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + if (bytes != (SMALL_FILE_SIZE+ SMALL_FILE_HEADER_SIZE)) { + fprintf(stderr, + "prsocet_test: PR_TransmitFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + bytes = PR_TransmitFile(sockfd, local_large_file_fd, NULL, 0, + 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_KEEP_OPEN, + 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; + } + /* + * case 8: partial large file at non-zero page-aligned offset, + * with header and trailer + */ + sfd.fd = local_large_file_fd; + sfd.file_offset = LARGE_FILE_OFFSET_4; + sfd.file_nbytes = LARGE_FILE_LEN_4; + 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_CLOSE_SOCKET, + PR_INTERVAL_NO_TIMEOUT); + slen = LARGE_FILE_LEN_4 + 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; + } +done: + if (local_small_file_fd != NULL) { + PR_Close(local_small_file_fd); + } + if (local_large_file_fd != NULL) { + PR_Close(local_large_file_fd); + } +} + +/* + * TransmitFile Server + * Server Thread + * Bind an address to a socket and listen for incoming connections + * Create worker threads to service clients + */ +static void +TransmitFile_Server(void *arg) +{ + PRThread **t = NULL; /* an array of PRThread pointers */ + Server_Param *sp = (Server_Param *) arg; + Serve_Client_Param *scp; + PRFileDesc *sockfd = NULL, *newsockfd; + PRNetAddr netaddr; + PRInt32 i; + + t = (PRThread**)PR_MALLOC(num_transmitfile_clients * sizeof(PRThread *)); + if (t == NULL) { + fprintf(stderr, "prsocket_test: run out of memory\n"); + failed_already=1; + goto exit; + } + /* + * Create a tcp socket + */ + if ((sockfd = PR_OpenTCPSocket(PR_AF_INET)) == NULL) { + fprintf(stderr,"prsocket_test: PR_OpenTCPSocket failed\n"); + failed_already=1; + goto exit; + } + memset(&netaddr, 0, sizeof(netaddr)); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + while (PR_Bind(sockfd, &netaddr) < 0) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + netaddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + fprintf(stderr,"prsocket_test: ERROR - PR_Bind failed\n"); + failed_already=1; + perror("PR_Bind"); + goto exit; + } + + if (PR_Listen(sockfd, 32) < 0) { + fprintf(stderr,"prsocket_test: ERROR - PR_Listen failed\n"); + failed_already=1; + goto exit; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr, + "prsocket_test: ERROR - PR_GetSockName failed\n"); + failed_already=1; + goto exit; + } + + DPRINTF(("TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", + netaddr.inet.ip, netaddr.inet.port)); + tcp_server_addr.inet.family = netaddr.inet.family; + tcp_server_addr.inet.port = netaddr.inet.port; + tcp_server_addr.inet.ip = netaddr.inet.ip; + + /* + * Wake up parent thread because server address is bound and made + * available in the global variable 'tcp_server_addr' + */ + PR_PostSem(sp->addr_sem); + + for (i = 0; i < num_transmitfile_clients ; i++) { + /* test both null and non-null 'addr' argument to PR_Accept */ + PRNetAddr *addrp = (i%2 ? &netaddr: NULL); + + if ((newsockfd = PR_Accept(sockfd, addrp, + PR_INTERVAL_NO_TIMEOUT)) == NULL) { + fprintf(stderr, + "prsocket_test: ERROR - PR_Accept failed\n"); + failed_already=1; + goto exit; + } + /* test both regular and emulated PR_SendFile */ + if (i%2) { + PRFileDesc *layer = PR_CreateIOLayerStub( + emuSendFileIdentity, &emuSendFileMethods); + if (layer == NULL) { + fprintf(stderr, + "prsocket_test: ERROR - PR_CreateIOLayerStub failed\n"); + failed_already=1; + goto exit; + } + if (PR_PushIOLayer(newsockfd, PR_TOP_IO_LAYER, layer) + == PR_FAILURE) { + fprintf(stderr, + "prsocket_test: ERROR - PR_PushIOLayer failed\n"); + failed_already=1; + goto exit; + } + } + scp = PR_NEW(Serve_Client_Param); + if (scp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + goto exit; + } + + /* + * Start a Serve_Client thread for each incoming connection + */ + scp->sockfd = newsockfd; + scp->datalen = sp->datalen; + + t[i] = PR_CreateThread(PR_USER_THREAD, + Serve_TransmitFile_Client, (void *)scp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0); + if (t[i] == NULL) { + fprintf(stderr, + "prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + goto exit; + } + DPRINTF(("TransmitFile_Server: Created Serve_TransmitFile_Client = 0x%lx\n", t)); + } + + /* + * Wait for all the worker threads to end, so that we know + * they are no longer using the small and large file fd's. + */ + + for (i = 0; i < num_transmitfile_clients; i++) { + PR_JoinThread(t[i]); + } + +exit: + if (t) { + PR_DELETE(t); + } + if (sockfd) { + PR_Close(sockfd); + } + + /* + * Decrement exit_counter and notify parent thread + */ + + PR_EnterMonitor(sp->exit_mon); + --(*sp->exit_counter); + PR_Notify(sp->exit_mon); + PR_ExitMonitor(sp->exit_mon); + DPRINTF(("TransmitFile_Server [0x%lx] exiting\n", PR_GetCurrentThread())); +} + +/* + * Socket_Misc_Test - test miscellaneous functions + * + */ +static PRInt32 +Socket_Misc_Test(void) +{ + PRIntn i, rv = 0, bytes, count, len; + PRThread *t; + PRSemaphore *server_sem; + Server_Param *sparamp; + Client_Param *cparamp; + PRMonitor *mon2; + PRInt32 datalen; + + /* + * We deliberately pick a buffer size that is not a nice multiple + * of 1024. + */ +#define TRANSMITFILE_BUF_SIZE (4 * 1024 - 11) + + typedef struct { + char data[TRANSMITFILE_BUF_SIZE]; + } file_buf; + file_buf *buf = NULL; + + /* + * create file(s) to be transmitted + */ + if ((PR_MkDir(TEST_DIR, 0777)) < 0) { + printf("prsocket_test failed to create dir %s\n",TEST_DIR); + failed_already=1; + return -1; + } + + small_file_fd = PR_Open(SMALL_FILE_NAME, PR_RDWR | PR_CREATE_FILE,0777); + + if (small_file_fd == NULL) { + fprintf(stderr,"prsocket_test failed to create/open file %s\n", + SMALL_FILE_NAME); + failed_already=1; + rv = -1; + goto done; + } + buf = PR_NEW(file_buf); + if (buf == NULL) { + fprintf(stderr,"prsocket_test failed to allocate buffer\n"); + failed_already=1; + rv = -1; + goto done; + } + /* + * fill in random data + */ + for (i = 0; i < TRANSMITFILE_BUF_SIZE; i++) { + buf->data[i] = i; + } + count = 0; + do { + len = (SMALL_FILE_SIZE - count) > TRANSMITFILE_BUF_SIZE ? + TRANSMITFILE_BUF_SIZE : (SMALL_FILE_SIZE - count); + bytes = PR_Write(small_file_fd, buf->data, len); + if (bytes <= 0) { + fprintf(stderr, + "prsocket_test failed to write to file %s\n", + SMALL_FILE_NAME); + failed_already=1; + rv = -1; + goto done; + } + count += bytes; + } while (count < SMALL_FILE_SIZE); +#ifdef XP_UNIX + /* + * map the small file; used in checking for data corruption + */ + small_file_addr = mmap(0, SMALL_FILE_SIZE, PROT_READ, + 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); + failed_already=1; + rv = -1; + goto done; + } +#endif + /* + * header for small file + */ + small_file_header = PR_MALLOC(SMALL_FILE_HEADER_SIZE); + if (small_file_header == NULL) { + fprintf(stderr,"prsocket_test failed to malloc header file\n"); + failed_already=1; + rv = -1; + goto done; + } + 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); + + if (large_file_fd == NULL) { + fprintf(stderr,"prsocket_test failed to create/open file %s\n", + LARGE_FILE_NAME); + failed_already=1; + rv = -1; + goto done; + } + /* + * fill in random data + */ + for (i = 0; i < TRANSMITFILE_BUF_SIZE; i++) { + buf->data[i] = i; + } + count = 0; + do { + len = (LARGE_FILE_SIZE - count) > TRANSMITFILE_BUF_SIZE ? + TRANSMITFILE_BUF_SIZE : (LARGE_FILE_SIZE - count); + bytes = PR_Write(large_file_fd, buf->data, len); + if (bytes <= 0) { + fprintf(stderr, + "prsocket_test failed to write to file %s: (%ld, %ld)\n", + LARGE_FILE_NAME, + PR_GetError(), PR_GetOSError()); + failed_already=1; + rv = -1; + goto done; + } + count += bytes; + } while (count < LARGE_FILE_SIZE); +#if defined(XP_UNIX) + /* + * map the large file; used in checking for data corruption + */ + large_file_addr = mmap(0, LARGE_FILE_SIZE, PROT_READ, + 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); + failed_already=1; + rv = -1; + 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; + /* + * start the server thread + */ + sparamp = PR_NEW(Server_Param); + if (sparamp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + rv = -1; + goto done; + } + server_sem = PR_NewSem(0); + if (server_sem == NULL) { + fprintf(stderr,"prsocket_test: PR_NewSem failed\n"); + failed_already=1; + rv = -1; + goto done; + } + mon2 = PR_NewMonitor(); + if (mon2 == NULL) { + fprintf(stderr,"prsocket_test: PR_NewMonitor failed\n"); + failed_already=1; + rv = -1; + goto done; + } + PR_EnterMonitor(mon2); + + sparamp->addr_sem = server_sem; + sparamp->exit_mon = mon2; + sparamp->exit_counter = &thread_count; + sparamp->datalen = datalen; + t = PR_CreateThread(PR_USER_THREAD, + TransmitFile_Server, (void *)sparamp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + failed_already=1; + rv = -1; + goto done; + } + DPRINTF(("Created TCP server = 0x%x\n", t)); + thread_count++; + + /* + * wait till the server address is setup + */ + PR_WaitSem(server_sem); + + /* + * Now start a bunch of client threads + */ + + cparamp = PR_NEW(Client_Param); + if (cparamp == NULL) { + fprintf(stderr,"prsocket_test: PR_NEW failed\n"); + failed_already=1; + rv = -1; + goto done; + } + cparamp->server_addr = tcp_server_addr; + cparamp->server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + cparamp->exit_mon = mon2; + cparamp->exit_counter = &thread_count; + cparamp->datalen = datalen; + for (i = 0; i < num_transmitfile_clients; i++) { + t = create_new_thread(PR_USER_THREAD, + TransmitFile_Client, (void *) cparamp, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, i); + if (t == NULL) { + fprintf(stderr,"prsocket_test: PR_CreateThread failed\n"); + rv = -1; + failed_already=1; + goto done; + } + DPRINTF(("Created TransmitFile client = 0x%lx\n", t)); + thread_count++; + } + /* Wait for server and client threads to exit */ + while (thread_count) { + PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("Socket_Misc_Test - thread_count = %d\n", thread_count)); + } + PR_ExitMonitor(mon2); +done: + if (buf) { + PR_DELETE(buf); + } +#if defined(XP_UNIX) + munmap((char*)small_file_addr, SMALL_FILE_SIZE); + munmap((char*)large_file_addr, LARGE_FILE_SIZE); +#endif + PR_Close(small_file_fd); + PR_Close(large_file_fd); + if ((PR_Delete(SMALL_FILE_NAME)) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: failed to unlink file %s\n", + SMALL_FILE_NAME); + failed_already=1; + } + if ((PR_Delete(LARGE_FILE_NAME)) == PR_FAILURE) { + fprintf(stderr,"prsocket_test: failed to unlink file %s\n", + LARGE_FILE_NAME); + failed_already=1; + } + if ((PR_RmDir(TEST_DIR)) == PR_FAILURE) { + fprintf(stderr,"prsocket_test failed to rmdir %s: (%ld, %ld)\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + failed_already=1; + } + + printf("%-29s%s","Socket_Misc_Test",":"); + printf("%2d Server %2d Clients\n",1, num_transmitfile_clients); + printf("%30s Sizes of Transmitted Files - %4d KB, %2d MB \n",":", + SMALL_FILE_SIZE/1024, LARGE_FILE_SIZE/(1024 * 1024)); + + + return rv; +} +/************************************************************************/ + +/* + * Test Socket NSPR APIs + */ + +int main(int argc, char **argv) +{ + /* + * -d debug mode + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetConcurrency(4); + + emuSendFileIdentity = PR_GetUniqueIdentity("Emulated SendFile"); + emuSendFileMethods = *PR_GetDefaultIOMethods(); + emuSendFileMethods.transmitfile = emu_TransmitFile; + emuSendFileMethods.sendfile = emu_SendFile; + + /* + * run client-server test with TCP, Ipv4-Ipv4 + */ + printf("TCP Client/Server Test - IPv4/Ipv4\n"); + if (TCP_Socket_Client_Server_Test() < 0) { + printf("TCP_Socket_Client_Server_Test failed\n"); + goto done; + } else { + printf("TCP_Socket_Client_Server_Test Passed\n"); + } + /* + * client-server test, Ipv6-Ipv4 + */ + client_domain = PR_AF_INET6; + printf("TCP Client/Server Test - IPv6/Ipv4\n"); + if (TCP_Socket_Client_Server_Test() < 0) { + printf("TCP_Socket_Client_Server_Test failed\n"); + goto done; + } else { + printf("TCP_Socket_Client_Server_Test Passed\n"); + } + /* + * client-server test, Ipv4-Ipv6 + */ + client_domain = PR_AF_INET; + server_domain = PR_AF_INET6; + printf("TCP Client/Server Test - IPv4/Ipv6\n"); + if (TCP_Socket_Client_Server_Test() < 0) { + printf("TCP_Socket_Client_Server_Test failed\n"); + goto done; + } else { + printf("TCP_Socket_Client_Server_Test Passed\n"); + } + /* + * client-server test, Ipv6-Ipv6 + */ + client_domain = PR_AF_INET6; + server_domain = PR_AF_INET6; + printf("TCP Client/Server Test - IPv6/Ipv6\n"); + if (TCP_Socket_Client_Server_Test() < 0) { + printf("TCP_Socket_Client_Server_Test failed\n"); + goto done; + } else { + printf("TCP_Socket_Client_Server_Test Passed\n"); + } + test_cancelio = 0; + + /* + * Misc socket tests - including transmitfile, etc. + */ + + /* File transmission test can not be done in Symbian OS because of + * large file's size and the incomplete mmap() implementation. */ +#if !defined(WIN16) + /* + ** The 'transmit file' test does not run because + ** transmit file is not implemented in NSPR yet. + ** + */ + if (Socket_Misc_Test() < 0) { + printf("Socket_Misc_Test failed\n"); + failed_already=1; + goto done; + } else { + printf("Socket_Misc_Test passed\n"); + } + + /* + * run client-server test with TCP again to test + * recycling used sockets from PR_TransmitFile(). + */ + if (TCP_Socket_Client_Server_Test() < 0) { + printf("TCP_Socket_Client_Server_Test failed\n"); + goto done; + } else { + printf("TCP_Socket_Client_Server_Test Passed\n"); + } +#endif + +done: + PR_Cleanup(); + if (failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/sockopt.c b/nsprpub/pr/tests/sockopt.c new file mode 100644 index 0000000000..d1a04451ab --- /dev/null +++ b/nsprpub/pr/tests/sockopt.c @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "prio.h" +#include "prinit.h" +#include "prprf.h" +#include "obsolete/probslet.h" + +#include "plerror.h" + +#ifdef XP_UNIX +#include <sys/socket.h> /* SO_REUSEPORT */ +#endif + +static PRFileDesc *err = NULL; +static PRBool failed = PR_FALSE; + +static void Failed(const char *msg1, const char *msg2) +{ + if (NULL != msg1) { + PR_fprintf(err, "%s ", msg1); + } + PL_FPrintError(err, msg2); + failed = PR_TRUE; +} /* Failed */ + +static PRSockOption Incr(PRSockOption *option) +{ + PRIntn val = ((PRIntn)*option) + 1; + *option = (PRSockOption)val; + return (PRSockOption)val; +} /* Incr */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PRFileDesc *udp = PR_NewUDPSocket(); + PRFileDesc *tcp = PR_NewTCPSocket(); + const char *tag[] = + { + "PR_SockOpt_Nonblocking", /* nonblocking io */ + "PR_SockOpt_Linger", /* linger on close if data present */ + "PR_SockOpt_Reuseaddr", /* allow local address reuse */ + "PR_SockOpt_Keepalive", /* keep connections alive */ + "PR_SockOpt_RecvBufferSize", /* receive buffer size */ + "PR_SockOpt_SendBufferSize", /* send buffer size */ + + "PR_SockOpt_IpTimeToLive", /* time to live */ + "PR_SockOpt_IpTypeOfService", /* type of service and precedence */ + + "PR_SockOpt_AddMember", /* add an IP group membership */ + "PR_SockOpt_DropMember", /* drop an IP group membership */ + "PR_SockOpt_McastInterface", /* multicast interface address */ + "PR_SockOpt_McastTimeToLive", /* multicast timetolive */ + "PR_SockOpt_McastLoopback", /* multicast loopback */ + + "PR_SockOpt_NoDelay", /* don't delay send to coalesce packets */ + "PR_SockOpt_MaxSegment", /* maximum segment size */ + "PR_SockOpt_Broadcast", /* Enable broadcast */ + "PR_SockOpt_Reuseport", /* allow local address & port reuse */ + "PR_SockOpt_DontFrag", /* do not fragment */ + "PR_SockOpt_Last" + }; + + err = PR_GetSpecialFD(PR_StandardError); + PR_STDIO_INIT(); + + if (NULL == udp) { + Failed("PR_NewUDPSocket()", NULL); + } + else if (NULL == tcp) { + Failed("PR_NewTCPSocket()", NULL); + } + else + { + PRSockOption option; + PRUint32 segment = 1024; + PRNetAddr addr; + + rv = PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr); + if (PR_FAILURE == rv) { + Failed("PR_InitializeNetAddr()", NULL); + } + rv = PR_Bind(udp, &addr); + if (PR_FAILURE == rv) { + Failed("PR_Bind()", NULL); + } + for(option = PR_SockOpt_Linger; option < PR_SockOpt_Last; Incr(&option)) + { + PRSocketOptionData data; + PRFileDesc *fd = tcp; + data.option = option; + switch (option) + { + case PR_SockOpt_Nonblocking: + data.value.non_blocking = PR_TRUE; + break; + case PR_SockOpt_Linger: + data.value.linger.polarity = PR_TRUE; + data.value.linger.linger = PR_SecondsToInterval(2); + break; + case PR_SockOpt_Reuseaddr: + data.value.reuse_addr = PR_TRUE; + break; + case PR_SockOpt_Keepalive: + data.value.keep_alive = PR_TRUE; + break; + case PR_SockOpt_RecvBufferSize: + data.value.recv_buffer_size = segment; + break; + case PR_SockOpt_SendBufferSize: + data.value.send_buffer_size = segment; + break; + case PR_SockOpt_IpTimeToLive: + data.value.ip_ttl = 64; + break; + case PR_SockOpt_IpTypeOfService: + data.value.tos = 0; + break; + case PR_SockOpt_McastTimeToLive: + fd = udp; + data.value.mcast_ttl = 4; + break; + case PR_SockOpt_McastLoopback: + fd = udp; + data.value.mcast_loopback = PR_TRUE; + break; + case PR_SockOpt_NoDelay: + data.value.no_delay = PR_TRUE; + break; +#ifndef WIN32 + case PR_SockOpt_MaxSegment: + data.value.max_segment = segment; + break; +#endif + case PR_SockOpt_Broadcast: + fd = udp; + data.value.broadcast = PR_TRUE; + break; +#ifdef SO_REUSEPORT + case PR_SockOpt_Reuseport: + data.value.reuse_port = PR_TRUE; + break; +#endif + case PR_SockOpt_DontFrag: + data.value.dont_fragment = PR_TRUE; + break; + + default: continue; + } + + /* + * TCP_MAXSEG can only be read, not set + */ + if (option != PR_SockOpt_MaxSegment) { +#ifdef WIN32 + if (option != PR_SockOpt_McastLoopback) +#endif + { + rv = PR_SetSocketOption(fd, &data); + if (PR_FAILURE == rv) { + Failed("PR_SetSocketOption()", tag[option]); + } + } + } + + rv = PR_GetSocketOption(fd, &data); + if (PR_FAILURE == rv) { + Failed("PR_GetSocketOption()", tag[option]); + } + } + PR_Close(udp); + PR_Close(tcp); + } + PR_fprintf(err, "%s\n", (failed) ? "FAILED" : "PASSED"); + return (failed) ? 1 : 0; +} /* main */ + +/* sockopt.c */ + diff --git a/nsprpub/pr/tests/sockping.c b/nsprpub/pr/tests/sockping.c new file mode 100644 index 0000000000..327d1d8e1a --- /dev/null +++ b/nsprpub/pr/tests/sockping.c @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: sockping.c + * + * Description: + * This test runs in conjunction with the sockpong test. + * This test creates a socket pair and passes one socket + * to the sockpong test. Then this test writes "ping" to + * to the sockpong test and the sockpong test writes "pong" + * back. To run this pair of tests, just invoke sockping. + * + * Tested areas: process creation, socket pairs, file + * descriptor inheritance. + */ + +#include "prerror.h" +#include "prio.h" +#include "prproces.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define NUM_ITERATIONS 10 + +static char *child_argv[] = { "sockpong", NULL }; + +int main(int argc, char **argv) +{ + PRFileDesc *sock[2]; + PRStatus status; + PRProcess *process; + PRProcessAttr *attr; + char buf[1024]; + PRInt32 nBytes; + PRInt32 exitCode; + int idx; + + status = PR_NewTCPSocketPair(sock); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_NewTCPSocketPair failed\n"); + exit(1); + } + + status = PR_SetFDInheritable(sock[0], PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + status = PR_SetFDInheritable(sock[1], PR_TRUE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + + attr = PR_NewProcessAttr(); + if (attr == NULL) { + fprintf(stderr, "PR_NewProcessAttr failed\n"); + exit(1); + } + + status = PR_ProcessAttrSetInheritableFD(attr, sock[1], "SOCKET"); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_ProcessAttrSetInheritableFD failed\n"); + exit(1); + } + + process = PR_CreateProcess(child_argv[0], child_argv, NULL, attr); + if (process == NULL) { + fprintf(stderr, "PR_CreateProcess failed\n"); + exit(1); + } + PR_DestroyProcessAttr(attr); + status = PR_Close(sock[1]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + strcpy(buf, "ping"); + printf("ping process: sending \"%s\"\n", buf); + nBytes = PR_Write(sock[0], buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(sock[0], buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("ping process: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "ping process: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "pong") != 0) { + fprintf(stderr, "ping process: expected \"pong\" but got \"%s\"\n", + buf); + exit(1); + } + } + + status = PR_Close(sock[0]); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + status = PR_WaitProcess(process, &exitCode); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_WaitProcess failed\n"); + exit(1); + } + if (exitCode == 0) { + printf("PASS\n"); + return 0; + } else { + printf("FAIL\n"); + return 1; + } +} diff --git a/nsprpub/pr/tests/sockpong.c b/nsprpub/pr/tests/sockpong.c new file mode 100644 index 0000000000..31cfd3ee8c --- /dev/null +++ b/nsprpub/pr/tests/sockpong.c @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: sockpong.c + * + * Description: + * This test runs in conjunction with the sockping test. + * The sockping test creates a socket pair and passes one + * socket to this test. Then the sockping test writes + * "ping" to this test and this test writes "pong" back. + * To run this pair of tests, just invoke sockping. + * + * Tested areas: process creation, socket pairs, file + * descriptor inheritance. + */ + +#include "prerror.h" +#include "prio.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define NUM_ITERATIONS 10 + +int main(int argc, char **argv) +{ + PRFileDesc *sock; + PRStatus status; + char buf[1024]; + PRInt32 nBytes; + int idx; + + sock = PR_GetInheritedFD("SOCKET"); + if (sock == NULL) { + fprintf(stderr, "PR_GetInheritedFD failed\n"); + exit(1); + } + status = PR_SetFDInheritable(sock, PR_FALSE); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_SetFDInheritable failed\n"); + exit(1); + } + + for (idx = 0; idx < NUM_ITERATIONS; idx++) { + memset(buf, 0, sizeof(buf)); + nBytes = PR_Read(sock, buf, sizeof(buf)); + if (nBytes == -1) { + fprintf(stderr, "PR_Read failed: (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + printf("pong process: received \"%s\"\n", buf); + if (nBytes != 5) { + fprintf(stderr, "pong process: expected 5 bytes but got %d bytes\n", + nBytes); + exit(1); + } + if (strcmp(buf, "ping") != 0) { + fprintf(stderr, "pong process: expected \"ping\" but got \"%s\"\n", + buf); + exit(1); + } + + strcpy(buf, "pong"); + printf("pong process: sending \"%s\"\n", buf); + nBytes = PR_Write(sock, buf, 5); + if (nBytes == -1) { + fprintf(stderr, "PR_Write failed\n"); + exit(1); + } + } + + status = PR_Close(sock); + if (status == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + return 0; +} diff --git a/nsprpub/pr/tests/sprintf.c b/nsprpub/pr/tests/sprintf.c new file mode 100644 index 0000000000..51db026d1c --- /dev/null +++ b/nsprpub/pr/tests/sprintf.c @@ -0,0 +1,459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: sprintf.c + * Description: + * This is a test program for the PR_snprintf() functions defined + * in prprf.c. This test program is based on ns/nspr/tests/sprintf.c, + * revision 1.10. + * Modification History: + * 20-May-1997 AGarcia replaced printf statment to return PASS\n. This is to be used by the + * regress tool parsing routine. + ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to + * recognize the return code from tha main program. + */ + +#include "prinit.h" +#include "prprf.h" +#include "prlog.h" +#include "prlong.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +static char sbuf[20000]; + + +/* +** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf. +** Make sure the results are identical +*/ +static void test_i(char *pattern, int i) +{ + char *s; + char buf[200]; + int n; + + /* try all three routines */ + s = PR_smprintf(pattern, i); + PR_ASSERT(s != 0); + n = PR_snprintf(buf, sizeof(buf), pattern, i); + PR_ASSERT(n <= sizeof(buf)); + sprintf(sbuf, pattern, i); + + /* compare results */ + if ((strncmp(s, buf, sizeof(buf)) != 0) || + (strncmp(s, sbuf, sizeof(sbuf)) != 0)) { + fprintf(stderr, + "pattern='%s' i=%d\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n", + pattern, i, s, buf, sbuf); + PR_smprintf_free(s); + exit(-1); + } + PR_smprintf_free(s); +} + +static void TestI(void) +{ + static int nums[] = { + 0, 1, -1, 10, -10, + 32767, -32768, + }; + static char *signs[] = { + "", + "0", "-", "+", " ", + "0-", "0+", "0 ", "-0", "-+", "- ", + "+0", "+-", "+ ", " 0", " -", " +", + "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +", + "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +", + "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -", + " 0-", " 0+", " -0", " -+", " +0", " +-", + "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-", + "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0", + "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0", + " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0", + }; + static char *precs[] = { + "", "3", "5", "43", + "7.3", "7.5", "7.11", "7.43", + }; + static char *formats[] = { + "d", "o", "x", "u", + "hd", "ho", "hx", "hu" + }; + int f, s, n, p; + char fmt[20]; + + for (f = 0; f < PR_ARRAY_SIZE(formats); f++) { + for (s = 0; s < PR_ARRAY_SIZE(signs); s++) { + for (p = 0; p < PR_ARRAY_SIZE(precs); p++) { + fmt[0] = '%'; + fmt[1] = 0; + if (signs[s]) { + strcat(fmt, signs[s]); + } + if (precs[p]) { + strcat(fmt, precs[p]); + } + if (formats[f]) { + strcat(fmt, formats[f]); + } + for (n = 0; n < PR_ARRAY_SIZE(nums); n++) { + test_i(fmt, nums[n]); + } + } + } + } +} + +/************************************************************************/ + +/* +** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf. +** Make sure the results are identical +*/ +static void test_l(char *pattern, char *spattern, PRInt32 l) +{ + char *s; + char buf[200]; + int n; + + /* try all three routines */ + s = PR_smprintf(pattern, l); + PR_ASSERT(s != 0); + n = PR_snprintf(buf, sizeof(buf), pattern, l); + PR_ASSERT(n <= sizeof(buf)); + sprintf(sbuf, spattern, l); + + /* compare results */ + if ((strncmp(s, buf, sizeof(buf)) != 0) || + (strncmp(s, sbuf, sizeof(sbuf)) != 0)) { + fprintf(stderr, + "pattern='%s' l=%ld\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n", + pattern, l, s, buf, sbuf); + PR_smprintf_free(s); + exit(-1); + } + PR_smprintf_free(s); +} + +static void TestL(void) +{ + static PRInt32 nums[] = { + 0, + 1, + -1, + 10, + -10, + 32767, + -32768, + PR_INT32(0x7fffffff), /* 2147483647L */ + -1 - PR_INT32(0x7fffffff) /* -2147483648L */ + }; + static char *signs[] = { + "", + "0", "-", "+", " ", + "0-", "0+", "0 ", "-0", "-+", "- ", + "+0", "+-", "+ ", " 0", " -", " +", + "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +", + "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +", + "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -", + " 0-", " 0+", " -0", " -+", " +0", " +-", + "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-", + "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0", + "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0", + " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0", + }; + static char *precs[] = { + "", "3", "5", "43", + ".3", ".43", + "7.3", "7.5", "7.11", "7.43", + }; + static char *formats[] = { "ld", "lo", "lx", "lu" }; + +#if PR_BYTES_PER_INT == 4 + static char *sformats[] = { "d", "o", "x", "u" }; +#elif PR_BYTES_PER_LONG == 4 + static char *sformats[] = { "ld", "lo", "lx", "lu" }; +#else +#error Neither int nor long is 4 bytes on this platform +#endif + + int f, s, n, p; + char fmt[40], sfmt[40]; + + for (f = 0; f < PR_ARRAY_SIZE(formats); f++) { + for (s = 0; s < PR_ARRAY_SIZE(signs); s++) { + for (p = 0; p < PR_ARRAY_SIZE(precs); p++) { + fmt[0] = '%'; + fmt[1] = 0; + if (signs[s]) { + strcat(fmt, signs[s]); + } + if (precs[p]) { + strcat(fmt, precs[p]); + } + strcpy(sfmt, fmt); + if (formats[f]) { + strcat(fmt, formats[f]); + } + if (sformats[f]) { + strcat(sfmt, sformats[f]); + } + for (n = 0; n < PR_ARRAY_SIZE(nums); n++) { + test_l(fmt, sfmt, nums[n]); + } + } + } + } +} + +/************************************************************************/ + +/* +** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf. +** Make sure the results are identical +*/ +static void test_ll(char *pattern, char *spattern, PRInt64 l) +{ + char *s; + char buf[200]; + int n; + + /* try all three routines */ + s = PR_smprintf(pattern, l); + PR_ASSERT(s != 0); + n = PR_snprintf(buf, sizeof(buf), pattern, l); + PR_ASSERT(n <= sizeof(buf)); +#if defined(HAVE_LONG_LONG) + sprintf(sbuf, spattern, l); + + /* compare results */ + if ((strncmp(s, buf, sizeof(buf)) != 0) || + (strncmp(s, sbuf, sizeof(sbuf)) != 0)) { +#if PR_BYTES_PER_LONG == 8 +#define FORMAT_SPEC "%ld" +#elif defined(WIN16) +#define FORMAT_SPEC "%Ld" +#elif defined(WIN32) +#define FORMAT_SPEC "%I64d" +#else +#define FORMAT_SPEC "%lld" +#endif + fprintf(stderr, + "pattern='%s' ll=" FORMAT_SPEC "\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n", + pattern, l, s, buf, sbuf); + printf("FAIL\n"); + PR_smprintf_free(s); + exit(-1); + } + PR_smprintf_free(s); +#else + /* compare results */ + if ((strncmp(s, buf, sizeof(buf)) != 0)) { + fprintf(stderr, + "pattern='%s'\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n", + pattern, s, buf, sbuf); + printf("FAIL\n"); + PR_smprintf_free(s); + exit(-1); + } + PR_smprintf_free(s); +#endif +} + +static void TestLL(void) +{ + static PRInt64 nums[] = { + LL_INIT(0, 0), + LL_INIT(0, 1), + LL_INIT(0xffffffff, 0xffffffff), /* -1 */ + LL_INIT(0, 10), + LL_INIT(0xffffffff, 0xfffffff6), /* -10 */ + LL_INIT(0, 32767), + LL_INIT(0xffffffff, 0xffff8000), /* -32768 */ + LL_INIT(0, 0x7fffffff), /* 2147483647 */ + LL_INIT(0xffffffff, 0x80000000), /* -2147483648 */ + LL_INIT(0x7fffffff, 0xffffffff), /* 9223372036854775807 */ + LL_INIT(0x80000000, 0), /* -9223372036854775808 */ + PR_INT64(0), + PR_INT64(1), + PR_INT64(-1), + PR_INT64(10), + PR_INT64(-10), + PR_INT64(32767), + PR_INT64(-32768), + PR_INT64(2147483647), + PR_INT64(-2147483648), + PR_INT64(9223372036854775807), + PR_INT64(-9223372036854775808) + }; + + static char *signs[] = { + "", + "0", "-", "+", " ", + "0-", "0+", "0 ", "-0", "-+", "- ", + "+0", "+-", "+ ", " 0", " -", " +", + "0-+", "0- ", "0+-", "0+ ", "0 -", "0 +", + "-0+", "-0 ", "-+0", "-+ ", "- 0", "- +", + "+0-", "+0 ", "+-0", "+- ", "+ 0", "+ -", + " 0-", " 0+", " -0", " -+", " +0", " +-", + "0-+ ", "0- +", "0+- ", "0+ -", "0 -+", "0 +-", + "-0+ ", "-0 +", "-+0 ", "-+ 0", "- 0+", "- +0", + "+0- ", "+0 -", "+-0 ", "+- 0", "+ 0-", "+ -0", + " 0-+", " 0+-", " -0+", " -+0", " +0-", " +-0", + }; + static char *precs[] = { + "", "3", "5", "43", + ".3", ".43", + "7.3", "7.5", "7.11", "7.43", + }; + static char *formats[] = { "lld", "llo", "llx", "llu" }; + +#if PR_BYTES_PER_LONG == 8 + static char *sformats[] = { "ld", "lo", "lx", "lu" }; +#elif defined(WIN16) + /* Watcom uses the format string "%Ld" instead of "%lld". */ + static char *sformats[] = { "Ld", "Lo", "Lx", "Lu" }; +#elif defined(WIN32) + static char *sformats[] = { "I64d", "I64o", "I64x", "I64u" }; +#else + static char *sformats[] = { "lld", "llo", "llx", "llu" }; +#endif + + int f, s, n, p; + char fmt[40], sfmt[40]; + + for (f = 0; f < PR_ARRAY_SIZE(formats); f++) { + for (s = 0; s < PR_ARRAY_SIZE(signs); s++) { + for (p = 0; p < PR_ARRAY_SIZE(precs); p++) { + fmt[0] = '%'; + fmt[1] = 0; + if (signs[s]) { + strcat(fmt, signs[s]); + } + if (precs[p]) { + strcat(fmt, precs[p]); + } + strcpy(sfmt, fmt); + if (formats[f]) { + strcat(fmt, formats[f]); + } + if (sformats[f]) { + strcat(sfmt, sformats[f]); + } + for (n = 0; n < PR_ARRAY_SIZE(nums); n++) { + test_ll(fmt, sfmt, nums[n]); + } + } + } + } +} + +/************************************************************************/ + +/* +** Perform a three way test against PR_smprintf, PR_snprintf, and sprintf. +** Make sure the results are identical +*/ +static void test_s(char *pattern, char *ss) +{ + char *s; + unsigned char before[8]; + char buf[200]; + unsigned char after[8]; + int n; + + memset(before, 0xBB, 8); + memset(after, 0xAA, 8); + + /* try all three routines */ + s = PR_smprintf(pattern, ss); + PR_ASSERT(s != 0); + n = PR_snprintf(buf, sizeof(buf), pattern, ss); + PR_ASSERT(n <= sizeof(buf)); + sprintf(sbuf, pattern, ss); + + for (n = 0; n < 8; n++) { + PR_ASSERT(before[n] == 0xBB); + PR_ASSERT(after[n] == 0xAA); + } + + /* compare results */ + if ((strncmp(s, buf, sizeof(buf)) != 0) || + (strncmp(s, sbuf, sizeof(sbuf)) != 0)) { + fprintf(stderr, + "pattern='%s' ss=%.20s\nPR_smprintf='%s'\nPR_snprintf='%s'\n sprintf='%s'\n", + pattern, ss, s, buf, sbuf); + printf("FAIL\n"); + PR_smprintf_free(s); + exit(-1); + } + PR_smprintf_free(s); +} + +static void TestS(void) +{ + static char *strs[] = { + "", + "a", + "abc", + "abcde", + "abcdefABCDEF", + "abcdefghijklmnopqrstuvwxyz0123456789!@#$" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$" + "abcdefghijklmnopqrstuvwxyz0123456789!@#$", + }; + /* '0' is not relevant to printing strings */ + static char *signs[] = { + "", + "-", "+", " ", + "-+", "- ", "+-", "+ ", " -", " +", + "-+ ", "- +", "+- ", "+ -", " -+", " +-", + }; + static char *precs[] = { + "", "3", "5", "43", + ".3", ".43", + "7.3", "7.5", "7.11", "7.43", + }; + static char *formats[] = { "s" }; + int f, s, n, p; + char fmt[40]; + + for (f = 0; f < PR_ARRAY_SIZE(formats); f++) { + for (s = 0; s < PR_ARRAY_SIZE(signs); s++) { + for (p = 0; p < PR_ARRAY_SIZE(precs); p++) { + fmt[0] = '%'; + fmt[1] = 0; + if (signs[s]) { + strcat(fmt+strlen(fmt), signs[s]); + } + if (precs[p]) { + strcat(fmt+strlen(fmt), precs[p]); + } + if (formats[f]) { + strcat(fmt+strlen(fmt), formats[f]); + } + for (n = 0; n < PR_ARRAY_SIZE(strs); n++) { + test_s(fmt, strs[n]); + } + } + } + } +} + +/************************************************************************/ + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + TestI(); + TestL(); + TestLL(); + TestS(); + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/stack.c b/nsprpub/pr/tests/stack.c new file mode 100644 index 0000000000..5526a27445 --- /dev/null +++ b/nsprpub/pr/tests/stack.c @@ -0,0 +1,282 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * + * Test atomic stack operations + * + * Two stacks are created and threads add data items (each containing + * one of the first n integers) to the first stack, remove data items + * from the first stack and add them to the second stack. The primordial + * thread compares the sum of the first n integers to the sum of the + * integers in the data items in the second stack. The test succeeds if + * they are equal. + */ + +#include "nspr.h" +#include "plgetopt.h" + +typedef struct _DataRecord { + PRInt32 data; + PRStackElem link; +} DataRecord; + +#define RECORD_LINK_PTR(lp) ((DataRecord*) ((char*) (lp) - offsetof(DataRecord,link))) + +#define MAX_THREAD_CNT 100 +#define DEFAULT_THREAD_CNT 4 +#define DEFAULT_DATA_CNT 100 +#define DEFAULT_LOOP_CNT 10000 + +/* + * sum of the first n numbers using the formula n*(n+1)/2 + */ +#define SUM_OF_NUMBERS(n) ((n & 1) ? (((n + 1)/2) * n) : ((n/2) * (n+1))) + +typedef struct stack_data { + PRStack *list1; + PRStack *list2; + PRInt32 initial_data_value; + PRInt32 data_cnt; + PRInt32 loops; +} stack_data; + +static void stackop(void *arg); + +static int _debug_on; + +PRFileDesc *output; +PRFileDesc *errhandle; + +int main(int argc, char **argv) +{ + PRInt32 rv, cnt, sum; + DataRecord *Item; + PRStack *list1, *list2; + PRStackElem *node; + PRStatus rc; + + PRInt32 thread_cnt = DEFAULT_THREAD_CNT; + PRInt32 data_cnt = DEFAULT_DATA_CNT; + PRInt32 loops = DEFAULT_LOOP_CNT; + PRThread **threads; + stack_data *thread_args; + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:l:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + case 't': /* thread count */ + thread_cnt = atoi(opt->value); + break; + case 'c': /* data count */ + data_cnt = atoi(opt->value); + break; + case 'l': /* loop count */ + loops = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_SetConcurrency(4); + + output = PR_GetSpecialFD(PR_StandardOutput); + errhandle = PR_GetSpecialFD(PR_StandardError); + list1 = PR_CreateStack("Stack_1"); + if (list1 == NULL) { + PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n", + PR_GetError()); + return 1; + } + + list2 = PR_CreateStack("Stack_2"); + if (list2 == NULL) { + PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n", + PR_GetError()); + return 1; + } + + + threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt); + thread_args = (stack_data *) PR_CALLOC(sizeof(stack_data) * thread_cnt); + + if (_debug_on) + PR_fprintf(output,"%s: thread_cnt = %d data_cnt = %d\n", argv[0], + thread_cnt, data_cnt); + for(cnt = 0; cnt < thread_cnt; cnt++) { + PRThreadScope scope; + + thread_args[cnt].list1 = list1; + thread_args[cnt].list2 = list2; + thread_args[cnt].loops = loops; + thread_args[cnt].data_cnt = data_cnt; + thread_args[cnt].initial_data_value = 1 + cnt * data_cnt; + + if (cnt & 1) { + scope = PR_GLOBAL_THREAD; + } + else { + scope = PR_LOCAL_THREAD; + } + + + threads[cnt] = PR_CreateThread(PR_USER_THREAD, + stackop, &thread_args[cnt], + PR_PRIORITY_NORMAL, + scope, + PR_JOINABLE_THREAD, + 0); + if (threads[cnt] == NULL) { + PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n", + PR_GetError()); + PR_ProcessExit(2); + } + if (_debug_on) + PR_fprintf(output,"%s: created thread = 0x%x\n", argv[0], + threads[cnt]); + } + + for(cnt = 0; cnt < thread_cnt; cnt++) { + rc = PR_JoinThread(threads[cnt]); + PR_ASSERT(rc == PR_SUCCESS); + } + + node = PR_StackPop(list1); + /* + * list1 should be empty + */ + if (node != NULL) { + PR_fprintf(errhandle, "Error - Stack 1 not empty\n"); + PR_ASSERT(node == NULL); + PR_ProcessExit(4); + } + + cnt = data_cnt * thread_cnt; + sum = 0; + while (cnt-- > 0) { + node = PR_StackPop(list2); + /* + * There should be at least 'cnt' number of records + */ + if (node == NULL) { + PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n"); + PR_ProcessExit(3); + } + Item = RECORD_LINK_PTR(node); + sum += Item->data; + } + node = PR_StackPop(list2); + /* + * there should be exactly 'cnt' number of records + */ + if (node != NULL) { + PR_fprintf(errhandle, "Error - Stack 2 not empty\n"); + PR_ASSERT(node == NULL); + PR_ProcessExit(4); + } + PR_DELETE(threads); + PR_DELETE(thread_args); + + PR_DestroyStack(list1); + PR_DestroyStack(list2); + + if (sum == SUM_OF_NUMBERS(data_cnt * thread_cnt)) { + PR_fprintf(output, "%s successful\n", argv[0]); + PR_fprintf(output, "\t\tsum = 0x%x, expected = 0x%x\n", sum, + SUM_OF_NUMBERS(thread_cnt * data_cnt)); + return 0; + } else { + PR_fprintf(output, "%s failed: sum = 0x%x, expected = 0x%x\n", + argv[0], sum, + SUM_OF_NUMBERS(data_cnt * thread_cnt)); + return 2; + } +} + +static void stackop(void *thread_arg) +{ + PRInt32 val, cnt, index, loops; + DataRecord *Items, *Item; + PRStack *list1, *list2; + PRStackElem *node; + stack_data *arg = (stack_data *) thread_arg; + + val = arg->initial_data_value; + cnt = arg->data_cnt; + loops = arg->loops; + list1 = arg->list1; + list2 = arg->list2; + + /* + * allocate memory for the data records + */ + Items = (DataRecord *) PR_CALLOC(sizeof(DataRecord) * cnt); + PR_ASSERT(Items != NULL); + index = 0; + + if (_debug_on) + PR_fprintf(output, + "Thread[0x%x] init_val = %d cnt = %d data1 = 0x%x datan = 0x%x\n", + PR_GetCurrentThread(), val, cnt, &Items[0], &Items[cnt-1]); + + + /* + * add the data records to list1 + */ + while (cnt-- > 0) { + Items[index].data = val++; + PR_StackPush(list1, &Items[index].link); + index++; + } + + /* + * pop data records from list1 and add them back to list1 + * generates contention for the stack accesses + */ + while (loops-- > 0) { + cnt = arg->data_cnt; + while (cnt-- > 0) { + node = PR_StackPop(list1); + if (node == NULL) { + PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n"); + PR_ASSERT(node != NULL); + PR_ProcessExit(3); + } + PR_StackPush(list1, node); + } + } + /* + * remove the data records from list1 and add them to list2 + */ + cnt = arg->data_cnt; + while (cnt-- > 0) { + node = PR_StackPop(list1); + if (node == NULL) { + PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n"); + PR_ASSERT(node != NULL); + PR_ProcessExit(3); + } + PR_StackPush(list2, node); + } + if (_debug_on) + PR_fprintf(output, + "Thread[0x%x] init_val = %d cnt = %d exiting\n", + PR_GetCurrentThread(), val, cnt); + +} + diff --git a/nsprpub/pr/tests/stat.c b/nsprpub/pr/tests/stat.c new file mode 100644 index 0000000000..1f870a0d42 --- /dev/null +++ b/nsprpub/pr/tests/stat.c @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Program to test different ways to get file info; right now it + * only works for solaris and OS/2. + * + */ +#include "nspr.h" +#include "prpriv.h" +#include "prinrval.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef XP_OS2 +#include <io.h> +#include <sys/types.h> +#include <sys/stat.h> +#endif + +#define DEFAULT_COUNT 100000 +PRInt32 count; + +#ifndef XP_PC +char *filename = "/etc/passwd"; +#else +char *filename = "..\\stat.c"; +#endif + +static void statPRStat(void) +{ + PRFileInfo finfo; + PRInt32 index = count; + + for (; index--;) { + PR_GetFileInfo(filename, &finfo); + } +} + +static void statStat(void) +{ + struct stat finfo; + PRInt32 index = count; + + for (; index--;) { + stat(filename, &finfo); + } +} + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + PRInt32 tot; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + tot = PR_IntervalToMilliseconds(stop-start); + + printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot); +} + +int main(int argc, char **argv) +{ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (argc > 1) { + count = atoi(argv[1]); + } else { + count = DEFAULT_COUNT; + } + + Measure(statPRStat, "time to call PR_GetFileInfo()"); + Measure(statStat, "time to call stat()"); + + PR_Cleanup(); +} diff --git a/nsprpub/pr/tests/stdio.c b/nsprpub/pr/tests/stdio.c new file mode 100644 index 0000000000..d8a12389b2 --- /dev/null +++ b/nsprpub/pr/tests/stdio.c @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: stdio.c + * Description: testing the "special" fds + * Modification History: + * 20-May-1997 AGarcia - Replace Test succeeded status with PASS. This is used by the + * regress tool parsing code. + ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to +** recognize the return code from tha main program. + */ + + +#include "prlog.h" +#include "prinit.h" +#include "prio.h" + +#include <stdio.h> +#include <string.h> + +static PRIntn PR_CALLBACK stdio(PRIntn argc, char **argv) +{ + PRInt32 rv; + + PRFileDesc *out = PR_GetSpecialFD(PR_StandardOutput); + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + + rv = PR_Write( + out, "This to standard out\n", + strlen("This to standard out\n")); + PR_ASSERT((PRInt32)strlen("This to standard out\n") == rv); + rv = PR_Write( + err, "This to standard err\n", + strlen("This to standard err\n")); + PR_ASSERT((PRInt32)strlen("This to standard err\n") == rv); + + return 0; + +} /* stdio */ + +int main(int argc, char **argv) +{ + PR_STDIO_INIT(); + return PR_Initialize(stdio, argc, argv, 0); +} /* main */ + + +/* stdio.c */ diff --git a/nsprpub/pr/tests/str2addr.c b/nsprpub/pr/tests/str2addr.c new file mode 100644 index 0000000000..ad80137d4d --- /dev/null +++ b/nsprpub/pr/tests/str2addr.c @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: str2addr.c + * Description: a test for PR_StringToNetAddr + */ + +#include "nspr.h" + +#include <stdio.h> +#include <stdlib.h> + +/* Address string to convert */ +#define DEFAULT_IPV4_ADDR_STR "207.200.73.41" + +/* Expected conversion result, in network byte order */ +static unsigned char default_ipv4_addr[] = {207, 200, 73, 41}; + +int main(int argc, char **argv) +{ + PRNetAddr addr; + const char *addrStr; + unsigned char *bytes; + int idx; + + addrStr = DEFAULT_IPV4_ADDR_STR; + if (PR_StringToNetAddr(addrStr, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_StringToNetAddr failed\n"); + exit(1); + } + if (addr.inet.family != PR_AF_INET) { + fprintf(stderr, "addr.inet.family should be %d but is %d\n", + PR_AF_INET, addr.inet.family); + exit(1); + } + bytes = (unsigned char *) &addr.inet.ip; + for (idx = 0; idx < 4; idx++) { + if (bytes[idx] != default_ipv4_addr[idx]) { + fprintf(stderr, "byte %d of IPv4 addr should be %d but is %d\n", + idx, default_ipv4_addr[idx], bytes[idx]); + exit(1); + } + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/strod.c b/nsprpub/pr/tests/strod.c new file mode 100644 index 0000000000..014192fa6b --- /dev/null +++ b/nsprpub/pr/tests/strod.c @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prinit.h" +#include "prio.h" +#include "prprf.h" +#include "prdtoa.h" +#include "plgetopt.h" + +#include <stdlib.h> + +static void Help(void) +{ + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + PR_fprintf(err, "Usage: /.strod [-n n] [-l n] [-h]\n"); + PR_fprintf(err, "\t-n n Number to translate (default: 1234567890123456789)\n"); + PR_fprintf(err, "\t-l n Times to loop the test (default: 1)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); +} /* Help */ + +static PRIntn PR_CALLBACK RealMain(PRIntn argc, char **argv) +{ + PLOptStatus os; + PRIntn loops = 1; + PRFloat64 answer; + const char *number = "1234567890123456789"; + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + PLOptState *opt = PL_CreateOptState(argc, argv, "hc:l:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'n': /* number to translate */ + number = opt->value; + break; + case 'l': /* number of times to run the tests */ + loops = atoi(opt->value); + break; + case 'h': /* user wants some guidance */ + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_fprintf(err, "Settings\n"); + PR_fprintf(err, "\tNumber to translate %s\n", number); + PR_fprintf(err, "\tLoops to run test: %d\n", loops); + + while (loops--) + { + answer = PR_strtod(number, NULL); + PR_fprintf(err, "Translation = %20.0f\n", answer); + } + return 0; +} + + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ diff --git a/nsprpub/pr/tests/suspend.c b/nsprpub/pr/tests/suspend.c new file mode 100644 index 0000000000..1976b9d8c0 --- /dev/null +++ b/nsprpub/pr/tests/suspend.c @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "prpriv.h" +#include "prinrval.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRMonitor *mon; +PRInt32 count; +PRInt32 alive; + +#define SLEEP_TIME 4 /* secs */ + +void PR_CALLBACK +Level_2_Thread(void *arg) +{ + PR_Sleep(PR_MillisecondsToInterval(4 * 1000)); + printf("Level_2_Thread[0x%lx] exiting\n",PR_GetCurrentThread()); + return; +} + +void PR_CALLBACK +Level_1_Thread(void *arg) +{ + PRUint32 tmp = (PRUint32)arg; + PRThreadScope scope = (PRThreadScope) tmp; + PRThread *thr; + + thr = PR_CreateThreadGCAble(PR_USER_THREAD, + Level_2_Thread, + NULL, + PR_PRIORITY_HIGH, + scope, + PR_JOINABLE_THREAD, + 0); + + if (!thr) { + printf("Could not create thread!\n"); + } else { + printf("Level_1_Thread[0x%lx] created %15s thread 0x%lx\n", + PR_GetCurrentThread(), + (scope == PR_GLOBAL_THREAD) ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD", + thr); + PR_JoinThread(thr); + } + PR_EnterMonitor(mon); + alive--; + PR_Notify(mon); + PR_ExitMonitor(mon); + printf("Thread[0x%lx] exiting\n",PR_GetCurrentThread()); +} + +static PRStatus PR_CALLBACK print_thread(PRThread *thread, int i, void *arg) +{ + PRInt32 words; + PRWord *registers; + + printf( + "\nprint_thread[0x%lx]: %-20s - i = %ld\n",thread, + (PR_GLOBAL_THREAD == PR_GetThreadScope(thread)) ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD", i); + registers = PR_GetGCRegisters(thread, 0, (int *)&words); + if (registers) + printf("Registers R0 = 0x%x R1 = 0x%x R2 = 0x%x R3 = 0x%x\n", + registers[0],registers[1],registers[2],registers[3]); + printf("Stack Pointer = 0x%lx\n", PR_GetSP(thread)); + return PR_SUCCESS; +} + +static void Level_0_Thread(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *thr; + PRThread *me = PR_GetCurrentThread(); + int n; + PRInt32 words; + PRWord *registers; + + alive = 0; + mon = PR_NewMonitor(); + + alive = count; + for (n=0; n<count; n++) { + thr = PR_CreateThreadGCAble(PR_USER_THREAD, + Level_1_Thread, + (void *)scope2, + PR_PRIORITY_NORMAL, + scope1, + PR_UNJOINABLE_THREAD, + 0); + if (!thr) { + printf("Could not create thread!\n"); + alive--; + } + printf("Level_0_Thread[0x%lx] created %15s thread 0x%lx\n", + PR_GetCurrentThread(), + (scope1 == PR_GLOBAL_THREAD) ? + "PR_GLOBAL_THREAD" : "PR_LOCAL_THREAD", + thr); + + PR_Sleep(0); + } + PR_SuspendAll(); + PR_EnumerateThreads(print_thread, NULL); + registers = PR_GetGCRegisters(me, 1, (int *)&words); + if (registers) + printf("My Registers: R0 = 0x%x R1 = 0x%x R2 = 0x%x R3 = 0x%x\n", + registers[0],registers[1],registers[2],registers[3]); + printf("My Stack Pointer = 0x%lx\n", PR_GetSP(me)); + PR_ResumeAll(); + + /* Wait for all threads to exit */ + PR_EnterMonitor(mon); + while (alive) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + + PR_ExitMonitor(mon); + PR_DestroyMonitor(mon); +} + +static void CreateThreadsUU(void) +{ + Level_0_Thread(PR_LOCAL_THREAD, PR_LOCAL_THREAD); +} + +static void CreateThreadsUK(void) +{ + Level_0_Thread(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); +} + +static void CreateThreadsKU(void) +{ + Level_0_Thread(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); +} + +static void CreateThreadsKK(void) +{ + Level_0_Thread(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); +} + + +int main(int argc, char **argv) +{ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (argc > 1) { + count = atoi(argv[1]); + } else { + count = 5; + } + + printf("\n\n%20s%30s\n\n"," ","Suspend_Resume Test"); + CreateThreadsUU(); + CreateThreadsUK(); + CreateThreadsKU(); + CreateThreadsKK(); + PR_SetConcurrency(2); + + printf("\n%20s%30s\n\n"," ","Added 2nd CPU\n"); + + CreateThreadsUK(); + CreateThreadsKK(); + CreateThreadsUU(); + CreateThreadsKU(); + PR_Cleanup(); + + return 0; +} + diff --git a/nsprpub/pr/tests/switch.c b/nsprpub/pr/tests/switch.c new file mode 100644 index 0000000000..43db7888fb --- /dev/null +++ b/nsprpub/pr/tests/switch.c @@ -0,0 +1,252 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: switch.c +** Description: trying to time context switches +*/ + +#include "prinit.h" +#include "prcvar.h" +#include "prmem.h" +#include "prinrval.h" +#include "prlock.h" +#include "prlog.h" +#include "prthread.h" +#include "prprf.h" + +#include "plerror.h" +#include "plgetopt.h" + +#include "private/pprio.h" + +#include <stdlib.h> + +#define INNER_LOOPS 100 +#define DEFAULT_LOOPS 100 +#define DEFAULT_THREADS 10 + +static PRFileDesc *debug_out = NULL; +static PRBool debug_mode = PR_FALSE, verbosity = PR_FALSE, failed = PR_FALSE; + +typedef struct Shared +{ + PRLock *ml; + PRCondVar *cv; + PRBool twiddle; + PRThread *thread; + struct Shared *next; +} Shared; + +static void Help(void) +{ + debug_out = PR_STDOUT; + + PR_fprintf( + debug_out, "Usage: >./switch [-c n] [-t n] [-d] [-v] [-G] [-C n]\n"); + PR_fprintf( + debug_out, "-c n\tloops at thread level (default: %d)\n", DEFAULT_LOOPS); + PR_fprintf( + debug_out, "-t n\tnumber of threads (default: %d)\n", DEFAULT_THREADS); + PR_fprintf(debug_out, "-d\tturn on debugging output (default: FALSE)\n"); + PR_fprintf(debug_out, "-v\tturn on verbose output (default: FALSE)\n"); + PR_fprintf(debug_out, "-G\tglobal threads only (default: FALSE)\n"); + PR_fprintf(debug_out, "-C n\tconcurrency setting (default: 1)\n"); +} /* Help */ + +static void PR_CALLBACK Notified(void *arg) +{ + Shared *shared = (Shared*)arg; + PRStatus status = PR_SUCCESS; + while (PR_SUCCESS == status) + { + PR_Lock(shared->ml); + while (shared->twiddle && (PR_SUCCESS == status)) { + status = PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT); + } + if (verbosity) { + PR_fprintf(debug_out, "+"); + } + shared->twiddle = PR_TRUE; + shared->next->twiddle = PR_FALSE; + PR_NotifyCondVar(shared->next->cv); + PR_Unlock(shared->ml); + } +} /* Notified */ + +static Shared home; +PRIntn PR_CALLBACK Switch(PRIntn argc, char **argv) +{ + PLOptStatus os; + PRStatus status; + PRBool help = PR_FALSE; + PRUintn concurrency = 1; + Shared *shared, *link; + PRIntervalTime timein, timeout; + PRThreadScope thread_scope = PR_LOCAL_THREAD; + PRUintn thread_count, inner_count, loop_count, average; + PRUintn thread_limit = DEFAULT_THREADS, loop_limit = DEFAULT_LOOPS; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdvc:t:C:G"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'v': /* verbose mode */ + verbosity = PR_TRUE; + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'c': /* loop counter */ + loop_limit = atoi(opt->value); + break; + case 't': /* thread limit */ + thread_limit = atoi(opt->value); + break; + case 'C': /* Concurrency limit */ + concurrency = atoi(opt->value); + break; + case 'G': /* global threads only */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'h': /* help message */ + Help(); + help = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (help) { + return -1; + } + + if (PR_TRUE == debug_mode) + { + debug_out = PR_STDOUT; + PR_fprintf(debug_out, "Test parameters\n"); + PR_fprintf(debug_out, "\tThreads involved: %d\n", thread_limit); + PR_fprintf(debug_out, "\tIteration limit: %d\n", loop_limit); + PR_fprintf(debug_out, "\tConcurrency: %d\n", concurrency); + PR_fprintf( + debug_out, "\tThread type: %s\n", + (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); + } + + PR_SetConcurrency(concurrency); + + link = &home; + home.ml = PR_NewLock(); + home.cv = PR_NewCondVar(home.ml); + home.twiddle = PR_FALSE; + home.next = NULL; + + timeout = 0; + + for (thread_count = 1; thread_count <= thread_limit; ++thread_count) + { + shared = PR_NEWZAP(Shared); + + shared->ml = home.ml; + shared->cv = PR_NewCondVar(home.ml); + shared->twiddle = PR_TRUE; + shared->next = link; + link = shared; + + shared->thread = PR_CreateThread( + PR_USER_THREAD, Notified, shared, + PR_PRIORITY_HIGH, thread_scope, + PR_JOINABLE_THREAD, 0); + PR_ASSERT(shared->thread != NULL); + if (NULL == shared->thread) { + failed = PR_TRUE; + } + } + + for (loop_count = 1; loop_count <= loop_limit; ++loop_count) + { + timein = PR_IntervalNow(); + for (inner_count = 0; inner_count < INNER_LOOPS; ++inner_count) + { + PR_Lock(home.ml); + home.twiddle = PR_TRUE; + shared->twiddle = PR_FALSE; + PR_NotifyCondVar(shared->cv); + while (home.twiddle) + { + status = PR_WaitCondVar(home.cv, PR_INTERVAL_NO_TIMEOUT); + if (PR_FAILURE == status) { + failed = PR_TRUE; + } + } + PR_Unlock(home.ml); + } + timeout += (PR_IntervalNow() - timein); + } + + if (debug_mode) + { + average = PR_IntervalToMicroseconds(timeout) + / (INNER_LOOPS * loop_limit * thread_count); + PR_fprintf( + debug_out, "Average switch times %d usecs for %d threads\n", + average, thread_limit); + } + + link = shared; + for (thread_count = 1; thread_count <= thread_limit; ++thread_count) + { + if (&home == link) { + break; + } + status = PR_Interrupt(link->thread); + if (PR_SUCCESS != status) + { + failed = PR_TRUE; + if (debug_mode) { + PL_FPrintError(debug_out, "Failed to interrupt"); + } + } + link = link->next; + } + + for (thread_count = 1; thread_count <= thread_limit; ++thread_count) + { + link = shared->next; + status = PR_JoinThread(shared->thread); + if (PR_SUCCESS != status) + { + failed = PR_TRUE; + if (debug_mode) { + PL_FPrintError(debug_out, "Failed to join"); + } + } + PR_DestroyCondVar(shared->cv); + PR_DELETE(shared); + if (&home == link) { + break; + } + shared = link; + } + PR_DestroyCondVar(home.cv); + PR_DestroyLock(home.ml); + + PR_fprintf(PR_STDOUT, ((failed) ? "FAILED\n" : "PASSED\n")); + return ((failed) ? 1 : 0); +} /* Switch */ + +int main(int argc, char **argv) +{ + PRIntn result; + PR_STDIO_INIT(); + result = PR_Initialize(Switch, argc, argv, 0); + return result; +} /* main */ + +/* switch.c */ diff --git a/nsprpub/pr/tests/system.c b/nsprpub/pr/tests/system.c new file mode 100644 index 0000000000..85b5e2e502 --- /dev/null +++ b/nsprpub/pr/tests/system.c @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prmem.h" +#include "prprf.h" +#include "prsystem.h" + +#include "plerror.h" + +static char *tag[] = +{ + "PR_SI_HOSTNAME", + "PR_SI_SYSNAME", + "PR_SI_RELEASE", + "PR_SI_ARCHITECTURE" +}; + +static PRSysInfo Incr(PRSysInfo *cmd) +{ + PRIntn tmp = (PRIntn)*cmd + 1; + *cmd = (PRSysInfo)tmp; + return (PRSysInfo)tmp; +} /* Incr */ + +int main(int argc, char **argv) +{ + PRStatus rv; + PRSysInfo cmd; + PRFileDesc *output = PR_GetSpecialFD(PR_StandardOutput); + + char *info = (char*)PR_Calloc(SYS_INFO_BUFFER_LENGTH, 1); + for (cmd = PR_SI_HOSTNAME; cmd <= PR_SI_ARCHITECTURE; Incr(&cmd)) + { + rv = PR_GetSystemInfo(cmd, info, SYS_INFO_BUFFER_LENGTH); + if (PR_SUCCESS == rv) { + PR_fprintf(output, "%s: %s\n", tag[cmd], info); + } + else { + PL_FPrintError(output, tag[cmd]); + } + } + PR_DELETE(info); + + PR_fprintf(output, "Host page size is %d\n", PR_GetPageSize()); + PR_fprintf(output, "Page shift is %d\n", PR_GetPageShift()); + PR_fprintf(output, "Memory map alignment is %ld\n", PR_GetMemMapAlignment()); + PR_fprintf(output, "Number of processors is: %d\n", PR_GetNumberOfProcessors()); + PR_fprintf(output, "Physical memory size is: %llu\n", PR_GetPhysicalMemorySize()); + + return 0; +} /* main */ + +/* system.c */ diff --git a/nsprpub/pr/tests/testbit.c b/nsprpub/pr/tests/testbit.c new file mode 100644 index 0000000000..889c637e22 --- /dev/null +++ b/nsprpub/pr/tests/testbit.c @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: lazyinit.c +** Description: Test the functions and macros declared in prbit.h +** +*/ + +#include "nspr.h" + +#define ErrorReport(x) { printf((x)); failed = 1; } + +prbitmap_t myMap[512/32] = { 0 }; + +PRInt32 rc; +PRInt32 i; +PRIntn failed = 0; + +int main(int argc, char **argv) +{ + /* + ** Test bitmap things. + */ + if ( PR_TEST_BIT( myMap, 0 )) { + ErrorReport("Test 0.0: Failed\n"); + } + + if ( PR_TEST_BIT( myMap, 31 )) { + ErrorReport("Test 0.1: Failed\n"); + } + + if ( PR_TEST_BIT( myMap, 128 )) { + ErrorReport("Test 0.2: Failed\n"); + } + + if ( PR_TEST_BIT( myMap, 129 )) { + ErrorReport("Test 0.3: Failed\n"); + } + + + PR_SET_BIT( myMap, 0 ); + if ( !PR_TEST_BIT( myMap, 0 )) { + ErrorReport("Test 1.0: Failed\n"); + } + + PR_CLEAR_BIT( myMap, 0 ); + if ( PR_TEST_BIT( myMap, 0 )) { + ErrorReport("Test 1.0.1: Failed\n"); + } + + PR_SET_BIT( myMap, 31 ); + if ( !PR_TEST_BIT( myMap, 31 )) { + ErrorReport("Test 1.1: Failed\n"); + } + + PR_CLEAR_BIT( myMap, 31 ); + if ( PR_TEST_BIT( myMap, 31 )) { + ErrorReport("Test 1.1.1: Failed\n"); + } + + PR_SET_BIT( myMap, 128 ); + if ( !PR_TEST_BIT( myMap, 128 )) { + ErrorReport("Test 1.2: Failed\n"); + } + + PR_CLEAR_BIT( myMap, 128 ); + if ( PR_TEST_BIT( myMap, 128 )) { + ErrorReport("Test 1.2.1: Failed\n"); + } + + PR_SET_BIT( myMap, 129 ); + if ( !PR_TEST_BIT( myMap, 129 )) { + ErrorReport("Test 1.3: Failed\n"); + } + + PR_CLEAR_BIT( myMap, 129 ); + if ( PR_TEST_BIT( myMap, 129 )) { + ErrorReport("Test 1.3.1: Failed\n"); + } + + + /* + ** Test Ceiling and Floor functions and macros + */ + if ((rc = PR_CeilingLog2(32)) != 5 ) { + ErrorReport("Test 10.0: Failed\n"); + } + + if ((rc = PR_FloorLog2(32)) != 5 ) { + ErrorReport("Test 10.1: Failed\n"); + } + + + /* + ** Evaluate results and exit + */ + if (failed) + { + printf("FAILED\n"); + return(1); + } + else + { + printf("PASSED\n"); + return(0); + } +} /* end main() */ +/* end testbit.c */ diff --git a/nsprpub/pr/tests/testfile.c b/nsprpub/pr/tests/testfile.c new file mode 100644 index 0000000000..79d8a05f92 --- /dev/null +++ b/nsprpub/pr/tests/testfile.c @@ -0,0 +1,963 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "prpriv.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef WIN32 +#include <windows.h> +#include <process.h> +#endif +#if defined(_PR_PTHREADS) +#include <pthread.h> +#endif + +#if defined(XP_UNIX) +#include <unistd.h> +#endif + +#if defined(XP_OS2) +#define INCL_DOSFILEMGR +#include <os2.h> +#include <getopt.h> +#include <errno.h> +#endif /* XP_OS2 */ + +static int _debug_on = 0; + +#ifdef WINCE +#define setbuf(x,y) +#endif + +#ifdef XP_WIN +#define mode_t int +#endif + +#define DPRINTF(arg) if (_debug_on) printf arg + +PRLock *lock; +PRMonitor *mon; +PRInt32 count; +int thread_count; + +#ifdef WIN16 +#define BUF_DATA_SIZE 256 * 120 +#else +#define BUF_DATA_SIZE 256 * 1024 +#endif + +#define NUM_RDWR_THREADS 10 +#define NUM_DIRTEST_THREADS 4 +#define CHUNK_SIZE 512 + +typedef struct buffer { + char data[BUF_DATA_SIZE]; +} buffer; + +typedef struct File_Rdwr_Param { + char *pathname; + char *buf; + int offset; + int len; +} File_Rdwr_Param; + +#ifdef XP_PC +#ifdef XP_OS2 +char *TEST_DIR = "prdir"; +#else +char *TEST_DIR = "C:\\temp\\prdir"; +#endif +char *FILE_NAME = "pr_testfile"; +char *HIDDEN_FILE_NAME = "hidden_pr_testfile"; +#else +char *TEST_DIR = "./tmp-testfile_dir"; +char *FILE_NAME = "pr_testfile"; +char *HIDDEN_FILE_NAME = ".hidden_pr_testfile"; +#endif +buffer *in_buf, *out_buf; +char pathname[256], renamename[256]; +#ifdef WINCE +WCHAR wPathname[256]; +#endif +#define TMPDIR_LEN 64 +char testdir[TMPDIR_LEN]; +static PRInt32 PR_CALLBACK DirTest(void *argunused); +PRInt32 dirtest_failed = 0; + +PRThread* create_new_thread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, PRInt32 index) +{ + PRInt32 native_thread = 0; + + PR_ASSERT(state == PR_UNJOINABLE_THREAD); + +#if defined(_PR_PTHREADS) || defined(WIN32) || defined(XP_OS2) + + switch(index % 4) { + case 0: + scope = (PR_LOCAL_THREAD); + break; + case 1: + scope = (PR_GLOBAL_THREAD); + break; + case 2: + scope = (PR_GLOBAL_BOUND_THREAD); + break; + case 3: + native_thread = 1; + break; + default: + PR_NOT_REACHED("Invalid scope"); + break; + } + if (native_thread) { +#if defined(_PR_PTHREADS) + pthread_t tid; + if (!pthread_create(&tid, NULL, start, arg)) { + return((PRThread *) tid); + } + else { + return (NULL); + } +#elif defined(XP_OS2) + TID tid; + + tid = (TID)_beginthread((void(* _Optlink)(void*))start, + NULL, 32768, arg); + if (tid == -1) { + printf("_beginthread failed. errno %d\n", errno); + return (NULL); + } + else { + return((PRThread *) tid); + } +#else + HANDLE thandle; + unsigned tid; + + thandle = (HANDLE) _beginthreadex( + NULL, + stackSize, + (unsigned (__stdcall *)(void *))start, + arg, + STACK_SIZE_PARAM_IS_A_RESERVATION, + &tid); + return((PRThread *) thandle); +#endif + } else { + return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize)); + } +#else + return(PR_CreateThread(type,start,arg,priority,scope,state,stackSize)); +#endif +} + +static void PR_CALLBACK File_Write(void *arg) +{ + PRFileDesc *fd_file; + File_Rdwr_Param *fp = (File_Rdwr_Param *) arg; + char *name, *buf; + int offset, len; + + setbuf(stdout, NULL); + name = fp->pathname; + buf = fp->buf; + offset = fp->offset; + len = fp->len; + + fd_file = PR_Open(name, PR_RDWR | PR_CREATE_FILE, 0777); + if (fd_file == NULL) { + printf("testfile failed to create/open file %s\n",name); + return; + } + if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) { + printf("testfile failed to seek in file %s\n",name); + return; + } + if ((PR_Write(fd_file, buf, len)) < 0) { + printf("testfile failed to write to file %s\n",name); + return; + } + DPRINTF(("Write out_buf[0] = 0x%x\n",(*((int *) buf)))); + PR_Close(fd_file); + PR_DELETE(fp); + + PR_EnterMonitor(mon); + --thread_count; + PR_Notify(mon); + PR_ExitMonitor(mon); +} + +static void PR_CALLBACK File_Read(void *arg) +{ + PRFileDesc *fd_file; + File_Rdwr_Param *fp = (File_Rdwr_Param *) arg; + char *name, *buf; + int offset, len; + + setbuf(stdout, NULL); + name = fp->pathname; + buf = fp->buf; + offset = fp->offset; + len = fp->len; + + fd_file = PR_Open(name, PR_RDONLY, 0); + if (fd_file == NULL) { + printf("testfile failed to open file %s\n",name); + return; + } + if (PR_Seek(fd_file, offset, PR_SEEK_SET) < 0) { + printf("testfile failed to seek in file %s\n",name); + return; + } + if ((PR_Read(fd_file, buf, len)) < 0) { + printf("testfile failed to read to file %s\n",name); + return; + } + DPRINTF(("Read in_buf[0] = 0x%x\n",(*((int *) buf)))); + PR_Close(fd_file); + PR_DELETE(fp); + + PR_EnterMonitor(mon); + --thread_count; + PR_Notify(mon); + PR_ExitMonitor(mon); +} + + +static PRInt32 Misc_File_Tests(char *pathname) +{ + PRFileDesc *fd_file; + int len, rv = 0; + PRFileInfo file_info, file_info1; + char tmpname[1024]; + + setbuf(stdout, NULL); + /* + * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access + */ + + fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777); + + if (fd_file == NULL) { + printf("testfile failed to create/open file %s\n",pathname); + return -1; + } + if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) { + printf("testfile PR_GetFileInfo failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } + if (PR_Access(pathname, PR_ACCESS_EXISTS) != 0) { + printf("testfile PR_Access failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } + if (PR_Access(pathname, PR_ACCESS_WRITE_OK) != 0) { + printf("testfile PR_Access failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } + if (PR_Access(pathname, PR_ACCESS_READ_OK) != 0) { + printf("testfile PR_Access failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } + + + if (PR_GetFileInfo(pathname, &file_info) < 0) { + printf("testfile PR_GetFileInfo failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } + if (file_info.type != PR_FILE_FILE) { + printf( + "testfile: Error - PR_GetFileInfo returned incorrect type for file %s\n", + pathname); + rv = -1; + goto cleanup; + } + if (file_info.size != 0) { + printf( + "testfile PR_GetFileInfo returned incorrect size (%d should be 0) for file %s\n", + file_info.size, pathname); + rv = -1; + goto cleanup; + } + file_info1 = file_info; + + len = PR_Available(fd_file); + if (len < 0) { + printf("testfile PR_Available failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } else if (len != 0) { + printf( + "testfile PR_Available failed: expected/returned = %d/%d bytes\n", + 0, len); + rv = -1; + goto cleanup; + } + if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) { + printf("testfile PR_GetFileInfo failed on file %s\n",pathname); + goto cleanup; + } + if (LL_NE(file_info.creationTime, file_info1.creationTime)) { + printf( + "testfile PR_GetFileInfo returned incorrect status-change time: %s\n", + pathname); + printf("ft = %lld, ft1 = %lld\n",file_info.creationTime, + file_info1.creationTime); + rv = -1; + goto cleanup; + } + len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE); + if (len < 0) { + printf("testfile failed to write to file %s\n",pathname); + rv = -1; + goto cleanup; + } + if (PR_GetOpenFileInfo(fd_file, &file_info) < 0) { + printf("testfile PR_GetFileInfo failed on file %s\n",pathname); + goto cleanup; + } + if (file_info.size != CHUNK_SIZE) { + printf( + "testfile PR_GetFileInfo returned incorrect size (%d should be %d) for file %s\n", + file_info.size, CHUNK_SIZE, pathname); + rv = -1; + goto cleanup; + } + if (LL_CMP(file_info.modifyTime, <, file_info1.modifyTime)) { + printf( + "testfile PR_GetFileInfo returned incorrect modify time: %s\n", + pathname); + printf("ft = %lld, ft1 = %lld\n",file_info.modifyTime, + file_info1.modifyTime); + rv = -1; + goto cleanup; + } + + len = PR_Available(fd_file); + if (len < 0) { + printf("testfile PR_Available failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } else if (len != 0) { + printf( + "testfile PR_Available failed: expected/returned = %d/%d bytes\n", + 0, len); + rv = -1; + goto cleanup; + } + + PR_Seek(fd_file, 0, PR_SEEK_SET); + len = PR_Available(fd_file); + if (len < 0) { + printf("testfile PR_Available failed on file %s\n",pathname); + rv = -1; + goto cleanup; + } else if (len != CHUNK_SIZE) { + printf( + "testfile PR_Available failed: expected/returned = %d/%d bytes\n", + CHUNK_SIZE, len); + rv = -1; + goto cleanup; + } + PR_Close(fd_file); + + strcpy(tmpname,pathname); + strcat(tmpname,".RENAMED"); + if (PR_FAILURE == PR_Rename(pathname, tmpname)) { + printf("testfile failed to rename file %s\n",pathname); + rv = -1; + goto cleanup; + } + + fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777); + len = PR_Write(fd_file, out_buf->data, CHUNK_SIZE); + PR_Close(fd_file); + if (PR_SUCCESS == PR_Rename(pathname, tmpname)) { + printf("testfile renamed to existing file %s\n",pathname); + } + + if ((PR_Delete(tmpname)) < 0) { + printf("testfile failed to unlink file %s\n",tmpname); + rv = -1; + } + +cleanup: + if ((PR_Delete(pathname)) < 0) { + printf("testfile failed to unlink file %s\n",pathname); + rv = -1; + } + return rv; +} + + +static PRInt32 PR_CALLBACK FileTest(void) +{ + PRDir *fd_dir; + int i, offset, len, rv = 0; + PRThread *t; + PRThreadScope scope = PR_GLOBAL_THREAD; + File_Rdwr_Param *fparamp; + + /* + * Create Test dir + */ + if ((PR_MkDir(TEST_DIR, 0777)) < 0) { + printf("testfile failed to create dir %s\n",TEST_DIR); + return -1; + } + fd_dir = PR_OpenDir(TEST_DIR); + if (fd_dir == NULL) { + printf("testfile failed to open dir %s\n",TEST_DIR); + rv = -1; + goto cleanup; + } + + PR_CloseDir(fd_dir); + + strcat(pathname, TEST_DIR); + strcat(pathname, "/"); + strcat(pathname, FILE_NAME); + + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + printf( + "testfile failed to alloc buffer struct\n"); + rv = -1; + goto cleanup; + } + out_buf = PR_NEW(buffer); + if (out_buf == NULL) { + printf( + "testfile failed to alloc buffer struct\n"); + rv = -1; + goto cleanup; + } + + /* + * Start a bunch of writer threads + */ + offset = 0; + len = CHUNK_SIZE; + PR_EnterMonitor(mon); + for (i = 0; i < NUM_RDWR_THREADS; i++) { + fparamp = PR_NEW(File_Rdwr_Param); + if (fparamp == NULL) { + printf( + "testfile failed to alloc File_Rdwr_Param struct\n"); + rv = -1; + goto cleanup; + } + fparamp->pathname = pathname; + fparamp->buf = out_buf->data + offset; + fparamp->offset = offset; + fparamp->len = len; + memset(fparamp->buf, i, len); + + t = create_new_thread(PR_USER_THREAD, + File_Write, (void *)fparamp, + PR_PRIORITY_NORMAL, + scope, + PR_UNJOINABLE_THREAD, + 0, i); + offset += len; + } + thread_count = i; + /* Wait for writer threads to exit */ + while (thread_count) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(mon); + + + /* + * Start a bunch of reader threads + */ + offset = 0; + len = CHUNK_SIZE; + PR_EnterMonitor(mon); + for (i = 0; i < NUM_RDWR_THREADS; i++) { + fparamp = PR_NEW(File_Rdwr_Param); + if (fparamp == NULL) { + printf( + "testfile failed to alloc File_Rdwr_Param struct\n"); + rv = -1; + goto cleanup; + } + fparamp->pathname = pathname; + fparamp->buf = in_buf->data + offset; + fparamp->offset = offset; + fparamp->len = len; + + t = create_new_thread(PR_USER_THREAD, + File_Read, (void *)fparamp, + PR_PRIORITY_NORMAL, + scope, + PR_UNJOINABLE_THREAD, + 0, i); + offset += len; + if ((offset + len) > BUF_DATA_SIZE) { + break; + } + } + thread_count = i; + + /* Wait for reader threads to exit */ + while (thread_count) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(mon); + + if (memcmp(in_buf->data, out_buf->data, offset) != 0) { + printf("File Test failed: file data corrupted\n"); + rv = -1; + goto cleanup; + } + + if ((PR_Delete(pathname)) < 0) { + printf("testfile failed to unlink file %s\n",pathname); + rv = -1; + goto cleanup; + } + + /* + * Test PR_Available, PR_Seek, PR_GetFileInfo, PR_Rename, PR_Access + */ + if (Misc_File_Tests(pathname) < 0) { + rv = -1; + } + +cleanup: + if ((PR_RmDir(TEST_DIR)) < 0) { + printf("testfile failed to rmdir %s\n", TEST_DIR); + rv = -1; + } + return rv; +} + +struct dirtest_arg { + PRMonitor *mon; + PRInt32 done; +}; + +static PRInt32 RunDirTest(void) +{ + int i; + PRThread *t; + PRMonitor *mon; + struct dirtest_arg thrarg; + + mon = PR_NewMonitor(); + if (!mon) { + printf("RunDirTest: Error - failed to create monitor\n"); + dirtest_failed = 1; + return -1; + } + thrarg.mon = mon; + + for (i = 0; i < NUM_DIRTEST_THREADS; i++) { + + thrarg.done= 0; + t = create_new_thread(PR_USER_THREAD, + DirTest, &thrarg, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, i); + if (!t) { + printf("RunDirTest: Error - failed to create thread\n"); + dirtest_failed = 1; + return -1; + } + PR_EnterMonitor(mon); + while (!thrarg.done) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + PR_ExitMonitor(mon); + + } + PR_DestroyMonitor(mon); + return 0; +} + +static PRInt32 PR_CALLBACK DirTest(void *arg) +{ + struct dirtest_arg *tinfo = (struct dirtest_arg *) arg; + PRFileDesc *fd_file; + PRDir *fd_dir; + int i; + int path_len; + PRDirEntry *dirEntry; + PRFileInfo info; + PRInt32 num_files = 0; +#if defined(XP_PC) && defined(WIN32) + HANDLE hfile; +#endif + +#define FILES_IN_DIR 20 + + /* + * Create Test dir + */ + DPRINTF(("Creating test dir %s\n",TEST_DIR)); + if ((PR_MkDir(TEST_DIR, 0777)) < 0) { + printf( + "testfile failed to create dir %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + fd_dir = PR_OpenDir(TEST_DIR); + if (fd_dir == NULL) { + printf( + "testfile failed to open dirctory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + strcpy(pathname, TEST_DIR); + strcat(pathname, "/"); + strcat(pathname, FILE_NAME); + path_len = strlen(pathname); + + for (i = 0; i < FILES_IN_DIR; i++) { + + sprintf(pathname + path_len,"%d%s",i,""); + + DPRINTF(("Creating test file %s\n",pathname)); + + fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777); + + if (fd_file == NULL) { + printf( + "testfile failed to create/open file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + PR_Close(fd_file); + } +#if defined(XP_UNIX) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2) + /* + * Create a hidden file - a platform-dependent operation + */ + strcpy(pathname, TEST_DIR); + strcat(pathname, "/"); + strcat(pathname, HIDDEN_FILE_NAME); +#if defined(XP_UNIX) + DPRINTF(("Creating hidden test file %s\n",pathname)); + fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, 0777); + + if (fd_file == NULL) { + printf( + "testfile failed to create/open hidden file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + + PR_Close(fd_file); + +#elif defined(WINCE) + DPRINTF(("Creating hidden test file %s\n",pathname)); + MultiByteToWideChar(CP_ACP, 0, pathname, -1, wPathname, 256); + hfile = CreateFile(wPathname, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_HIDDEN, + NULL); + if (hfile == INVALID_HANDLE_VALUE) { + printf("testfile failed to create/open hidden file %s [0, %d]\n", + pathname, GetLastError()); + return -1; + } + CloseHandle(hfile); + +#elif defined(XP_PC) && defined(WIN32) + DPRINTF(("Creating hidden test file %s\n",pathname)); + hfile = CreateFile(pathname, GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_HIDDEN, + NULL); + if (hfile == INVALID_HANDLE_VALUE) { + printf("testfile failed to create/open hidden file %s [0, %d]\n", + pathname, GetLastError()); + return -1; + } + CloseHandle(hfile); + +#elif defined(OS2) + DPRINTF(("Creating hidden test file %s\n",pathname)); + fd_file = PR_Open(pathname, PR_RDWR | PR_CREATE_FILE, (int)FILE_HIDDEN); + + if (fd_file == NULL) { + printf("testfile failed to create/open hidden file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + PR_Close(fd_file); +#endif /* XP_UNIX */ + +#endif /* XP_UNIX || (XP_PC && WIN32) */ + + + if (PR_FAILURE == PR_CloseDir(fd_dir)) + { + printf( + "testfile failed to close dirctory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + fd_dir = PR_OpenDir(TEST_DIR); + if (fd_dir == NULL) { + printf( + "testfile failed to reopen dirctory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + /* + * List all files, including hidden files + */ + DPRINTF(("Listing all files in directory %s\n",TEST_DIR)); +#if defined(XP_UNIX) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2) + num_files = FILES_IN_DIR + 1; +#else + num_files = FILES_IN_DIR; +#endif + while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_BOTH)) != NULL) { + num_files--; + strcpy(pathname, TEST_DIR); + strcat(pathname, "/"); + strcat(pathname, dirEntry->name); + DPRINTF(("\t%s\n",dirEntry->name)); + + if ((PR_GetFileInfo(pathname, &info)) < 0) { + printf( + "testfile failed to GetFileInfo file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + + if (info.type != PR_FILE_FILE) { + printf( + "testfile incorrect fileinfo for file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + } + if (num_files != 0) + { + printf( + "testfile failed to find all files in directory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + PR_CloseDir(fd_dir); + +#if defined(XP_UNIX) || (defined(XP_PC) && defined(WIN32)) || defined(XP_OS2) + + /* + * List all files, except hidden files + */ + + fd_dir = PR_OpenDir(TEST_DIR); + if (fd_dir == NULL) { + printf( + "testfile failed to reopen dirctory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + DPRINTF(("Listing non-hidden files in directory %s\n",TEST_DIR)); + while ((dirEntry = PR_ReadDir(fd_dir, PR_SKIP_HIDDEN)) != NULL) { + DPRINTF(("\t%s\n",dirEntry->name)); + if (!strcmp(HIDDEN_FILE_NAME, dirEntry->name)) { + printf("testfile found hidden file %s\n", pathname); + return -1; + } + + } + /* + * Delete hidden file + */ + strcpy(pathname, TEST_DIR); + strcat(pathname, "/"); + strcat(pathname, HIDDEN_FILE_NAME); + if (PR_FAILURE == PR_Delete(pathname)) { + printf( + "testfile failed to delete hidden file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + + PR_CloseDir(fd_dir); +#endif /* XP_UNIX || (XP_PC && WIN32) */ + + strcpy(renamename, TEST_DIR); + strcat(renamename, ".RENAMED"); + if (PR_FAILURE == PR_Rename(TEST_DIR, renamename)) { + printf( + "testfile failed to rename directory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + if (PR_FAILURE == PR_MkDir(TEST_DIR, 0777)) { + printf( + "testfile failed to recreate dir %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + if (PR_SUCCESS == PR_Rename(renamename, TEST_DIR)) { + printf( + "testfile renamed directory to existing name %s\n", + renamename); + return -1; + } + + if (PR_FAILURE == PR_RmDir(TEST_DIR)) { + printf( + "testfile failed to rmdir %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + if (PR_FAILURE == PR_Rename(renamename, TEST_DIR)) { + printf( + "testfile failed to rename directory %s [%d, %d]\n", + renamename, PR_GetError(), PR_GetOSError()); + return -1; + } + fd_dir = PR_OpenDir(TEST_DIR); + if (fd_dir == NULL) { + printf( + "testfile failed to reopen directory %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + + strcpy(pathname, TEST_DIR); + strcat(pathname, "/"); + strcat(pathname, FILE_NAME); + path_len = strlen(pathname); + + for (i = 0; i < FILES_IN_DIR; i++) { + + sprintf(pathname + path_len,"%d%s",i,""); + + if (PR_FAILURE == PR_Delete(pathname)) { + printf( + "testfile failed to delete file %s [%d, %d]\n", + pathname, PR_GetError(), PR_GetOSError()); + return -1; + } + } + + PR_CloseDir(fd_dir); + + if (PR_FAILURE == PR_RmDir(TEST_DIR)) { + printf( + "testfile failed to rmdir %s [%d, %d]\n", + TEST_DIR, PR_GetError(), PR_GetOSError()); + return -1; + } + PR_EnterMonitor(tinfo->mon); + tinfo->done = 1; + PR_Notify(tinfo->mon); + PR_ExitMonitor(tinfo->mon); + + return 0; +} +/************************************************************************/ + +/* + * Test file and directory NSPR APIs + */ + +int main(int argc, char **argv) +{ +#ifdef WIN32 + PRUint32 len; +#endif +#if defined(XP_UNIX) || defined(XP_OS2) + int opt; + extern char *optarg; + extern int optind; +#endif +#if defined(XP_UNIX) || defined(XP_OS2) + while ((opt = getopt(argc, argv, "d")) != EOF) { + switch(opt) { + case 'd': + _debug_on = 1; + break; + default: + break; + } + } +#endif + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + mon = PR_NewMonitor(); + if (mon == NULL) { + printf("testfile: PR_NewMonitor failed\n"); + exit(2); + } +#ifdef WIN32 + +#ifdef WINCE + { + WCHAR tdir[TMPDIR_LEN]; + len = GetTempPath(TMPDIR_LEN, tdir); + if ((len > 0) && (len < (TMPDIR_LEN - 6))) { + /* + * enough space for prdir + */ + WideCharToMultiByte(CP_ACP, 0, tdir, -1, testdir, TMPDIR_LEN, 0, 0); + } + } +#else + len = GetTempPath(TMPDIR_LEN, testdir); +#endif /* WINCE */ + + if ((len > 0) && (len < (TMPDIR_LEN - 6))) { + /* + * enough space for prdir + */ + strcpy((testdir + len),"prdir"); + TEST_DIR = testdir; + printf("TEST_DIR = %s\n",TEST_DIR); + } +#endif /* WIN32 */ + + if (FileTest() < 0) { + printf("File Test failed\n"); + exit(2); + } + printf("File Test passed\n"); + if ((RunDirTest() < 0) || dirtest_failed) { + printf("Dir Test failed\n"); + exit(2); + } + printf("Dir Test passed\n"); + + PR_DestroyMonitor(mon); + PR_Cleanup(); + return 0; +} diff --git a/nsprpub/pr/tests/threads.c b/nsprpub/pr/tests/threads.c new file mode 100644 index 0000000000..4a28dc6e58 --- /dev/null +++ b/nsprpub/pr/tests/threads.c @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" +#include "prinrval.h" +#include "plgetopt.h" +#include "pprthred.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +PRMonitor *mon; +PRInt32 count, iterations, alive; + +PRBool debug_mode = PR_FALSE, passed = PR_TRUE; + +void +PR_CALLBACK +ReallyDumbThread(void *arg) +{ + return; +} + +void +PR_CALLBACK +DumbThread(void *arg) +{ + PRInt32 tmp = (PRInt32)arg; + PRThreadScope scope = (PRThreadScope)tmp; + PRThread *thr; + + thr = PR_CreateThread(PR_USER_THREAD, + ReallyDumbThread, + NULL, + PR_PRIORITY_NORMAL, + scope, + PR_JOINABLE_THREAD, + 0); + + if (!thr) { + if (debug_mode) { + printf("Could not create really dumb thread (%d, %d)!\n", + PR_GetError(), PR_GetOSError()); + } + passed = PR_FALSE; + } else { + PR_JoinThread(thr); + } + PR_EnterMonitor(mon); + alive--; + PR_Notify(mon); + PR_ExitMonitor(mon); +} + +static void CreateThreads(PRThreadScope scope1, PRThreadScope scope2) +{ + PRThread *thr; + int n; + + alive = 0; + mon = PR_NewMonitor(); + + alive = count; + for (n=0; n<count; n++) { + thr = PR_CreateThread(PR_USER_THREAD, + DumbThread, + (void *)scope2, + PR_PRIORITY_NORMAL, + scope1, + PR_UNJOINABLE_THREAD, + 0); + if (!thr) { + if (debug_mode) { + printf("Could not create dumb thread (%d, %d)!\n", + PR_GetError(), PR_GetOSError()); + } + passed = PR_FALSE; + alive--; + } + + PR_Sleep(0); + } + + /* Wait for all threads to exit */ + PR_EnterMonitor(mon); + while (alive) { + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + } + + PR_ExitMonitor(mon); + PR_DestroyMonitor(mon); +} + +static void CreateThreadsUU(void) +{ + CreateThreads(PR_LOCAL_THREAD, PR_LOCAL_THREAD); +} + +static void CreateThreadsUK(void) +{ + CreateThreads(PR_LOCAL_THREAD, PR_GLOBAL_THREAD); +} + +static void CreateThreadsKU(void) +{ + CreateThreads(PR_GLOBAL_THREAD, PR_LOCAL_THREAD); +} + +static void CreateThreadsKK(void) +{ + CreateThreads(PR_GLOBAL_THREAD, PR_GLOBAL_THREAD); +} + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + if (debug_mode) + { + d = (double)PR_IntervalToMicroseconds(stop - start); + printf("%40s: %6.2f usec\n", msg, d / count); + } +} + +int main(int argc, char **argv) +{ + int index; + + PR_STDIO_INIT(); + PR_Init(PR_USER_THREAD, PR_PRIORITY_HIGH, 0); + + { + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:i:"); + 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 counter */ + count = atoi(opt->value); + break; + case 'i': /* loop counter */ + iterations = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + } + + if (0 == count) { + count = 50; + } + if (0 == iterations) { + iterations = 10; + } + + if (debug_mode) + { + printf("\ +** Tests lots of thread creations. \n\ +** Create %ld native threads %ld times. \n\ +** Create %ld user threads %ld times \n", iterations,count,iterations,count); + } + + for (index=0; index<iterations; index++) { + Measure(CreateThreadsUU, "Create user/user threads"); + Measure(CreateThreadsUK, "Create user/native threads"); + Measure(CreateThreadsKU, "Create native/user threads"); + Measure(CreateThreadsKK, "Create native/native threads"); + } + + if (debug_mode) { + printf("\nNow switch to recycling threads \n\n"); + } + PR_SetThreadRecycleMode(1); + + for (index=0; index<iterations; index++) { + Measure(CreateThreadsUU, "Create user/user threads"); + Measure(CreateThreadsUK, "Create user/native threads"); + Measure(CreateThreadsKU, "Create native/user threads"); + Measure(CreateThreadsKK, "Create native/native threads"); + } + + + printf("%s\n", ((passed) ? "PASS" : "FAIL")); + + PR_Cleanup(); + + if (passed) { + return 0; + } else { + return 1; + } +} diff --git a/nsprpub/pr/tests/thrpool_client.c b/nsprpub/pr/tests/thrpool_client.c new file mode 100644 index 0000000000..2c01a3c51d --- /dev/null +++ b/nsprpub/pr/tests/thrpool_client.c @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: thrpool_client.c +** +** Description: Test threadpool functionality. +** +** Modification History: +*/ +#include "primpl.h" + +#include "plgetopt.h" + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#ifdef XP_UNIX +#include <sys/mman.h> +#endif +#if defined(_PR_PTHREADS) +#include <pthread.h> +#endif + +#ifdef WIN32 +#include <process.h> +#endif + +static int _debug_on = 0; +static int server_port = -1; +static char *program_name = NULL; + +#include "obsolete/prsem.h" + +#ifdef XP_PC +#define mode_t int +#endif + +#define DPRINTF(arg) if (_debug_on) printf arg + +#define BUF_DATA_SIZE (2 * 1024) +#define TCP_MESG_SIZE 1024 +#define NUM_TCP_CLIENTS 10 /* for a listen queue depth of 5 */ + +#define NUM_TCP_CONNECTIONS_PER_CLIENT 10 +#define NUM_TCP_MESGS_PER_CONNECTION 10 +#define TCP_SERVER_PORT 10000 + +static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS; +static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT; +static PRInt32 tcp_mesg_size = TCP_MESG_SIZE; +static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION; + +int failed_already=0; + +typedef struct buffer { + char data[BUF_DATA_SIZE]; +} buffer; + +PRNetAddr tcp_server_addr, udp_server_addr; + +typedef struct Client_Param { + PRNetAddr server_addr; + PRMonitor *exit_mon; /* monitor to signal on exit */ + PRInt32 *exit_counter; /* counter to decrement, before exit */ + PRInt32 datalen; +} Client_Param; + +/* + * readn + * read data from sockfd into buf + */ +static PRInt32 +readn(PRFileDesc *sockfd, char *buf, int len) +{ + int rem; + int bytes; + int offset = 0; + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; + + for (rem=len; rem; offset += bytes, rem -= bytes) { + DPRINTF(("thread = 0x%lx: calling PR_Recv, bytes = %d\n", + PR_GetCurrentThread(), rem)); + bytes = PR_Recv(sockfd, buf + offset, rem, 0, + timeout); + DPRINTF(("thread = 0x%lx: returning from PR_Recv, bytes = %d\n", + PR_GetCurrentThread(), bytes)); + if (bytes < 0) { + return -1; + } + } + return len; +} + +/* + * writen + * write data from buf to sockfd + */ +static PRInt32 +writen(PRFileDesc *sockfd, char *buf, int len) +{ + int rem; + int bytes; + int offset = 0; + + for (rem=len; rem; offset += bytes, rem -= bytes) { + DPRINTF(("thread = 0x%lx: calling PR_Send, bytes = %d\n", + PR_GetCurrentThread(), rem)); + bytes = PR_Send(sockfd, buf + offset, rem, 0, + PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("thread = 0x%lx: returning from PR_Send, bytes = %d\n", + PR_GetCurrentThread(), bytes)); + if (bytes <= 0) { + return -1; + } + } + return len; +} + +/* + * TCP_Client + * Client job + * Connect to the server at the address specified in the argument. + * Fill in a buffer, write data to server, read it back and check + * for data corruption. + * Close the socket for server connection + */ +static void PR_CALLBACK +TCP_Client(void *arg) +{ + Client_Param *cp = (Client_Param *) arg; + PRFileDesc *sockfd; + buffer *in_buf, *out_buf; + union PRNetAddr netaddr; + PRInt32 bytes, i, j; + + + DPRINTF(("TCP client started\n")); + bytes = cp->datalen; + out_buf = PR_NEW(buffer); + if (out_buf == NULL) { + fprintf(stderr,"%s: failed to alloc buffer struct\n", program_name); + failed_already=1; + return; + } + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + fprintf(stderr,"%s: failed to alloc buffer struct\n", program_name); + failed_already=1; + return; + } + netaddr.inet.family = cp->server_addr.inet.family; + netaddr.inet.port = cp->server_addr.inet.port; + netaddr.inet.ip = cp->server_addr.inet.ip; + + for (i = 0; i < num_tcp_connections_per_client; i++) { + if ((sockfd = PR_OpenTCPSocket(PR_AF_INET)) == NULL) { + fprintf(stderr,"%s: PR_OpenTCPSocket failed\n", program_name); + failed_already=1; + return; + } + + DPRINTF(("TCP client connecting to server:%d\n", server_port)); + if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0) { + fprintf(stderr, "PR_Connect failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + return; + } + for (j = 0; j < num_tcp_mesgs_per_connection; j++) { + /* + * fill in random data + */ + memset(out_buf->data, ((PRInt32) (&netaddr)) + i + j, bytes); + /* + * write to server + */ + if (writen(sockfd, out_buf->data, bytes) < bytes) { + fprintf(stderr,"%s: ERROR - TCP_Client:writen\n", program_name); + failed_already=1; + return; + } + /* + DPRINTF(("TCP Client [0x%lx]: out_buf = 0x%lx out_buf[0] = 0x%lx\n", + PR_GetCurrentThread(), out_buf, (*((int *) out_buf->data)))); + */ + if (readn(sockfd, in_buf->data, bytes) < bytes) { + fprintf(stderr,"%s: ERROR - TCP_Client:readn\n", program_name); + failed_already=1; + return; + } + /* + * verify the data read + */ + if (memcmp(in_buf->data, out_buf->data, bytes) != 0) { + fprintf(stderr,"%s: ERROR - data corruption\n", program_name); + failed_already=1; + return; + } + } + /* + * shutdown reads and writes + */ + if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) { + fprintf(stderr,"%s: ERROR - PR_Shutdown\n", program_name); + failed_already=1; + } + PR_Close(sockfd); + } + + PR_DELETE(out_buf); + PR_DELETE(in_buf); + + /* + * Decrement exit_counter and notify parent thread + */ + + PR_EnterMonitor(cp->exit_mon); + --(*cp->exit_counter); + PR_Notify(cp->exit_mon); + PR_ExitMonitor(cp->exit_mon); + DPRINTF(("TCP_Client exiting\n")); +} + +/* + * TCP_Socket_Client_Server_Test - concurrent server test + * + * Each client connects to the server and sends a chunk of data + * For each connection, server reads the data + * from the client and sends it back to the client, unmodified. + * Each client checks that data received from server is same as the + * data it sent to the server. + * + */ + +static PRInt32 +TCP_Socket_Client_Server_Test(void) +{ + int i; + Client_Param *cparamp; + PRMonitor *mon2; + PRInt32 datalen; + PRInt32 connections = 0; + PRThread *thr; + + datalen = tcp_mesg_size; + connections = 0; + + mon2 = PR_NewMonitor(); + if (mon2 == NULL) { + fprintf(stderr,"%s: PR_NewMonitor failed\n", program_name); + failed_already=1; + return -1; + } + + /* + * Start client jobs + */ + cparamp = PR_NEW(Client_Param); + if (cparamp == NULL) { + fprintf(stderr,"%s: PR_NEW failed\n", program_name); + failed_already=1; + return -1; + } + cparamp->server_addr.inet.family = PR_AF_INET; + cparamp->server_addr.inet.port = PR_htons(server_port); + cparamp->server_addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + cparamp->exit_mon = mon2; + cparamp->exit_counter = &connections; + cparamp->datalen = datalen; + for (i = 0; i < num_tcp_clients; i++) { + thr = PR_CreateThread(PR_USER_THREAD, TCP_Client, (void *)cparamp, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); + if (NULL == thr) { + fprintf(stderr,"%s: PR_CreateThread failed\n", program_name); + failed_already=1; + return -1; + } + PR_EnterMonitor(mon2); + connections++; + PR_ExitMonitor(mon2); + DPRINTF(("Created TCP client = 0x%lx\n", thr)); + } + /* Wait for client jobs to exit */ + PR_EnterMonitor(mon2); + while (0 != connections) { + PR_Wait(mon2, PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("Client job count = %d\n", connections)); + } + PR_ExitMonitor(mon2); + printf("%30s","TCP_Socket_Client_Server_Test:"); + printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l, + num_tcp_clients, num_tcp_connections_per_client); + printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":", + num_tcp_mesgs_per_connection, tcp_mesg_size); + + PR_DELETE(cparamp); + return 0; +} + +/************************************************************************/ + +int main(int argc, char **argv) +{ + /* + * -d debug mode + */ + PLOptStatus os; + PLOptState *opt; + program_name = argv[0]; + + opt = PL_CreateOptState(argc, argv, "dp:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + case 'p': + server_port = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetConcurrency(4); + + TCP_Socket_Client_Server_Test(); + + PR_Cleanup(); + if (failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/thrpool_server.c b/nsprpub/pr/tests/thrpool_server.c new file mode 100644 index 0000000000..ecae117a8e --- /dev/null +++ b/nsprpub/pr/tests/thrpool_server.c @@ -0,0 +1,579 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: thrpool.c +** +** Description: Test threadpool functionality. +** +** Modification History: +*/ +#include "primpl.h" + +#include "plgetopt.h" + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#ifdef XP_UNIX +#include <sys/mman.h> +#endif +#if defined(_PR_PTHREADS) +#include <pthread.h> +#endif + +/* for getcwd */ +#if defined(XP_UNIX) || defined (XP_OS2) +#include <unistd.h> +#elif defined(XP_PC) +#include <direct.h> +#endif + +#ifdef WIN32 +#include <process.h> +#endif + +static int _debug_on = 0; +static char *program_name = NULL; +static void serve_client_write(void *arg); + +#include "obsolete/prsem.h" + +#ifdef XP_PC +#define mode_t int +#endif + +#define DPRINTF(arg) if (_debug_on) printf arg + + +#define BUF_DATA_SIZE (2 * 1024) +#define TCP_MESG_SIZE 1024 +#define NUM_TCP_CLIENTS 10 /* for a listen queue depth of 5 */ + + +#define NUM_TCP_CONNECTIONS_PER_CLIENT 10 +#define NUM_TCP_MESGS_PER_CONNECTION 10 +#define TCP_SERVER_PORT 10000 +#define SERVER_MAX_BIND_COUNT 100 + +#ifdef WINCE +char *getcwd(char *buf, size_t size) +{ + wchar_t wpath[MAX_PATH]; + _wgetcwd(wpath, MAX_PATH); + WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0); +} + +#define perror(s) +#endif + +static PRInt32 num_tcp_clients = NUM_TCP_CLIENTS; +static PRInt32 num_tcp_connections_per_client = NUM_TCP_CONNECTIONS_PER_CLIENT; +static PRInt32 tcp_mesg_size = TCP_MESG_SIZE; +static PRInt32 num_tcp_mesgs_per_connection = NUM_TCP_MESGS_PER_CONNECTION; +static void TCP_Server_Accept(void *arg); + + +int failed_already=0; +typedef struct buffer { + char data[BUF_DATA_SIZE]; +} buffer; + + +typedef struct Server_Param { + PRJobIoDesc iod; /* socket to read from/write to */ + PRInt32 datalen; /* bytes of data transfered in each read/write */ + PRNetAddr netaddr; + PRMonitor *exit_mon; /* monitor to signal on exit */ + PRInt32 *job_counterp; /* counter to decrement, before exit */ + PRInt32 conn_counter; /* counter to decrement, before exit */ + PRThreadPool *tp; +} Server_Param; + +typedef struct Serve_Client_Param { + PRJobIoDesc iod; /* socket to read from/write to */ + PRInt32 datalen; /* bytes of data transfered in each read/write */ + PRMonitor *exit_mon; /* monitor to signal on exit */ + PRInt32 *job_counterp; /* counter to decrement, before exit */ + PRThreadPool *tp; +} Serve_Client_Param; + +typedef struct Session { + PRJobIoDesc iod; /* socket to read from/write to */ + buffer *in_buf; + PRInt32 bytes; + PRInt32 msg_num; + PRInt32 bytes_read; + PRMonitor *exit_mon; /* monitor to signal on exit */ + PRInt32 *job_counterp; /* counter to decrement, before exit */ + PRThreadPool *tp; +} Session; + +static void +serve_client_read(void *arg) +{ + Session *sp = (Session *) arg; + int rem; + int bytes; + int offset; + PRFileDesc *sockfd; + char *buf; + PRJob *jobp; + + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; + + sockfd = sp->iod.socket; + buf = sp->in_buf->data; + + PR_ASSERT(sp->msg_num < num_tcp_mesgs_per_connection); + PR_ASSERT(sp->bytes_read < sp->bytes); + + offset = sp->bytes_read; + rem = sp->bytes - offset; + bytes = PR_Recv(sockfd, buf + offset, rem, 0, timeout); + if (bytes < 0) { + return; + } + sp->bytes_read += bytes; + sp->iod.timeout = PR_SecondsToInterval(60); + if (sp->bytes_read < sp->bytes) { + jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + return; + } + PR_ASSERT(sp->bytes_read == sp->bytes); + DPRINTF(("serve_client: read complete, msg(%d) \n", sp->msg_num)); + + sp->iod.timeout = PR_SecondsToInterval(60); + jobp = PR_QueueJob_Write(sp->tp, &sp->iod, serve_client_write, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + + return; +} + +static void +serve_client_write(void *arg) +{ + Session *sp = (Session *) arg; + int bytes; + PRFileDesc *sockfd; + char *buf; + PRJob *jobp; + + sockfd = sp->iod.socket; + buf = sp->in_buf->data; + + PR_ASSERT(sp->msg_num < num_tcp_mesgs_per_connection); + + bytes = PR_Send(sockfd, buf, sp->bytes, 0, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(bytes == sp->bytes); + + if (bytes < 0) { + return; + } + DPRINTF(("serve_client: write complete, msg(%d) \n", sp->msg_num)); + sp->msg_num++; + if (sp->msg_num < num_tcp_mesgs_per_connection) { + sp->bytes_read = 0; + sp->iod.timeout = PR_SecondsToInterval(60); + jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + return; + } + + DPRINTF(("serve_client: read/write complete, msg(%d) \n", sp->msg_num)); + if (PR_Shutdown(sockfd, PR_SHUTDOWN_BOTH) < 0) { + fprintf(stderr,"%s: ERROR - PR_Shutdown\n", program_name); + } + + PR_Close(sockfd); + PR_EnterMonitor(sp->exit_mon); + --(*sp->job_counterp); + PR_Notify(sp->exit_mon); + PR_ExitMonitor(sp->exit_mon); + + PR_DELETE(sp->in_buf); + PR_DELETE(sp); + + return; +} + +/* + * Serve_Client + * Thread, started by the server, for serving a client connection. + * Reads data from socket and writes it back, unmodified, and + * closes the socket + */ +static void PR_CALLBACK +Serve_Client(void *arg) +{ + Serve_Client_Param *scp = (Serve_Client_Param *) arg; + buffer *in_buf; + Session *sp; + PRJob *jobp; + + sp = PR_NEW(Session); + sp->iod = scp->iod; + + in_buf = PR_NEW(buffer); + if (in_buf == NULL) { + fprintf(stderr,"%s: failed to alloc buffer struct\n",program_name); + failed_already=1; + return; + } + + sp->in_buf = in_buf; + sp->bytes = scp->datalen; + sp->msg_num = 0; + sp->bytes_read = 0; + sp->tp = scp->tp; + sp->exit_mon = scp->exit_mon; + sp->job_counterp = scp->job_counterp; + + sp->iod.timeout = PR_SecondsToInterval(60); + jobp = PR_QueueJob_Read(sp->tp, &sp->iod, serve_client_read, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + PR_DELETE(scp); +} + +static void +print_stats(void *arg) +{ + Server_Param *sp = (Server_Param *) arg; + PRThreadPool *tp = sp->tp; + PRInt32 counter; + PRJob *jobp; + + PR_EnterMonitor(sp->exit_mon); + counter = (*sp->job_counterp); + PR_ExitMonitor(sp->exit_mon); + + printf("PRINT_STATS: #client connections = %d\n",counter); + + + jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(500), + print_stats, sp, PR_FALSE); + + PR_ASSERT(NULL != jobp); +} + +static int job_counter = 0; +/* + * TCP Server + * Server binds an address to a socket, starts a client process and + * listens for incoming connections. + * Each client connects to the server and sends a chunk of data + * Starts a Serve_Client job for each incoming connection, to read + * the data from the client and send it back to the client, unmodified. + * Each client checks that data received from server is same as the + * data it sent to the server. + * Finally, the threadpool is shutdown + */ +static void PR_CALLBACK +TCP_Server(void *arg) +{ + PRThreadPool *tp = (PRThreadPool *) arg; + Server_Param *sp; + PRFileDesc *sockfd; + PRNetAddr netaddr; + PRMonitor *sc_mon; + PRJob *jobp; + int i; + PRStatus rval; + + /* + * Create a tcp socket + */ + if ((sockfd = PR_NewTCPSocket()) == NULL) { + fprintf(stderr,"%s: PR_NewTCPSocket failed\n", program_name); + return; + } + memset(&netaddr, 0, sizeof(netaddr)); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.port = PR_htons(TCP_SERVER_PORT); + netaddr.inet.ip = PR_htonl(PR_INADDR_ANY); + /* + * try a few times to bind server's address, if addresses are in + * use + */ + i = 0; + while (PR_Bind(sockfd, &netaddr) < 0) { + if (PR_GetError() == PR_ADDRESS_IN_USE_ERROR) { + netaddr.inet.port += 2; + if (i++ < SERVER_MAX_BIND_COUNT) { + continue; + } + } + fprintf(stderr,"%s: ERROR - PR_Bind failed\n", program_name); + perror("PR_Bind"); + failed_already=1; + return; + } + + if (PR_Listen(sockfd, 32) < 0) { + fprintf(stderr,"%s: ERROR - PR_Listen failed\n", program_name); + failed_already=1; + return; + } + + if (PR_GetSockName(sockfd, &netaddr) < 0) { + fprintf(stderr,"%s: ERROR - PR_GetSockName failed\n", program_name); + failed_already=1; + return; + } + + DPRINTF(( + "TCP_Server: PR_BIND netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", + netaddr.inet.ip, netaddr.inet.port)); + + sp = PR_NEW(Server_Param); + if (sp == NULL) { + fprintf(stderr,"%s: PR_NEW failed\n", program_name); + failed_already=1; + return; + } + sp->iod.socket = sockfd; + sp->iod.timeout = PR_SecondsToInterval(60); + sp->datalen = tcp_mesg_size; + sp->exit_mon = sc_mon; + sp->job_counterp = &job_counter; + sp->conn_counter = 0; + sp->tp = tp; + sp->netaddr = netaddr; + + /* create and cancel an io job */ + jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + rval = PR_CancelJob(jobp); + PR_ASSERT(PR_SUCCESS == rval); + + /* + * create the client process + */ + { +#define MAX_ARGS 4 + char *argv[MAX_ARGS + 1]; + int index = 0; + char port[32]; + char path[1024 + sizeof("/thrpool_client")]; + + getcwd(path, sizeof(path)); + + (void)strcat(path, "/thrpool_client"); +#ifdef XP_PC + (void)strcat(path, ".exe"); +#endif + argv[index++] = path; + sprintf(port,"%d",PR_ntohs(netaddr.inet.port)); + if (_debug_on) + { + argv[index++] = "-d"; + argv[index++] = "-p"; + argv[index++] = port; + argv[index++] = NULL; + } else { + argv[index++] = "-p"; + argv[index++] = port; + argv[index++] = NULL; + } + PR_ASSERT(MAX_ARGS >= (index - 1)); + + DPRINTF(("creating client process %s ...\n", path)); + if (PR_FAILURE == PR_CreateProcessDetached(path, argv, NULL, NULL)) { + fprintf(stderr, + "thrpool_server: ERROR - PR_CreateProcessDetached failed\n"); + failed_already=1; + return; + } + } + + sc_mon = PR_NewMonitor(); + if (sc_mon == NULL) { + fprintf(stderr,"%s: PR_NewMonitor failed\n", program_name); + failed_already=1; + return; + } + + sp->iod.socket = sockfd; + sp->iod.timeout = PR_SecondsToInterval(60); + sp->datalen = tcp_mesg_size; + sp->exit_mon = sc_mon; + sp->job_counterp = &job_counter; + sp->conn_counter = 0; + sp->tp = tp; + sp->netaddr = netaddr; + + /* create and cancel a timer job */ + jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(5000), + print_stats, sp, PR_FALSE); + PR_ASSERT(NULL != jobp); + rval = PR_CancelJob(jobp); + PR_ASSERT(PR_SUCCESS == rval); + + DPRINTF(("TCP_Server: Accepting connections \n")); + + jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + return; +} + +static void +TCP_Server_Accept(void *arg) +{ + Server_Param *sp = (Server_Param *) arg; + PRThreadPool *tp = sp->tp; + Serve_Client_Param *scp; + PRFileDesc *newsockfd; + PRJob *jobp; + + if ((newsockfd = PR_Accept(sp->iod.socket, &sp->netaddr, + PR_INTERVAL_NO_TIMEOUT)) == NULL) { + fprintf(stderr,"%s: ERROR - PR_Accept failed\n", program_name); + failed_already=1; + goto exit; + } + scp = PR_NEW(Serve_Client_Param); + if (scp == NULL) { + fprintf(stderr,"%s: PR_NEW failed\n", program_name); + failed_already=1; + goto exit; + } + + /* + * Start a Serve_Client job for each incoming connection + */ + scp->iod.socket = newsockfd; + scp->iod.timeout = PR_SecondsToInterval(60); + scp->datalen = tcp_mesg_size; + scp->exit_mon = sp->exit_mon; + scp->job_counterp = sp->job_counterp; + scp->tp = sp->tp; + + PR_EnterMonitor(sp->exit_mon); + (*sp->job_counterp)++; + PR_ExitMonitor(sp->exit_mon); + jobp = PR_QueueJob(tp, Serve_Client, scp, + PR_FALSE); + + PR_ASSERT(NULL != jobp); + DPRINTF(("TCP_Server: Created Serve_Client = 0x%lx\n", jobp)); + + /* + * single-threaded update; no lock needed + */ + sp->conn_counter++; + if (sp->conn_counter < + (num_tcp_clients * num_tcp_connections_per_client)) { + jobp = PR_QueueJob_Accept(tp, &sp->iod, TCP_Server_Accept, sp, + PR_FALSE); + PR_ASSERT(NULL != jobp); + return; + } + jobp = PR_QueueJob_Timer(tp, PR_MillisecondsToInterval(500), + print_stats, sp, PR_FALSE); + + PR_ASSERT(NULL != jobp); + DPRINTF(("TCP_Server: Created print_stats timer job = 0x%lx\n", jobp)); + +exit: + PR_EnterMonitor(sp->exit_mon); + /* Wait for server jobs to finish */ + while (0 != *sp->job_counterp) { + PR_Wait(sp->exit_mon, PR_INTERVAL_NO_TIMEOUT); + DPRINTF(("TCP_Server: conn_counter = %d\n", + *sp->job_counterp)); + } + + PR_ExitMonitor(sp->exit_mon); + if (sp->iod.socket) { + PR_Close(sp->iod.socket); + } + PR_DestroyMonitor(sp->exit_mon); + printf("%30s","TCP_Socket_Client_Server_Test:"); + printf("%2ld Server %2ld Clients %2ld connections_per_client\n",1l, + num_tcp_clients, num_tcp_connections_per_client); + printf("%30s %2ld messages_per_connection %4ld bytes_per_message\n",":", + num_tcp_mesgs_per_connection, tcp_mesg_size); + + DPRINTF(("%s: calling PR_ShutdownThreadPool\n", program_name)); + PR_ShutdownThreadPool(sp->tp); + PR_DELETE(sp); +} + +/************************************************************************/ + +#define DEFAULT_INITIAL_THREADS 4 +#define DEFAULT_MAX_THREADS 100 +#define DEFAULT_STACKSIZE (512 * 1024) + +int main(int argc, char **argv) +{ + PRInt32 initial_threads = DEFAULT_INITIAL_THREADS; + PRInt32 max_threads = DEFAULT_MAX_THREADS; + PRInt32 stacksize = DEFAULT_STACKSIZE; + PRThreadPool *tp = NULL; + PRStatus rv; + PRJob *jobp; + + /* + * -d debug mode + */ + PLOptStatus os; + PLOptState *opt; + + program_name = argv[0]; + opt = PL_CreateOptState(argc, argv, "d"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + PR_SetConcurrency(4); + + tp = PR_CreateThreadPool(initial_threads, max_threads, stacksize); + if (NULL == tp) { + printf("PR_CreateThreadPool failed\n"); + failed_already=1; + goto done; + } + jobp = PR_QueueJob(tp, TCP_Server, tp, PR_TRUE); + rv = PR_JoinJob(jobp); + PR_ASSERT(PR_SUCCESS == rv); + + DPRINTF(("%s: calling PR_JoinThreadPool\n", program_name)); + rv = PR_JoinThreadPool(tp); + PR_ASSERT(PR_SUCCESS == rv); + DPRINTF(("%s: returning from PR_JoinThreadPool\n", program_name)); + +done: + PR_Cleanup(); + if (failed_already) { + return 1; + } + else { + return 0; + } +} diff --git a/nsprpub/pr/tests/thruput.c b/nsprpub/pr/tests/thruput.c new file mode 100644 index 0000000000..156bbde9a3 --- /dev/null +++ b/nsprpub/pr/tests/thruput.c @@ -0,0 +1,414 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: thruput.c +** Description: Test server's throughput capability comparing various +** implmentation strategies. +** +** Note: Requires a server machine and an aribitrary number of +** clients to bang on it. Trust the numbers on the server +** more than those being displayed by the various clients. +*/ + +#include "prerror.h" +#include "prinrval.h" +#include "prinit.h" +#include "prio.h" +#include "prlock.h" +#include "prmem.h" +#include "prnetdb.h" +#include "prprf.h" +#include "prthread.h" +#include "pprio.h" +#include "plerror.h" +#include "plgetopt.h" + +#define ADDR_BUFFER 100 + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define PORT_NUMBER 51877 PORT_INC_DO PORT_INC_3264 + +#define SAMPLING_INTERVAL 10 +#define BUFFER_SIZE (32 * 1024) + +static PRInt32 domain = PR_AF_INET; +static PRInt32 protocol = 6; /* TCP */ +static PRFileDesc *err = NULL; +static PRIntn concurrency = 1; +static PRInt32 xport_buffer = -1; +static PRUint32 initial_streams = 1; +static PRInt32 buffer_size = BUFFER_SIZE; +static PRThreadScope thread_scope = PR_LOCAL_THREAD; + +typedef struct Shared +{ + PRLock *ml; + PRUint32 sampled; + PRUint32 threads; + PRIntervalTime timein; + PRNetAddr server_address; +} Shared; + +static Shared *shared = NULL; + +static PRStatus PrintAddress(const PRNetAddr* address) +{ + char buffer[ADDR_BUFFER]; + PRStatus rv = PR_NetAddrToString(address, buffer, sizeof(buffer)); + if (PR_SUCCESS == rv) { + PR_fprintf(err, "%s:%u\n", buffer, PR_ntohs(address->inet.port)); + } + else { + PL_FPrintError(err, "PR_NetAddrToString"); + } + return rv; +} /* PrintAddress */ + + +static void PR_CALLBACK Clientel(void *arg) +{ + PRStatus rv; + PRFileDesc *xport; + PRInt32 bytes, sampled; + PRIntervalTime now, interval; + PRBool do_display = PR_FALSE; + Shared *shared = (Shared*)arg; + char *buffer = (char*)PR_Malloc(buffer_size); + PRNetAddr *server_address = &shared->server_address; + PRIntervalTime connect_timeout = PR_SecondsToInterval(5); + PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL); + + PR_fprintf(err, "Client connecting to "); + (void)PrintAddress(server_address); + + do + { + xport = PR_Socket(domain, PR_SOCK_STREAM, protocol); + if (NULL == xport) + { + PL_FPrintError(err, "PR_Socket"); + return; + } + + if (xport_buffer != -1) + { + PRSocketOptionData data; + data.option = PR_SockOpt_RecvBufferSize; + data.value.recv_buffer_size = (PRSize)xport_buffer; + rv = PR_SetSocketOption(xport, &data); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_SetSocketOption - ignored"); + } + data.option = PR_SockOpt_SendBufferSize; + data.value.send_buffer_size = (PRSize)xport_buffer; + rv = PR_SetSocketOption(xport, &data); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_SetSocketOption - ignored"); + } + } + + rv = PR_Connect(xport, server_address, connect_timeout); + if (PR_FAILURE == rv) + { + PL_FPrintError(err, "PR_Connect"); + if (PR_IO_TIMEOUT_ERROR != PR_GetError()) { + PR_Sleep(connect_timeout); + } + PR_Close(xport); /* delete it and start over */ + } + } while (PR_FAILURE == rv); + + do + { + bytes = PR_Recv( + xport, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT); + PR_Lock(shared->ml); + now = PR_IntervalNow(); + shared->sampled += bytes; + interval = now - shared->timein; + if (interval > sampling_interval) + { + sampled = shared->sampled; + shared->timein = now; + shared->sampled = 0; + do_display = PR_TRUE; + } + PR_Unlock(shared->ml); + + if (do_display) + { + PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval); + PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate); + do_display = PR_FALSE; + } + + } while (bytes > 0); +} /* Clientel */ + +static void Client(const char *server_name) +{ + PRStatus rv; + PRHostEnt host; + char buffer[PR_NETDB_BUF_SIZE]; + PRIntervalTime dally = PR_SecondsToInterval(60); + PR_fprintf(err, "Translating the name %s\n", server_name); + rv = PR_GetHostByName(server_name, buffer, sizeof(buffer), &host); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_GetHostByName"); + } + else + { + if (PR_EnumerateHostEnt( + 0, &host, PORT_NUMBER, &shared->server_address) < 0) { + PL_FPrintError(err, "PR_EnumerateHostEnt"); + } + else + { + do + { + shared->threads += 1; + (void)PR_CreateThread( + PR_USER_THREAD, Clientel, shared, + PR_PRIORITY_NORMAL, thread_scope, + PR_UNJOINABLE_THREAD, 8 * 1024); + if (shared->threads == initial_streams) + { + PR_Sleep(dally); + initial_streams += 1; + } + } while (PR_TRUE); + } + } +} + +static void PR_CALLBACK Servette(void *arg) +{ + PRInt32 bytes, sampled; + PRIntervalTime now, interval; + PRBool do_display = PR_FALSE; + PRFileDesc *client = (PRFileDesc*)arg; + char *buffer = (char*)PR_Malloc(buffer_size); + PRIntervalTime sampling_interval = PR_SecondsToInterval(SAMPLING_INTERVAL); + + if (xport_buffer != -1) + { + PRStatus rv; + PRSocketOptionData data; + data.option = PR_SockOpt_RecvBufferSize; + data.value.recv_buffer_size = (PRSize)xport_buffer; + rv = PR_SetSocketOption(client, &data); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_SetSocketOption - ignored"); + } + data.option = PR_SockOpt_SendBufferSize; + data.value.send_buffer_size = (PRSize)xport_buffer; + rv = PR_SetSocketOption(client, &data); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_SetSocketOption - ignored"); + } + } + + do + { + bytes = PR_Send( + client, buffer, buffer_size, 0, PR_INTERVAL_NO_TIMEOUT); + + PR_Lock(shared->ml); + now = PR_IntervalNow(); + shared->sampled += bytes; + interval = now - shared->timein; + if (interval > sampling_interval) + { + sampled = shared->sampled; + shared->timein = now; + shared->sampled = 0; + do_display = PR_TRUE; + } + PR_Unlock(shared->ml); + + if (do_display) + { + PRUint32 rate = sampled / PR_IntervalToMilliseconds(interval); + PR_fprintf(err, "%u streams @ %u Kbytes/sec\n", shared->threads, rate); + do_display = PR_FALSE; + } + } while (bytes > 0); +} /* Servette */ + +static void Server(void) +{ + PRStatus rv; + PRNetAddr server_address, client_address; + PRFileDesc *xport = PR_Socket(domain, PR_SOCK_STREAM, protocol); + + if (NULL == xport) + { + PL_FPrintError(err, "PR_Socket"); + return; + } + + rv = PR_InitializeNetAddr(PR_IpAddrAny, PORT_NUMBER, &server_address); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_InitializeNetAddr"); + } + else + { + rv = PR_Bind(xport, &server_address); + if (PR_FAILURE == rv) { + PL_FPrintError(err, "PR_Bind"); + } + else + { + PRFileDesc *client; + rv = PR_Listen(xport, 10); + PR_fprintf(err, "Server listening on "); + (void)PrintAddress(&server_address); + do + { + client = PR_Accept( + xport, &client_address, PR_INTERVAL_NO_TIMEOUT); + if (NULL == client) { + PL_FPrintError(err, "PR_Accept"); + } + else + { + PR_fprintf(err, "Server accepting from "); + (void)PrintAddress(&client_address); + shared->threads += 1; + (void)PR_CreateThread( + PR_USER_THREAD, Servette, client, + PR_PRIORITY_NORMAL, thread_scope, + PR_UNJOINABLE_THREAD, 8 * 1024); + } + } while (PR_TRUE); + + } + } +} /* Server */ + +static void Help(void) +{ + PR_fprintf(err, "Usage: [-h] [<server>]\n"); + PR_fprintf(err, "\t-s <n> Initial # of connections (default: 1)\n"); + PR_fprintf(err, "\t-C <n> Set 'concurrency' (default: 1)\n"); + PR_fprintf(err, "\t-b <nK> Client buffer size (default: 32k)\n"); + PR_fprintf(err, "\t-B <nK> Transport recv/send buffer size (default: sys)\n"); + PR_fprintf(err, "\t-G Use GLOBAL threads (default: LOCAL)\n"); + PR_fprintf(err, "\t-X Use XTP transport (default: TCP)\n"); + PR_fprintf(err, "\t-6 Use IPv6 (default: IPv4)\n"); + PR_fprintf(err, "\t-h This message and nothing else\n"); + PR_fprintf(err, "\t<server> DNS name of server\n"); + PR_fprintf(err, "\t\tIf <server> is not specified, this host will be\n"); + PR_fprintf(err, "\t\tthe server and not act as a client.\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + const char *server_name = NULL; + PLOptState *opt = PL_CreateOptState(argc, argv, "hGX6C:b:s:B:"); + + err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: /* Name of server */ + server_name = opt->value; + break; + case 'G': /* Globular threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'X': /* Use XTP as the transport */ + protocol = 36; + break; + case '6': /* Use IPv6 */ + domain = PR_AF_INET6; + break; + case 's': /* initial_streams */ + initial_streams = atoi(opt->value); + break; + case 'C': /* concurrency */ + concurrency = atoi(opt->value); + break; + case 'b': /* buffer size */ + buffer_size = 1024 * atoi(opt->value); + break; + case 'B': /* buffer size */ + xport_buffer = 1024 * atoi(opt->value); + break; + case 'h': /* user wants some guidance */ + default: + Help(); /* so give him an earful */ + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + + shared = PR_NEWZAP(Shared); + shared->ml = PR_NewLock(); + + PR_fprintf(err, + "This machine is %s\n", + (NULL == server_name) ? "the SERVER" : "a CLIENT"); + + PR_fprintf(err, + "Transport being used is %s\n", + (6 == protocol) ? "TCP" : "XTP"); + + if (PR_GLOBAL_THREAD == thread_scope) + { + if (1 != concurrency) + { + PR_fprintf(err, " **Concurrency > 1 and GLOBAL threads!?!?\n"); + PR_fprintf(err, " **Ignoring concurrency\n"); + concurrency = 1; + } + } + + if (1 != concurrency) + { + PR_SetConcurrency(concurrency); + PR_fprintf(err, "Concurrency set to %u\n", concurrency); + } + + PR_fprintf(err, + "All threads will be %s\n", + (PR_GLOBAL_THREAD == thread_scope) ? "GLOBAL" : "LOCAL"); + + PR_fprintf(err, "Client buffer size will be %u\n", buffer_size); + + if (-1 != xport_buffer) + PR_fprintf( + err, "Transport send & receive buffer size will be %u\n", xport_buffer); + + + if (NULL == server_name) { + Server(); + } + else { + Client(server_name); + } + + return 0; +} /* main */ + +/* thruput.c */ + diff --git a/nsprpub/pr/tests/time.c b/nsprpub/pr/tests/time.c new file mode 100644 index 0000000000..cdb3ef73a8 --- /dev/null +++ b/nsprpub/pr/tests/time.c @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Program to test different ways to get the time; right now it is tuned + * only for solaris. + * solaris results (100000 iterations): + * time to get time with time(): 4.63 usec avg, 463 msec total + * time to get time with gethrtime(): 2.17 usec avg, 217 msec total + * time to get time with gettimeofday(): 1.25 usec avg, 125 msec total + * + * + */ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "prpriv.h" +#include "prinrval.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + +#define DEFAULT_COUNT 100000 +PRInt32 count; + +time_t itime; +hrtime_t ihrtime; + +void +ftime_init() +{ + itime = time(NULL); + ihrtime = gethrtime(); +} + +time_t +ftime() +{ + hrtime_t now = gethrtime(); + + return itime + ((now - ihrtime) / 1000000000ll); +} + +static void timeTime(void) +{ + PRInt32 index = count; + time_t rv; + + for (; index--;) { + rv = time(NULL); + } +} + +static void timeGethrtime(void) +{ + PRInt32 index = count; + time_t rv; + + for (; index--;) { + rv = ftime(); + } +} + +static void timeGettimeofday(void) +{ + PRInt32 index = count; + time_t rv; + struct timeval tp; + + for (; index--;) { + rv = gettimeofday(&tp, NULL); + } +} + +static void timePRTime32(void) +{ + PRInt32 index = count; + PRInt32 rv32; + PRTime q; + PRTime rv; + + LL_I2L(q, 1000000); + + for (; index--;) { + rv = PR_Now(); + LL_DIV(rv, rv, q); + LL_L2I(rv32, rv); + } +} + +static void timePRTime64(void) +{ + PRInt32 index = count; + PRTime rv; + + for (; index--;) { + rv = PR_Now(); + } +} + +/************************************************************************/ + +static void Measure(void (*func)(void), const char *msg) +{ + PRIntervalTime start, stop; + double d; + PRInt32 tot; + + start = PR_IntervalNow(); + (*func)(); + stop = PR_IntervalNow(); + + d = (double)PR_IntervalToMicroseconds(stop - start); + tot = PR_IntervalToMilliseconds(stop-start); + + if (debug_mode) { + printf("%40s: %6.2f usec avg, %d msec total\n", msg, d / count, tot); + } +} + +int main(int argc, char **argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "d:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + + if (argc > 1) { + count = atoi(argv[1]); + } else { + count = DEFAULT_COUNT; + } + + ftime_init(); + + Measure(timeTime, "time to get time with time()"); + Measure(timeGethrtime, "time to get time with gethrtime()"); + Measure(timeGettimeofday, "time to get time with gettimeofday()"); + Measure(timePRTime32, "time to get time with PR_Time() (32bit)"); + Measure(timePRTime64, "time to get time with PR_Time() (64bit)"); + + PR_Cleanup(); + return 0; +} + + + diff --git a/nsprpub/pr/tests/timemac.c b/nsprpub/pr/tests/timemac.c new file mode 100644 index 0000000000..b0f8b17d8a --- /dev/null +++ b/nsprpub/pr/tests/timemac.c @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * file: timemac.c + * description: test time and date routines on the Mac + */ +#include <stdio.h> +#include "prinit.h" +#include "prtime.h" + + +static char *dayOfWeek[] = +{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" }; +static char *month[] = +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" +}; + +static void printExplodedTime(const PRExplodedTime *et) { + PRInt32 totalOffset; + PRInt32 hourOffset, minOffset; + const char *sign; + + /* Print day of the week, month, day, hour, minute, and second */ + printf( "%s %s %ld %02ld:%02ld:%02ld ", + dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday, + et->tm_hour, et->tm_min, et->tm_sec); + + /* Print time zone */ + totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset; + if (totalOffset == 0) { + printf("UTC "); + } else { + sign = ""; + if (totalOffset < 0) { + totalOffset = -totalOffset; + sign = "-"; + } + hourOffset = totalOffset / 3600; + minOffset = (totalOffset % 3600) / 60; + printf("%s%02ld%02ld ", sign, hourOffset, minOffset); + } + + /* Print year */ + printf("%d", et->tm_year); +} + +int main(int argc, char** argv) +{ + PR_STDIO_INIT(); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + + /* + ************************************************************* + ** + ** Testing PR_Now(), PR_ExplodeTime, and PR_ImplodeTime + ** on the current time + ** + ************************************************************* + */ + + { + PRTime t1, t2; + PRExplodedTime et; + + printf("*********************************************\n"); + printf("** **\n"); + printf("** Testing PR_Now(), PR_ExplodeTime, and **\n"); + printf("** PR_ImplodeTime on the current time **\n"); + printf("** **\n"); + printf("*********************************************\n\n"); + t1 = PR_Now(); + + /* First try converting to UTC */ + + PR_ExplodeTime(t1, PR_GMTParameters, &et); + if (et.tm_params.tp_gmt_offset || et.tm_params.tp_dst_offset) { + printf("ERROR: UTC has nonzero gmt or dst offset.\n"); + return 1; + } + printf("Current UTC is "); + printExplodedTime(&et); + printf("\n"); + + t2 = PR_ImplodeTime(&et); + if (LL_NE(t1, t2)) { + printf("ERROR: Explode and implode are NOT inverse.\n"); + return 1; + } + + /* Next, try converting to local (US Pacific) time */ + + PR_ExplodeTime(t1, PR_LocalTimeParameters, &et); + printf("Current local time is "); + printExplodedTime(&et); + printf("\n"); + printf("GMT offset is %ld, DST offset is %ld\n", + et.tm_params.tp_gmt_offset, et.tm_params.tp_dst_offset); + t2 = PR_ImplodeTime(&et); + if (LL_NE(t1, t2)) { + printf("ERROR: Explode and implode are NOT inverse.\n"); + return 1; + } + } + + printf("Please examine the results\n"); + return 0; +} diff --git a/nsprpub/pr/tests/timetest.c b/nsprpub/pr/tests/timetest.c new file mode 100644 index 0000000000..9f96025d2f --- /dev/null +++ b/nsprpub/pr/tests/timetest.c @@ -0,0 +1,823 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * file: timetest.c + * description: test time and date routines + */ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prtime.h" +#include "prprf.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int failed_already=0; +PRBool debug_mode = PR_FALSE; + +static char *dayOfWeek[] = +{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" }; +static char *month[] = +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" +}; + +static void PrintExplodedTime(const PRExplodedTime *et) { + PRInt32 totalOffset; + PRInt32 hourOffset, minOffset; + const char *sign; + + /* Print day of the week, month, day, hour, minute, and second */ + if (debug_mode) printf("%s %s %ld %02ld:%02ld:%02ld ", + dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday, + et->tm_hour, et->tm_min, et->tm_sec); + + /* Print time zone */ + totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset; + if (totalOffset == 0) { + if (debug_mode) { + printf("UTC "); + } + } else { + sign = "+"; + if (totalOffset < 0) { + totalOffset = -totalOffset; + sign = "-"; + } + hourOffset = totalOffset / 3600; + minOffset = (totalOffset % 3600) / 60; + if (debug_mode) { + printf("%s%02ld%02ld ", sign, hourOffset, minOffset); + } + } + + /* Print year */ + if (debug_mode) { + printf("%hd", et->tm_year); + } +} + +static int ExplodedTimeIsEqual(const PRExplodedTime *et1, + const PRExplodedTime *et2) +{ + if (et1->tm_usec == et2->tm_usec && + et1->tm_sec == et2->tm_sec && + et1->tm_min == et2->tm_min && + et1->tm_hour == et2->tm_hour && + et1->tm_mday == et2->tm_mday && + et1->tm_month == et2->tm_month && + et1->tm_year == et2->tm_year && + et1->tm_wday == et2->tm_wday && + et1->tm_yday == et2->tm_yday && + et1->tm_params.tp_gmt_offset == et2->tm_params.tp_gmt_offset && + et1->tm_params.tp_dst_offset == et2->tm_params.tp_dst_offset) { + return 1; + } else { + return 0; + } +} + +static void +testParseTimeString(PRTime t) +{ + PRExplodedTime et; + PRTime t2; + char timeString[128]; + char buf[128]; + PRInt32 totalOffset; + PRInt32 hourOffset, minOffset; + const char *sign; + PRInt64 usec_per_sec; + + /* Truncate the microsecond part of PRTime */ + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_DIV(t, t, usec_per_sec); + LL_MUL(t, t, usec_per_sec); + + PR_ExplodeTime(t, PR_LocalTimeParameters, &et); + + /* Print day of the week, month, day, hour, minute, and second */ + PR_snprintf(timeString, 128, "%s %s %ld %02ld:%02ld:%02ld ", + dayOfWeek[et.tm_wday], month[et.tm_month], et.tm_mday, + et.tm_hour, et.tm_min, et.tm_sec); + /* Print time zone */ + totalOffset = et.tm_params.tp_gmt_offset + et.tm_params.tp_dst_offset; + if (totalOffset == 0) { + strcat(timeString, "GMT "); /* I wanted to use "UTC" here, but + * PR_ParseTimeString doesn't + * understand "UTC". */ + } else { + sign = "+"; + if (totalOffset < 0) { + totalOffset = -totalOffset; + sign = "-"; + } + hourOffset = totalOffset / 3600; + minOffset = (totalOffset % 3600) / 60; + PR_snprintf(buf, 128, "%s%02ld%02ld ", sign, hourOffset, minOffset); + strcat(timeString, buf); + } + /* Print year */ + PR_snprintf(buf, 128, "%hd", et.tm_year); + strcat(timeString, buf); + + if (PR_ParseTimeString(timeString, PR_FALSE, &t2) == PR_FAILURE) { + fprintf(stderr, "PR_ParseTimeString() failed\n"); + exit(1); + } + if (LL_NE(t, t2)) { + fprintf(stderr, "PR_ParseTimeString() incorrect\n"); + PR_snprintf(buf, 128, "t is %lld, t2 is %lld, time string is %s\n", + t, t2, timeString); + fprintf(stderr, "%s\n", buf); + exit(1); + } +} + +int main(int argc, char** argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt; + + PR_STDIO_INIT(); + opt = PL_CreateOptState(argc, argv, "d"); + 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; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + /* Testing zero PRTime (the epoch) */ + { + PRTime t; + PRExplodedTime et; + + LL_I2L(t, 0); + if (debug_mode) { + printf("The NSPR epoch is:\n"); + } + PR_ExplodeTime(t, PR_LocalTimeParameters, &et); + PrintExplodedTime(&et); + if (debug_mode) { + printf("\n"); + } + PR_ExplodeTime(t, PR_GMTParameters, &et); + PrintExplodedTime(&et); + if (debug_mode) { + printf("\n\n"); + } + testParseTimeString(t); + } + + /* + ************************************************************* + ** + ** Testing PR_Now(), PR_ExplodeTime, and PR_ImplodeTime + ** on the current time + ** + ************************************************************* + */ + + { + PRTime t1, t2; + PRExplodedTime et; + + if (debug_mode) { + printf("*********************************************\n"); + printf("** **\n"); + printf("** Testing PR_Now(), PR_ExplodeTime, and **\n"); + printf("** PR_ImplodeTime on the current time **\n"); + printf("** **\n"); + printf("*********************************************\n\n"); + } + t1 = PR_Now(); + + /* First try converting to UTC */ + + PR_ExplodeTime(t1, PR_GMTParameters, &et); + if (et.tm_params.tp_gmt_offset || et.tm_params.tp_dst_offset) { + if (debug_mode) { + printf("ERROR: UTC has nonzero gmt or dst offset.\n"); + } + else { + failed_already=1; + } + return 1; + } + if (debug_mode) { + printf("Current UTC is "); + } + PrintExplodedTime(&et); + if (debug_mode) { + printf("\n"); + } + + t2 = PR_ImplodeTime(&et); + if (LL_NE(t1, t2)) { + if (debug_mode) { + printf("ERROR: Explode and implode are NOT inverse.\n"); + } + else { + printf("FAIL\n"); + } + return 1; + } + + /* Next, try converting to local (US Pacific) time */ + + PR_ExplodeTime(t1, PR_LocalTimeParameters, &et); + if (debug_mode) { + printf("Current local time is "); + } + PrintExplodedTime(&et); + if (debug_mode) { + printf("\n"); + } + if (debug_mode) printf("GMT offset is %ld, DST offset is %ld\n", + et.tm_params.tp_gmt_offset, et.tm_params.tp_dst_offset); + t2 = PR_ImplodeTime(&et); + if (LL_NE(t1, t2)) { + if (debug_mode) { + printf("ERROR: Explode and implode are NOT inverse.\n"); + } + return 1; + } + + if (debug_mode) { + printf("Please examine the results\n"); + } + testParseTimeString(t1); + } + + + /* + ******************************************* + ** + ** Testing PR_NormalizeTime() + ** + ******************************************* + */ + + /* July 4, 2001 is Wednesday */ + { + PRExplodedTime et; + + if (debug_mode) { + printf("\n"); + printf("**********************************\n"); + printf("** **\n"); + printf("** Testing PR_NormalizeTime() **\n"); + printf("** **\n"); + printf("**********************************\n\n"); + } + et.tm_year = 2001; + et.tm_month = 7 - 1; + et.tm_mday = 4; + et.tm_hour = 0; + et.tm_min = 0; + et.tm_sec = 0; + et.tm_usec = 0; + et.tm_params = PR_GMTParameters(&et); + + PR_NormalizeTime(&et, PR_GMTParameters); + + if (debug_mode) { + printf("July 4, 2001 is %s.\n", dayOfWeek[et.tm_wday]); + } + if (et.tm_wday == 3) { + if (debug_mode) { + printf("PASS\n"); + } + } else { + if (debug_mode) { + printf("ERROR: It should be Wednesday\n"); + } + else { + failed_already=1; + } + return 1; + } + testParseTimeString(PR_ImplodeTime(&et)); + + /* June 12, 1997 23:00 PST == June 13, 1997 00:00 PDT */ + et.tm_year = 1997; + et.tm_month = 6 - 1; + et.tm_mday = 12; + et.tm_hour = 23; + et.tm_min = 0; + et.tm_sec = 0; + et.tm_usec = 0; + et.tm_params.tp_gmt_offset = -8 * 3600; + et.tm_params.tp_dst_offset = 0; + + PR_NormalizeTime(&et, PR_USPacificTimeParameters); + + if (debug_mode) { + printf("Thu Jun 12, 1997 23:00:00 PST is "); + } + PrintExplodedTime(&et); + if (debug_mode) { + printf(".\n"); + } + if (et.tm_wday == 5) { + if (debug_mode) { + printf("PASS\n"); + } + } else { + if (debug_mode) { + printf("ERROR: It should be Friday\n"); + } + else { + failed_already=1; + } + return 1; + } + testParseTimeString(PR_ImplodeTime(&et)); + + /* Feb 14, 1997 00:00:00 PDT == Feb 13, 1997 23:00:00 PST */ + et.tm_year = 1997; + et.tm_month = 2 - 1; + et.tm_mday = 14; + et.tm_hour = 0; + et.tm_min = 0; + et.tm_sec = 0; + et.tm_usec = 0; + et.tm_params.tp_gmt_offset = -8 * 3600; + et.tm_params.tp_dst_offset = 3600; + + PR_NormalizeTime(&et, PR_USPacificTimeParameters); + + if (debug_mode) { + printf("Fri Feb 14, 1997 00:00:00 PDT is "); + } + PrintExplodedTime(&et); + if (debug_mode) { + printf(".\n"); + } + if (et.tm_wday == 4) { + if (debug_mode) { + printf("PASS\n"); + } + } else { + if (debug_mode) { + printf("ERROR: It should be Thursday\n"); + } + else { + failed_already=1; + } + return 1; + } + testParseTimeString(PR_ImplodeTime(&et)); + + /* What time is Nov. 7, 1996, 18:29:23 PDT? */ + et.tm_year = 1996; + et.tm_month = 11 - 1; + et.tm_mday = 7; + et.tm_hour = 18; + et.tm_min = 29; + et.tm_sec = 23; + et.tm_usec = 0; + et.tm_params.tp_gmt_offset = -8 * 3600; /* PDT */ + et.tm_params.tp_dst_offset = 3600; + + PR_NormalizeTime(&et, PR_LocalTimeParameters); + if (debug_mode) { + printf("Nov 7 18:29:23 PDT 1996 is "); + } + PrintExplodedTime(&et); + if (debug_mode) { + printf(".\n"); + } + testParseTimeString(PR_ImplodeTime(&et)); + + /* What time is Oct. 7, 1995, 18:29:23 PST? */ + et.tm_year = 1995; + et.tm_month = 10 - 1; + et.tm_mday = 7; + et.tm_hour = 18; + et.tm_min = 29; + et.tm_sec = 23; + et.tm_params.tp_gmt_offset = -8 * 3600; /* PST */ + et.tm_params.tp_dst_offset = 0; + + PR_NormalizeTime(&et, PR_LocalTimeParameters); + if (debug_mode) { + printf("Oct 7 18:29:23 PST 1995 is "); + } + PrintExplodedTime(&et); + if (debug_mode) { + printf(".\n"); + } + testParseTimeString(PR_ImplodeTime(&et)); + + if (debug_mode) { + printf("Please examine the results\n"); + } + } + + /* + ************************************************************** + ** + ** Testing range of years + ** + ************************************************************** + */ + + { + PRExplodedTime et1, et2; + PRTime ttt; + PRTime secs; + + if (debug_mode) { + printf("\n"); + printf("***************************************\n"); + printf("** **\n"); + printf("** Testing range of years **\n"); + printf("** **\n"); + printf("***************************************\n\n"); + } + /* April 4, 1917 GMT */ + et1.tm_usec = 0; + et1.tm_sec = 0; + et1.tm_min = 0; + et1.tm_hour = 0; + et1.tm_mday = 4; + et1.tm_month = 4 - 1; + et1.tm_year = 1917; + et1.tm_params = PR_GMTParameters(&et1); + PR_NormalizeTime(&et1, PR_LocalTimeParameters); + secs = PR_ImplodeTime(&et1); + if (LL_GE_ZERO(secs)) { + if (debug_mode) { + printf("ERROR: April 4, 1917 GMT returns a nonnegative second count\n"); + } + failed_already = 1; + return 1; + } + PR_ExplodeTime(secs, PR_LocalTimeParameters, &et2); + if (!ExplodedTimeIsEqual(&et1, &et2)) { + if (debug_mode) { + printf("ERROR: PR_ImplodeTime and PR_ExplodeTime are not inverse for April 4, 1917 GMT\n"); + } + failed_already=1; + return 1; + } + ttt = PR_ImplodeTime(&et1); + testParseTimeString( ttt ); + + if (debug_mode) { + printf("Test passed for April 4, 1917\n"); + } + + /* July 4, 2050 */ + et1.tm_usec = 0; + et1.tm_sec = 0; + et1.tm_min = 0; + et1.tm_hour = 0; + et1.tm_mday = 4; + et1.tm_month = 7 - 1; + et1.tm_year = 2050; + et1.tm_params = PR_GMTParameters(&et1); + PR_NormalizeTime(&et1, PR_LocalTimeParameters); + secs = PR_ImplodeTime(&et1); + if (!LL_GE_ZERO(secs)) { + if (debug_mode) { + printf("ERROR: July 4, 2050 GMT returns a negative second count\n"); + } + failed_already = 1; + return 1; + } + PR_ExplodeTime(secs, PR_LocalTimeParameters, &et2); + if (!ExplodedTimeIsEqual(&et1, &et2)) { + if (debug_mode) { + printf("ERROR: PR_ImplodeTime and PR_ExplodeTime are not inverse for July 4, 2050 GMT\n"); + } + failed_already=1; + return 1; + } + testParseTimeString(PR_ImplodeTime(&et1)); + + if (debug_mode) { + printf("Test passed for July 4, 2050\n"); + } + + } + + /* + ************************************************************** + ** + ** Stress test + * + ** Go through four years, starting from + ** 00:00:00 PST Jan. 1, 2005, incrementing + ** every 10 minutes. + ** + ************************************************************** + */ + + { + PRExplodedTime et, et1, et2; + PRInt64 usecPer10Min; + int day, hour, min; + PRTime usecs; + int dstInEffect = 0; + + if (debug_mode) { + printf("\n"); + printf("*******************************************************\n"); + printf("** **\n"); + printf("** Stress test Pacific Time **\n"); + printf("** Starting from midnight Jan. 1, 2005 PST, **\n"); + printf("** going through four years in 10-minute increment **\n"); + printf("** **\n"); + printf("*******************************************************\n\n"); + } + LL_I2L(usecPer10Min, 600000000L); + + /* 00:00:00 PST Jan. 1, 2005 */ + et.tm_usec = 0; + et.tm_sec = 0; + et.tm_min = 0; + et.tm_hour = 0; + et.tm_mday = 1; + et.tm_month = 0; + et.tm_year = 2005; + et.tm_params.tp_gmt_offset = -8 * 3600; + et.tm_params.tp_dst_offset = 0; + usecs = PR_ImplodeTime(&et); + + for (day = 0; day < 4 * 365 + 1; day++) { + for (hour = 0; hour < 24; hour++) { + for (min = 0; min < 60; min += 10) { + LL_ADD(usecs, usecs, usecPer10Min); + PR_ExplodeTime(usecs, PR_USPacificTimeParameters, &et1); + + et2 = et; + et2.tm_usec += 600000000L; + PR_NormalizeTime(&et2, PR_USPacificTimeParameters); + + if (!ExplodedTimeIsEqual(&et1, &et2)) { + printf("ERROR: componentwise comparison failed\n"); + PrintExplodedTime(&et1); + printf("\n"); + PrintExplodedTime(&et2); + printf("\n"); + failed_already=1; + return 1; + } + + if (LL_NE(usecs, PR_ImplodeTime(&et1))) { + printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n"); + PrintExplodedTime(&et1); + printf("\n"); + failed_already=1; + return 1; + } + testParseTimeString(usecs); + + if (!dstInEffect && et1.tm_params.tp_dst_offset) { + dstInEffect = 1; + if (debug_mode) { + printf("DST changeover from "); + PrintExplodedTime(&et); + printf(" to "); + PrintExplodedTime(&et1); + printf(".\n"); + } + } else if (dstInEffect && !et1.tm_params.tp_dst_offset) { + dstInEffect = 0; + if (debug_mode) { + printf("DST changeover from "); + PrintExplodedTime(&et); + printf(" to "); + PrintExplodedTime(&et1); + printf(".\n"); + } + } + + et = et1; + } + } + } + if (debug_mode) { + printf("Test passed\n"); + } + } + + + /* Same stress test, but with PR_LocalTimeParameters */ + + { + PRExplodedTime et, et1, et2; + PRInt64 usecPer10Min; + int day, hour, min; + PRTime usecs; + int dstInEffect = 0; + + if (debug_mode) { + printf("\n"); + printf("*******************************************************\n"); + printf("** **\n"); + printf("** Stress test Local Time **\n"); + printf("** Starting from midnight Jan. 1, 2005 PST, **\n"); + printf("** going through four years in 10-minute increment **\n"); + printf("** **\n"); + printf("*******************************************************\n\n"); + } + + LL_I2L(usecPer10Min, 600000000L); + + /* 00:00:00 PST Jan. 1, 2005 */ + et.tm_usec = 0; + et.tm_sec = 0; + et.tm_min = 0; + et.tm_hour = 0; + et.tm_mday = 1; + et.tm_month = 0; + et.tm_year = 2005; + et.tm_params.tp_gmt_offset = -8 * 3600; + et.tm_params.tp_dst_offset = 0; + usecs = PR_ImplodeTime(&et); + + for (day = 0; day < 4 * 365 + 1; day++) { + for (hour = 0; hour < 24; hour++) { + for (min = 0; min < 60; min += 10) { + LL_ADD(usecs, usecs, usecPer10Min); + PR_ExplodeTime(usecs, PR_LocalTimeParameters, &et1); + + et2 = et; + et2.tm_usec += 600000000L; + PR_NormalizeTime(&et2, PR_LocalTimeParameters); + + if (!ExplodedTimeIsEqual(&et1, &et2)) { + printf("ERROR: componentwise comparison failed\n"); + PrintExplodedTime(&et1); + printf("\n"); + PrintExplodedTime(&et2); + printf("\n"); + return 1; + } + + if (LL_NE(usecs, PR_ImplodeTime(&et1))) { + printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n"); + PrintExplodedTime(&et1); + printf("\n"); + failed_already=1; + return 1; + } + testParseTimeString(usecs); + + if (!dstInEffect && et1.tm_params.tp_dst_offset) { + dstInEffect = 1; + if (debug_mode) { + printf("DST changeover from "); + PrintExplodedTime(&et); + printf(" to "); + PrintExplodedTime(&et1); + printf(".\n"); + } + } else if (dstInEffect && !et1.tm_params.tp_dst_offset) { + dstInEffect = 0; + if (debug_mode) { + printf("DST changeover from "); + PrintExplodedTime(&et); + printf(" to "); + PrintExplodedTime(&et1); + printf(".\n"); + } + } + + et = et1; + } + } + } + if (debug_mode) { + printf("Test passed\n"); + } + } + + /* Same stress test, but with PR_LocalTimeParameters and going backward */ + + { + PRExplodedTime et, et1, et2; + PRInt64 usecPer10Min; + int day, hour, min; + PRTime usecs; + int dstInEffect = 0; + + if (debug_mode) { + printf("\n"); + printf("*******************************************************\n"); + printf("** **\n"); + printf("** Stress test Local Time **\n"); + printf("** Starting from midnight Jan. 1, 2009 PST, **\n"); + printf("** going back four years in 10-minute increment **\n"); + printf("** **\n"); + printf("*******************************************************\n\n"); + } + + LL_I2L(usecPer10Min, 600000000L); + + /* 00:00:00 PST Jan. 1, 2009 */ + et.tm_usec = 0; + et.tm_sec = 0; + et.tm_min = 0; + et.tm_hour = 0; + et.tm_mday = 1; + et.tm_month = 0; + et.tm_year = 2009; + et.tm_params.tp_gmt_offset = -8 * 3600; + et.tm_params.tp_dst_offset = 0; + usecs = PR_ImplodeTime(&et); + + for (day = 0; day < 4 * 365 + 1; day++) { + for (hour = 0; hour < 24; hour++) { + for (min = 0; min < 60; min += 10) { + LL_SUB(usecs, usecs, usecPer10Min); + PR_ExplodeTime(usecs, PR_LocalTimeParameters, &et1); + + et2 = et; + et2.tm_usec -= 600000000L; + PR_NormalizeTime(&et2, PR_LocalTimeParameters); + + if (!ExplodedTimeIsEqual(&et1, &et2)) { + printf("ERROR: componentwise comparison failed\n"); + PrintExplodedTime(&et1); + printf("\n"); + PrintExplodedTime(&et2); + printf("\n"); + return 1; + } + + if (LL_NE(usecs, PR_ImplodeTime(&et1))) { + printf("ERROR: PR_ExplodeTime and PR_ImplodeTime are not inverse\n"); + PrintExplodedTime(&et1); + printf("\n"); + failed_already=1; + return 1; + } + testParseTimeString(usecs); + + if (!dstInEffect && et1.tm_params.tp_dst_offset) { + dstInEffect = 1; + if (debug_mode) { + printf("DST changeover from "); + PrintExplodedTime(&et); + printf(" to "); + PrintExplodedTime(&et1); + printf(".\n"); + } + } else if (dstInEffect && !et1.tm_params.tp_dst_offset) { + dstInEffect = 0; + if (debug_mode) { + printf("DST changeover from "); + PrintExplodedTime(&et); + printf(" to "); + PrintExplodedTime(&et1); + printf(".\n"); + } + } + + et = et1; + } + } + } + } + + if (failed_already) { + return 1; + } + else { + return 0; + } + +} diff --git a/nsprpub/pr/tests/tmoacc.c b/nsprpub/pr/tests/tmoacc.c new file mode 100644 index 0000000000..d7e64cbea6 --- /dev/null +++ b/nsprpub/pr/tests/tmoacc.c @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +#include <stdlib.h> +#include <string.h> + +#include "plerror.h" +#include "plgetopt.h" + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 +#define DEFAULT_THREADS 1 +#define DEFAULT_BACKLOG 10 +#define DEFAULT_TIMEOUT 10 +#define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */ + +typedef enum {running, stopped} Status; + +typedef struct Shared +{ + PRLock *ml; + PRCondVar *cv; + PRBool passed; + PRBool random; + PRFileDesc *debug; + PRIntervalTime timeout; + PRFileDesc *listenSock; + Status status; +} Shared; + +static PRIntervalTime Timeout(const Shared *shared) +{ + PRIntervalTime timeout = shared->timeout; + if (shared->random) + { + PRIntervalTime half = timeout >> 1; /* one half of the interval */ + PRIntervalTime quarter = half >> 1; /* one quarter of the interval */ + /* something in [0..timeout / 2) */ + PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE; + timeout = (3 * quarter) + random; /* [75..125)% */ + } + return timeout; +} /* Timeout */ + +static void Accept(void *arg) +{ + PRStatus rv; + char *buffer = NULL; + PRNetAddr clientAddr; + Shared *shared = (Shared*)arg; + PRInt32 recv_length = 0, flags = 0; + PRFileDesc *clientSock; + PRIntn toread, byte, bytes, loop = 0; + struct Descriptor { + PRInt32 length; + PRUint32 checksum; + } descriptor; + + do + { + PRUint32 checksum = 0; + if (NULL != shared->debug) { + PR_fprintf(shared->debug, "[%d]accepting ... ", loop++); + } + clientSock = PR_Accept( + shared->listenSock, &clientAddr, Timeout(shared)); + if (clientSock != NULL) + { + if (NULL != shared->debug) { + PR_fprintf(shared->debug, "reading length ... "); + } + bytes = PR_Recv( + clientSock, &descriptor, sizeof(descriptor), + flags, Timeout(shared)); + if (sizeof(descriptor) == bytes) + { + /* and, before doing something stupid ... */ + descriptor.length = PR_ntohl(descriptor.length); + descriptor.checksum = PR_ntohl(descriptor.checksum); + if (NULL != shared->debug) { + PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length); + } + toread = descriptor.length; + if (recv_length < descriptor.length) + { + if (NULL != buffer) { + PR_DELETE(buffer); + } + buffer = (char*)PR_MALLOC(descriptor.length); + recv_length = descriptor.length; + } + for (toread = descriptor.length; toread > 0; toread -= bytes) + { + bytes = PR_Recv( + clientSock, &buffer[descriptor.length - toread], + toread, flags, Timeout(shared)); + if (-1 == bytes) + { + if (NULL != shared->debug) { + PR_fprintf(shared->debug, "read data failed..."); + } + bytes = 0; + } + } + } + else if (NULL != shared->debug) + { + PR_fprintf(shared->debug, "read desciptor failed..."); + descriptor.length = -1; + } + if (NULL != shared->debug) { + PR_fprintf(shared->debug, "closing"); + } + rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); + if ((PR_FAILURE == rv) && (NULL != shared->debug)) + { + PR_fprintf(shared->debug, " failed"); + shared->passed = PR_FALSE; + } + rv = PR_Close(clientSock); + if (PR_FAILURE == rv) if (NULL != shared->debug) + { + PR_fprintf(shared->debug, " failed"); + shared->passed = PR_FALSE; + } + if (descriptor.length > 0) + { + for (byte = 0; byte < descriptor.length; ++byte) + { + PRUint32 overflow = checksum & 0x80000000; + checksum = (checksum << 1); + if (0x00000000 != overflow) { + checksum += 1; + } + checksum += buffer[byte]; + } + if ((descriptor.checksum != checksum) && (NULL != shared->debug)) + { + PR_fprintf(shared->debug, " ... data mismatch"); + shared->passed = PR_FALSE; + } + } + else if (0 == descriptor.length) + { + PR_Lock(shared->ml); + shared->status = stopped; + PR_NotifyCondVar(shared->cv); + PR_Unlock(shared->ml); + } + if (NULL != shared->debug) { + PR_fprintf(shared->debug, "\n"); + } + } + else + { + if (PR_PENDING_INTERRUPT_ERROR != PR_GetError()) + { + if (NULL != shared->debug) { + PL_PrintError("Accept"); + } + shared->passed = PR_FALSE; + } + } + } while (running == shared->status); + if (NULL != buffer) { + PR_DELETE(buffer); + } +} /* Accept */ + +PRIntn Tmoacc(PRIntn argc, char **argv) +{ + PRStatus rv; + PRIntn exitStatus; + PRIntn index; + Shared *shared; + PLOptStatus os; + PRThread **thread; + PRNetAddr listenAddr; + PRSocketOptionData sockOpt; + PRIntn timeout = DEFAULT_TIMEOUT; + PRIntn threads = DEFAULT_THREADS; + PRIntn backlog = DEFAULT_BACKLOG; + PRThreadScope thread_scope = PR_LOCAL_THREAD; + + PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R"); + + shared = PR_NEWZAP(Shared); + + shared->debug = NULL; + shared->passed = PR_TRUE; + shared->random = PR_TRUE; + shared->status = running; + shared->ml = PR_NewLock(); + shared->cv = PR_NewCondVar(shared->ml); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + shared->debug = PR_GetSpecialFD(PR_StandardError); + break; + case 'G': /* use global threads */ + thread_scope = PR_GLOBAL_THREAD; + break; + case 'b': /* size of listen backlog */ + backlog = atoi(opt->value); + break; + case 't': /* number of threads doing accept */ + threads = atoi(opt->value); + break; + case 'T': /* timeout used for network operations */ + timeout = atoi(opt->value); + break; + case 'R': /* randomize the timeout values */ + shared->random = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + if (0 == threads) { + threads = DEFAULT_THREADS; + } + if (0 == backlog) { + backlog = DEFAULT_BACKLOG; + } + 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); + + shared->timeout = PR_SecondsToInterval(timeout); + + /* First bind to the socket */ + shared->listenSock = PR_NewTCPSocket(); + if (shared->listenSock) + { + sockOpt.option = PR_SockOpt_Reuseaddr; + sockOpt.value.reuse_addr = PR_TRUE; + rv = PR_SetSocketOption(shared->listenSock, &sockOpt); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_Bind(shared->listenSock, &listenAddr); + if (rv != PR_FAILURE) + { + rv = PR_Listen(shared->listenSock, threads + backlog); + if (PR_SUCCESS == rv) + { + thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); + for (index = 0; index < threads; ++index) + { + thread[index] = PR_CreateThread( + PR_USER_THREAD, Accept, shared, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 0); + PR_ASSERT(NULL != thread[index]); + } + + PR_Lock(shared->ml); + while (shared->status == running) { + PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(shared->ml); + for (index = 0; index < threads; ++index) + { + rv = PR_Interrupt(thread[index]); + PR_ASSERT(PR_SUCCESS== rv); + rv = PR_JoinThread(thread[index]); + PR_ASSERT(PR_SUCCESS== rv); + } + PR_DELETE(thread); + } + else + { + if (shared->debug) { + PL_PrintError("Listen"); + } + shared->passed = PR_FALSE; + } + } + else + { + if (shared->debug) { + PL_PrintError("Bind"); + } + shared->passed = PR_FALSE; + } + + PR_Close(shared->listenSock); + } + else + { + if (shared->debug) { + PL_PrintError("Create"); + } + shared->passed = PR_FALSE; + } + + PR_DestroyCondVar(shared->cv); + PR_DestroyLock(shared->ml); + + PR_fprintf( + PR_GetSpecialFD(PR_StandardError), "%s\n", + ((shared->passed) ? "PASSED" : "FAILED")); + + exitStatus = (shared->passed) ? 0 : 1; + PR_DELETE(shared); + return exitStatus; +} + +int main(int argc, char **argv) +{ + return (PR_VersionCheck(PR_VERSION)) ? + PR_Initialize(Tmoacc, argc, argv, 4) : -1; +} /* main */ + +/* tmoacc */ diff --git a/nsprpub/pr/tests/tmocon.c b/nsprpub/pr/tests/tmocon.c new file mode 100644 index 0000000000..3d992c3443 --- /dev/null +++ b/nsprpub/pr/tests/tmocon.c @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*********************************************************************** +** +** Name: tmocon.c +** +** Description: test client socket connection. +** +** Modification History: +** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag. +** The debug mode will print all of the printfs associated with this test. +** The regress mode will be the default mode. Since the regress tool limits +** the output to a one line status:PASS or FAIL,all of the printf statements +** have been handled with an if (debug_mode) statement. +***********************************************************************/ + +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "nspr.h" +#include "pprio.h" + +#include "plerror.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* for getcwd */ +#if defined(XP_UNIX) || defined (XP_OS2) +#include <unistd.h> +#elif defined(XP_PC) +#include <direct.h> +#endif + +#ifdef WINCE +#include <windows.h> +char *getcwd(char *buf, size_t size) +{ + wchar_t wpath[MAX_PATH]; + _wgetcwd(wpath, MAX_PATH); + WideCharToMultiByte(CP_ACP, 0, wpath, -1, buf, size, 0, 0); +} +#endif + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 + +#define DEFAULT_DALLY 1 +#define DEFAULT_THREADS 1 +#define DEFAULT_TIMEOUT 10 +#define DEFAULT_MESSAGES 100 +#define DEFAULT_MESSAGESIZE 100 + +static PRFileDesc *debug_out = NULL; + +typedef struct Shared +{ + PRBool random; + PRBool failed; + PRBool intermittant; + PRIntn debug; + PRInt32 messages; + PRIntervalTime dally; + PRIntervalTime timeout; + PRInt32 message_length; + PRNetAddr serverAddress; +} Shared; + +static PRIntervalTime Timeout(const Shared *shared) +{ + PRIntervalTime timeout = shared->timeout; + if (shared->random) + { + PRIntervalTime quarter = timeout >> 2; /* one quarter of the interval */ + PRUint32 random = rand() % quarter; /* something in[0..timeout / 4) */ + timeout = (((3 * quarter) + random) >> 2) + quarter; /* [75..125)% */ + } + return timeout; +} /* Timeout */ + +static void CauseTimeout(const Shared *shared) +{ + if (shared->intermittant) { + PR_Sleep(Timeout(shared)); + } +} /* CauseTimeout */ + +static PRStatus MakeReceiver(Shared *shared) +{ + PRStatus rv = PR_FAILURE; + if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback)) + { + char *argv[3]; + char path[1024 + sizeof("/tmoacc")]; + + getcwd(path, sizeof(path)); + + (void)strcat(path, "/tmoacc"); +#ifdef XP_PC + (void)strcat(path, ".exe"); +#endif + argv[0] = path; + if (shared->debug > 0) + { + argv[1] = "-d"; + argv[2] = NULL; + } + else { + argv[1] = NULL; + } + if (shared->debug > 1) { + PR_fprintf(debug_out, " creating accept process %s ...", path); + } + fflush(stdout); + rv = PR_CreateProcessDetached(path, argv, NULL, NULL); + if (PR_SUCCESS == rv) + { + if (shared->debug > 1) { + PR_fprintf(debug_out, " wait 5 seconds"); + } + if (shared->debug > 1) { + PR_fprintf(debug_out, " before connecting to accept process ..."); + } + fflush(stdout); + PR_Sleep(PR_SecondsToInterval(5)); + return rv; + } + shared->failed = PR_TRUE; + if (shared->debug > 0) { + PL_FPrintError(debug_out, "PR_CreateProcessDetached failed"); + } + } + return rv; +} /* MakeReceiver */ + +static void Connect(void *arg) +{ + PRStatus rv; + char *buffer = NULL; + PRFileDesc *clientSock; + Shared *shared = (Shared*)arg; + PRInt32 loop, bytes, flags = 0; + struct Descriptor { + PRInt32 length; + PRUint32 checksum; + } descriptor; + debug_out = (0 == shared->debug) ? NULL : PR_GetSpecialFD(PR_StandardError); + + buffer = (char*)PR_MALLOC(shared->message_length); + + for (bytes = 0; bytes < shared->message_length; ++bytes) { + buffer[bytes] = (char)bytes; + } + + descriptor.checksum = 0; + for (bytes = 0; bytes < shared->message_length; ++bytes) + { + PRUint32 overflow = descriptor.checksum & 0x80000000; + descriptor.checksum = (descriptor.checksum << 1); + if (0x00000000 != overflow) { + descriptor.checksum += 1; + } + descriptor.checksum += buffer[bytes]; + } + descriptor.checksum = PR_htonl(descriptor.checksum); + + for (loop = 0; loop < shared->messages; ++loop) + { + if (shared->debug > 1) { + PR_fprintf(debug_out, "[%d]socket ... ", loop); + } + clientSock = PR_NewTCPSocket(); + if (clientSock) + { + /* + * We need to slow down the rate of generating connect requests, + * otherwise the listen backlog queue on the accept side may + * become full and we will get connection refused or timeout + * error. + */ + + PR_Sleep(shared->dally); + 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) + { + PRInt32 descriptor_length = (loop < (shared->messages - 1)) ? + shared->message_length : 0; + descriptor.length = PR_htonl(descriptor_length); + if (shared->debug > 1) + PR_fprintf( + debug_out, "sending %d bytes ... ", descriptor_length); + CauseTimeout(shared); /* might cause server to timeout */ + bytes = PR_Send( + clientSock, &descriptor, sizeof(descriptor), + flags, Timeout(shared)); + if (bytes != sizeof(descriptor)) + { + shared->failed = PR_TRUE; + if (shared->debug > 0) { + PL_FPrintError(debug_out, "PR_Send failed"); + } + } + if (0 != descriptor_length) + { + CauseTimeout(shared); + bytes = PR_Send( + clientSock, buffer, descriptor_length, + flags, Timeout(shared)); + if (bytes != descriptor_length) + { + shared->failed = PR_TRUE; + if (shared->debug > 0) { + PL_FPrintError(debug_out, "PR_Send failed"); + } + } + } + if (shared->debug > 1) { + PR_fprintf(debug_out, "closing ... "); + } + rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); + rv = PR_Close(clientSock); + if (shared->debug > 1) + { + if (PR_SUCCESS == rv) { + PR_fprintf(debug_out, "\n"); + } + else { + PL_FPrintError(debug_out, "shutdown failed"); + } + } + } + else + { + if (shared->debug > 1) { + PL_FPrintError(debug_out, "connect failed"); + } + PR_Close(clientSock); + if ((loop == 0) && (PR_GetError() == PR_CONNECT_REFUSED_ERROR)) + { + if (MakeReceiver(shared) == PR_FAILURE) { + break; + } + } + else + { + if (shared->debug > 1) { + PR_fprintf(debug_out, " exiting\n"); + } + break; + } + } + } + else + { + shared->failed = PR_TRUE; + if (shared->debug > 0) { + PL_FPrintError(debug_out, "create socket"); + } + break; + } + } + + PR_DELETE(buffer); +} /* Connect */ + +int Tmocon(int argc, char **argv) +{ + /* + * USAGE + * -d turn on debugging output (default = off) + * -v turn on verbose output (default = off) + * -h <n> dns name of host serving the connection (default = self) + * -i dally intermittantly to cause timeouts (default = off) + * -m <n> number of messages to send (default = 100) + * -s <n> size of each message (default = 100) + * -t <n> number of threads sending (default = 1) + * -G use global threads (default = local) + * -T <n> timeout on I/O operations (seconds) (default = 10) + * -D <n> dally between connect requests (seconds)(default = 0) + * -R randomize the dally types around 'T' (default = no) + */ + + PRStatus rv; + int exitStatus; + PLOptStatus os; + Shared *shared = NULL; + PRThread **thread = NULL; + PRIntn index, threads = DEFAULT_THREADS; + PRThreadScope thread_scope = PR_LOCAL_THREAD; + PRInt32 dally = DEFAULT_DALLY, timeout = DEFAULT_TIMEOUT; + PLOptState *opt = PL_CreateOptState(argc, argv, "divGRh:m:s:t:T:D:"); + + shared = PR_NEWZAP(Shared); + + shared->debug = 0; + shared->failed = PR_FALSE; + shared->random = PR_FALSE; + shared->messages = DEFAULT_MESSAGES; + 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); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': + if (0 == shared->debug) { + shared->debug = 1; + } + break; + case 'v': + if (0 == shared->debug) { + shared->debug = 2; + } + break; + case 'i': + shared->intermittant = PR_TRUE; + break; + case 'R': + shared->random = PR_TRUE; + break; + case 'G': + thread_scope = PR_GLOBAL_THREAD; + break; + case 'h': /* the value for backlock */ + { + 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 */ + shared->messages = atoi(opt->value); + break; + case 't': /* number of threads sending */ + threads = atoi(opt->value); + break; + case 'D': /* dally time between transmissions */ + dally = atoi(opt->value); + break; + case 'T': /* timeout on I/O operations */ + timeout = atoi(opt->value); + break; + case 's': /* total size of each message */ + shared->message_length = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + if (0 == timeout) { + timeout = DEFAULT_TIMEOUT; + } + if (0 == threads) { + threads = DEFAULT_THREADS; + } + if (0 == shared->messages) { + shared->messages = DEFAULT_MESSAGES; + } + if (0 == shared->message_length) { + shared->message_length = DEFAULT_MESSAGESIZE; + } + + shared->dally = PR_SecondsToInterval(dally); + shared->timeout = PR_SecondsToInterval(timeout); + + thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); + + for (index = 0; index < threads; ++index) + thread[index] = PR_CreateThread( + PR_USER_THREAD, Connect, shared, + PR_PRIORITY_NORMAL, thread_scope, + PR_JOINABLE_THREAD, 0); + for (index = 0; index < threads; ++index) { + rv = PR_JoinThread(thread[index]); + } + + PR_DELETE(thread); + + PR_fprintf( + PR_GetSpecialFD(PR_StandardError), "%s\n", + ((shared->failed) ? "FAILED" : "PASSED")); + exitStatus = (shared->failed) ? 1 : 0; + PR_DELETE(shared); + return exitStatus; +} + +int main(int argc, char **argv) +{ + return (PR_VersionCheck(PR_VERSION)) ? + PR_Initialize(Tmocon, argc, argv, 4) : -1; +} /* main */ + +/* tmocon.c */ + + diff --git a/nsprpub/pr/tests/tpd.c b/nsprpub/pr/tests/tpd.c new file mode 100644 index 0000000000..cac8c59aaa --- /dev/null +++ b/nsprpub/pr/tests/tpd.c @@ -0,0 +1,308 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** File: tpd.c +** Description: Exercising the thread private data bailywick. +*/ + +#include "prmem.h" +#include "prinit.h" +#include "prlog.h" +#include "prprf.h" +#include "prthread.h" +#include "prtypes.h" + +#include "private/pprio.h" + +#include "plgetopt.h" + +static PRUintn key[128]; +static PRIntn debug = 0; +static PRBool failed = PR_FALSE; +static PRBool should = PR_TRUE; +static PRBool did = PR_TRUE; +static PRFileDesc *fout = NULL; + +static void PrintProgress(PRIntn line) +{ + failed = failed || (should && !did); + failed = failed || (!should && did); + if (debug > 0) + { +#if defined(WIN16) + printf( + "@ line %d destructor should%s have been called and was%s\n", + line, ((should) ? "" : " NOT"), ((did) ? "" : " NOT")); +#else + PR_fprintf( + fout, "@ line %d destructor should%s have been called and was%s\n", + line, ((should) ? "" : " NOT"), ((did) ? "" : " NOT")); +#endif + } +} /* PrintProgress */ + +static void MyAssert(const char *expr, const char *file, PRIntn line) +{ + if (debug > 0) { + (void)PR_fprintf(fout, "'%s' in file: %s: %d\n", expr, file, line); + } +} /* MyAssert */ + +#define MY_ASSERT(_expr) \ + ((_expr)?((void)0):MyAssert(# _expr,__FILE__,__LINE__)) + + +static void PR_CALLBACK Destructor(void *data) +{ + MY_ASSERT(NULL != data); + if (should) { + did = PR_TRUE; + } + else { + failed = PR_TRUE; + } + /* + * We don't actually free the storage since it's actually allocated + * on the stack. Normally, this would not be the case and this is + * the opportunity to free whatever. + PR_Free(data); + */ +} /* Destructor */ + +static void PR_CALLBACK Thread(void *null) +{ + void *pd; + PRStatus rv; + PRUintn keys; + char *key_string[] = { + "Key #0", "Key #1", "Key #2", "Key #3", + "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8" + }; + + did = should = PR_FALSE; + for (keys = 0; keys < 8; ++keys) + { + pd = PR_GetThreadPrivate(key[keys]); + MY_ASSERT(NULL == pd); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 4; keys < 8; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_FAILURE == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], "EXTENSION"); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 8; keys < 127; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + + /* put in keys and leave them there for thread exit */ + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + did = PR_FALSE; should = PR_TRUE; + +} /* Thread */ + +static PRIntn PR_CALLBACK Tpd(PRIntn argc, char **argv) +{ + void *pd; + PRStatus rv; + PRUintn keys; + PRThread *thread; + char *key_string[] = { + "Key #0", "Key #1", "Key #2", "Key #3", + "Bogus #5", "Bogus #6", "Bogus #7", "Bogus #8" + }; + + fout = PR_STDOUT; + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_NewThreadPrivateIndex(&key[keys], Destructor); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 8; ++keys) + { + pd = PR_GetThreadPrivate(key[keys]); + MY_ASSERT(NULL == pd); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + for (keys = 4; keys < 8; ++keys) { + key[keys] = 4096; /* set to invalid value */ + } + did = should = PR_FALSE; + for (keys = 4; keys < 8; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_FAILURE == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], key_string[keys]); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 0; keys < 4; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = PR_NewThreadPrivateIndex(&key[keys], Destructor); + MY_ASSERT(PR_SUCCESS == rv); + rv = PR_SetThreadPrivate(key[keys], "EXTENSION"); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = PR_FALSE; should = PR_TRUE; + for (keys = 8; keys < 127; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + PrintProgress(__LINE__); + + did = should = PR_FALSE; + for (keys = 8; keys < 127; ++keys) + { + rv = PR_SetThreadPrivate(key[keys], NULL); + MY_ASSERT(PR_SUCCESS == rv); + } + + thread = PR_CreateThread( + PR_USER_THREAD, Thread, NULL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + (void)PR_JoinThread(thread); + + PrintProgress(__LINE__); + +#if defined(WIN16) + printf( + "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED")); +#else + (void)PR_fprintf( + fout, "%s\n",((PR_TRUE == failed) ? "FAILED" : "PASSED")); +#endif + + return 0; + +} /* Tpd */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dl:r:"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug = PR_TRUE; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + PR_STDIO_INIT(); + return PR_Initialize(Tpd, argc, argv, 0); +} /* main */ + +/* tpd.c */ diff --git a/nsprpub/pr/tests/udpsrv.c b/nsprpub/pr/tests/udpsrv.c new file mode 100644 index 0000000000..39a919c606 --- /dev/null +++ b/nsprpub/pr/tests/udpsrv.c @@ -0,0 +1,546 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/******************************************************************* +** udpsrc.c -- Test basic function of UDP server +** +** udpsrv operates on the same machine with program udpclt. +** udpsrv is the server side of a udp sockets application. +** udpclt is the client side of a udp sockets application. +** +** The test is designed to assist developers in porting/debugging +** the UDP socket functions of NSPR. +** +** This test is not a stress test. +** +** main() starts two threads: UDP_Server() and UDP_Client(); +** main() uses PR_JoinThread() to wait for the threads to complete. +** +** UDP_Server() does repeated recvfrom()s from a socket. +** He detects an EOF condition set by UDP_Client(). For each +** packet received by UDP_Server(), he checks its content for +** expected content, then sends the packet back to UDP_Client(). +** +** UDP_Client() sends packets to UDP_Server() using sendto() +** he recieves packets back from the server via recvfrom(). +** After he sends enough packets containing UDP_AMOUNT_TO_WRITE +** bytes of data, he sends an EOF message. +** +** The test issues a pass/fail message at end. +** +** Notes: +** The variable "_debug_on" can be set to 1 to cause diagnostic +** messages related to client/server synchronization. Useful when +** the test hangs. +** +** Error messages are written to stdout. +** +******************************************************************** +*/ +/* --- include files --- */ +#include "nspr.h" +#include "prpriv.h" + +#include "plgetopt.h" +#include "prttools.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef XP_PC +#define mode_t int +#endif + +#define UDP_BUF_SIZE 4096 +#define UDP_DGRAM_SIZE 128 +#define UDP_AMOUNT_TO_WRITE (PRInt32)((UDP_DGRAM_SIZE * 1000l) +1) +#define NUM_UDP_CLIENTS 1 +#define NUM_UDP_DATAGRAMS_PER_CLIENT 5 +#define UDP_SERVER_PORT 9050 +#define UDP_CLIENT_PORT 9053 +#define MY_INADDR PR_INADDR_ANY +#define PEER_INADDR PR_INADDR_LOOPBACK + +#define UDP_TIMEOUT 400000 +/* #define UDP_TIMEOUT PR_INTERVAL_NO_TIMEOUT */ + +/* --- static data --- */ +static PRIntn _debug_on = 0; +static PRBool passed = PR_TRUE; +static PRUint32 cltBytesRead = 0; +static PRUint32 srvBytesRead = 0; +static PRFileDesc *output = NULL; + +/* --- static function declarations --- */ +#define DPRINTF(arg) if (_debug_on) PR_fprintf(output, arg) + + + +/******************************************************************* +** ListNetAddr() -- Display the Net Address on stdout +** +** Description: displays the component parts of a PRNetAddr struct +** +** Arguments: address of PRNetAddr structure to display +** +** Returns: void +** +** Notes: +** +******************************************************************** +*/ +void ListNetAddr( char *msg, PRNetAddr *na ) +{ + char mbuf[256]; + + sprintf( mbuf, "ListNetAddr: %s family: %d, port: %d, ip: %8.8X\n", + msg, na->inet.family, PR_ntohs( na->inet.port), PR_ntohl(na->inet.ip) ); +#if 0 + DPRINTF( mbuf ); +#endif +} /* --- end ListNetAddr() --- */ + +/******************************************************************** +** UDP_Server() -- Test a UDP server application +** +** Description: The Server side of a UDP Client/Server application. +** +** Arguments: none +** +** Returns: void +** +** Notes: +** +** +******************************************************************** +*/ +static void PR_CALLBACK UDP_Server( void *arg ) +{ + static char svrBuf[UDP_BUF_SIZE]; + PRFileDesc *svrSock; + PRInt32 rv; + PRNetAddr netaddr; + PRBool bound = PR_FALSE; + PRBool endOfInput = PR_FALSE; + PRInt32 numBytes = UDP_DGRAM_SIZE; + + DPRINTF("udpsrv: UDP_Server(): starting\n" ); + + /* --- Create the socket --- */ + DPRINTF("udpsrv: UDP_Server(): Creating UDP Socket\n" ); + svrSock = PR_NewUDPSocket(); + if ( svrSock == NULL ) + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Server(): PR_NewUDPSocket() returned NULL\n" ); + return; + } + + /* --- Initialize the sockaddr_in structure --- */ + memset( &netaddr, 0, sizeof( netaddr )); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.port = PR_htons( UDP_SERVER_PORT ); + netaddr.inet.ip = PR_htonl( MY_INADDR ); + + /* --- Bind the socket --- */ + while ( !bound ) + { + DPRINTF("udpsrv: UDP_Server(): Binding socket\n" ); + rv = PR_Bind( svrSock, &netaddr ); + if ( rv < 0 ) + { + if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR ) + { + if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ + PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); + PR_Sleep( PR_MillisecondsToInterval( 2000 )); + continue; + } + else + { + passed = PR_FALSE; + if (debug_mode) PR_fprintf(output, "udpsrv: UDP_Server(): \ + PR_Bind(): failed: %ld with error: %ld\n", + rv, PR_GetError() ); + PR_Close( svrSock ); + return; + } + } + else { + bound = PR_TRUE; + } + } + ListNetAddr( "UDP_Server: after bind", &netaddr ); + + /* --- Recv the socket --- */ + while( !endOfInput ) + { + DPRINTF("udpsrv: UDP_Server(): RecvFrom() socket\n" ); + rv = PR_RecvFrom( svrSock, svrBuf, numBytes, 0, &netaddr, UDP_TIMEOUT ); + if ( rv == -1 ) + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Server(): PR_RecvFrom(): failed with error: %ld\n", + PR_GetError() ); + PR_Close( svrSock ); + return; + } + ListNetAddr( "UDP_Server after RecvFrom", &netaddr ); + + srvBytesRead += rv; + + if ( svrBuf[0] == 'E' ) + { + DPRINTF("udpsrv: UDP_Server(): EOF on input detected\n" ); + endOfInput = PR_TRUE; + } + + /* --- Send the socket --- */ + DPRINTF("udpsrv: UDP_Server(): SendTo(): socket\n" ); + rv = PR_SendTo( svrSock, svrBuf, rv, 0, &netaddr, PR_INTERVAL_NO_TIMEOUT ); + if ( rv == -1 ) + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Server(): PR_SendTo(): failed with error: %ld\n", + PR_GetError() ); + PR_Close( svrSock ); + return; + } + ListNetAddr( "UDP_Server after SendTo", &netaddr ); + } + + /* --- Close the socket --- */ + DPRINTF("udpsrv: UDP_Server(): Closing socket\n" ); + rv = PR_Close( svrSock ); + if ( rv != PR_SUCCESS ) + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Server(): PR_Close(): failed to close socket\n" ); + return; + } + + DPRINTF("udpsrv: UDP_Server(): Normal end\n" ); +} /* --- end UDP_Server() --- */ + + +static char cltBuf[UDP_BUF_SIZE]; +static char cltBufin[UDP_BUF_SIZE]; +/******************************************************************** +** UDP_Client() -- Test a UDP client application +** +** Description: +** +** Arguments: +** +** +** Returns: +** 0 -- Successful execution +** 1 -- Test failed. +** +** Notes: +** +** +******************************************************************** +*/ +static void PR_CALLBACK UDP_Client( void *arg ) +{ + PRFileDesc *cltSock; + PRInt32 rv; + PRBool bound = PR_FALSE; + PRNetAddr netaddr; + PRNetAddr netaddrx; + PRBool endOfInput = PR_FALSE; + PRInt32 numBytes = UDP_DGRAM_SIZE; + PRInt32 writeThisMany = UDP_AMOUNT_TO_WRITE; + int i; + + + DPRINTF("udpsrv: UDP_Client(): starting\n" ); + + /* --- Create the socket --- */ + cltSock = PR_NewUDPSocket(); + if ( cltSock == NULL ) + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Client(): PR_NewUDPSocket() returned NULL\n" ); + return; + } + + /* --- Initialize the sockaddr_in structure --- */ + memset( &netaddr, 0, sizeof( netaddr )); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.ip = PR_htonl( MY_INADDR ); + netaddr.inet.port = PR_htons( UDP_CLIENT_PORT ); + + /* --- Initialize the write buffer --- */ + for ( i = 0; i < UDP_BUF_SIZE ; i++ ) { + cltBuf[i] = i; + } + + /* --- Bind the socket --- */ + while ( !bound ) + { + DPRINTF("udpsrv: UDP_Client(): Binding socket\n" ); + rv = PR_Bind( cltSock, &netaddr ); + if ( rv < 0 ) + { + if ( PR_GetError() == PR_ADDRESS_IN_USE_ERROR ) + { + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Client(): PR_Bind(): reports: PR_ADDRESS_IN_USE_ERROR\n"); + PR_Sleep( PR_MillisecondsToInterval( 2000 )); + continue; + } + else + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Client(): PR_Bind(): failed: %ld with error: %ld\n", + rv, PR_GetError() ); + PR_Close( cltSock ); + return; + } + } + else { + bound = PR_TRUE; + } + } + ListNetAddr( "UDP_Client after Bind", &netaddr ); + + /* --- Initialize the sockaddr_in structure --- */ + memset( &netaddr, 0, sizeof( netaddr )); + netaddr.inet.family = PR_AF_INET; + netaddr.inet.ip = PR_htonl( PEER_INADDR ); + netaddr.inet.port = PR_htons( UDP_SERVER_PORT ); + + /* --- send and receive packets until no more data left */ + while( !endOfInput ) + { + /* + ** Signal EOF in the data stream on the last packet + */ + if ( writeThisMany <= UDP_DGRAM_SIZE ) + { + DPRINTF("udpsrv: UDP_Client(): Send EOF packet\n" ); + cltBuf[0] = 'E'; + endOfInput = PR_TRUE; + } + + /* --- SendTo the socket --- */ + if ( writeThisMany > UDP_DGRAM_SIZE ) { + numBytes = UDP_DGRAM_SIZE; + } + else { + numBytes = writeThisMany; + } + writeThisMany -= numBytes; + { + char mbuf[256]; + sprintf( mbuf, "udpsrv: UDP_Client(): write_this_many: %d, numbytes: %d\n", + writeThisMany, numBytes ); + DPRINTF( mbuf ); + } + + DPRINTF("udpsrv: UDP_Client(): SendTo(): socket\n" ); + rv = PR_SendTo( cltSock, cltBuf, numBytes, 0, &netaddr, UDP_TIMEOUT ); + if ( rv == -1 ) + { + passed = PR_FALSE; + if (debug_mode) + PR_fprintf(output, + "udpsrv: UDP_Client(): PR_SendTo(): failed with error: %ld\n", + PR_GetError() ); + PR_Close( cltSock ); + return; + } + ListNetAddr( "UDP_Client after SendTo", &netaddr ); + + /* --- RecvFrom the socket --- */ + memset( cltBufin, 0, UDP_BUF_SIZE ); + DPRINTF("udpsrv: UDP_Client(): RecvFrom(): socket\n" ); + rv = PR_RecvFrom( cltSock, cltBufin, numBytes, 0, &netaddrx, UDP_TIMEOUT ); + if ( rv == -1 ) + { + passed = PR_FALSE; + if (debug_mode) PR_fprintf(output, + "udpsrv: UDP_Client(): PR_RecvFrom(): failed with error: %ld\n", + PR_GetError() ); + PR_Close( cltSock ); + return; + } + ListNetAddr( "UDP_Client after RecvFrom()", &netaddr ); + cltBytesRead += rv; + + /* --- verify buffer --- */ + for ( i = 0; i < rv ; i++ ) + { + if ( cltBufin[i] != i ) + { + /* --- special case, end of input --- */ + if ( endOfInput && i == 0 && cltBufin[0] == 'E' ) { + continue; + } + passed = PR_FALSE; + if (debug_mode) PR_fprintf(output, + "udpsrv: UDP_Client(): return data mismatch\n" ); + PR_Close( cltSock ); + return; + } + } + if (debug_mode) { + PR_fprintf(output, "."); + } + } + + /* --- Close the socket --- */ + DPRINTF("udpsrv: UDP_Server(): Closing socket\n" ); + rv = PR_Close( cltSock ); + if ( rv != PR_SUCCESS ) + { + passed = PR_FALSE; + if (debug_mode) PR_fprintf(output, + "udpsrv: UDP_Client(): PR_Close(): failed to close socket\n" ); + return; + } + DPRINTF("udpsrv: UDP_Client(): ending\n" ); +} /* --- end UDP_Client() --- */ + +/******************************************************************** +** main() -- udpsrv +** +** arguments: +** +** Returns: +** 0 -- Successful execution +** 1 -- Test failed. +** +** Description: +** +** Standard test case setup. +** +** Calls the function UDP_Server() +** +******************************************************************** +*/ + +int main(int argc, char **argv) +{ + PRThread *srv, *clt; + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d -v + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dv"); + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode */ + debug_mode = 1; + break; + case 'v': /* verbose mode */ + _debug_on = 1; + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + PR_STDIO_INIT(); + output = PR_STDERR; + + PR_SetConcurrency(4); + + /* + ** Create the Server thread + */ + DPRINTF( "udpsrv: Creating Server Thread\n" ); + srv = PR_CreateThread( PR_USER_THREAD, + UDP_Server, + (void *) 0, + PR_PRIORITY_LOW, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0 ); + if ( srv == NULL ) + { + if (debug_mode) { + PR_fprintf(output, "udpsrv: Cannot create server thread\n" ); + } + passed = PR_FALSE; + } + + /* + ** Give the Server time to Start + */ + DPRINTF( "udpsrv: Pausing to allow Server to start\n" ); + PR_Sleep( PR_MillisecondsToInterval(200) ); + + /* + ** Create the Client thread + */ + DPRINTF( "udpsrv: Creating Client Thread\n" ); + clt = PR_CreateThread( PR_USER_THREAD, + UDP_Client, + (void *) 0, + PR_PRIORITY_LOW, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0 ); + if ( clt == NULL ) + { + if (debug_mode) { + PR_fprintf(output, "udpsrv: Cannot create server thread\n" ); + } + passed = PR_FALSE; + } + + /* + ** + */ + DPRINTF("udpsrv: Waiting to join Server & Client Threads\n" ); + PR_JoinThread( srv ); + PR_JoinThread( clt ); + + /* + ** Evaluate test results + */ + if (debug_mode) PR_fprintf(output, "\n\nudpsrv: main(): cltBytesRead(%ld), \ + srvBytesRead(%ld), expected(%ld)\n", + cltBytesRead, srvBytesRead, UDP_AMOUNT_TO_WRITE ); + if ( cltBytesRead != srvBytesRead || cltBytesRead != UDP_AMOUNT_TO_WRITE ) + { + passed = PR_FALSE; + } + PR_Cleanup(); + if ( passed ) { + return 0; + } + else { + return 1; + } +} /* --- end main() --- */ diff --git a/nsprpub/pr/tests/ut_ttools.h b/nsprpub/pr/tests/ut_ttools.h new file mode 100644 index 0000000000..e3cac5daa9 --- /dev/null +++ b/nsprpub/pr/tests/ut_ttools.h @@ -0,0 +1,11 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used in Regress Tool */ +#define NOSTATUS 2 +#define PASS 1 +#define FAIL 0 + +PRIntn debug_mode=0; diff --git a/nsprpub/pr/tests/vercheck.c b/nsprpub/pr/tests/vercheck.c new file mode 100644 index 0000000000..95bda45292 --- /dev/null +++ b/nsprpub/pr/tests/vercheck.c @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * File: vercheck.c + * + * Description: + * This test tests the PR_VersionCheck() function. The + * compatible_version and incompatible_version arrays + * need to be updated for each patch or release. + * + * Tested areas: library version compatibility check. + */ + +#include "prinit.h" + +#include <stdio.h> +#include <stdlib.h> + +/* + * This release (4.10.10) is backward compatible with the + * 4.0.x, 4.1.x, 4.2.x, 4.3.x, 4.4.x, 4.5.x, 4.6.x, 4.7.x, + * 4.8.x, 4.9.x, 4.10.x and 4.11.X releases. + * It, of course, is compatible with itself. + */ +static char *compatible_version[] = { + "4.0", "4.0.1", "4.1", "4.1.1", "4.1.2", "4.1.3", + "4.2", "4.2.1", "4.2.2", "4.3", "4.4", "4.4.1", + "4.5", "4.5.1", + "4.6", "4.6.1", "4.6.2", "4.6.3", "4.6.4", "4.6.5", + "4.6.6", "4.6.7", "4.6.8", + "4.7", "4.7.1", "4.7.2", "4.7.3", "4.7.4", "4.7.5", + "4.7.6", + "4.8", "4.8.1", "4.8.2", "4.8.3", "4.8.4", "4.8.5", + "4.8.6", "4.8.7", "4.8.8", "4.8.9", + "4.9", "4.9.1", "4.9.2", "4.9.3", "4.9.4", "4.9.5", + "4.9.6", + "4.10", "4.10.1", "4.10.2", "4.10.3", "4.10.4", + "4.10.5", "4.10.6", "4.10.7", "4.10.8", "4.10.9", + "4.10.10", "4.11", "4.12", "4.13", "4.14", "4.15", + "4.16", "4.17", "4.18", "4.19", "4.20", "4.21", "4.22", + "4.23", "4.24", "4.25", "4,26", "4.27", "4.28", "4.29", + "4.30", "4.31", "4.32", "4.33", "4.34", + PR_VERSION +}; + +/* + * This release is not backward compatible with the old + * NSPR 2.1 and 3.x releases. + * + * Any release is incompatible with future releases and + * patches. + */ +static char *incompatible_version[] = { + "2.1 19980529", + "3.0", "3.0.1", + "3.1", "3.1.1", "3.1.2", "3.1.3", + "3.5", "3.5.1", + "4.35.1", + "4.36", "4.36.1", + "10.0", "11.1", "12.14.20" +}; + +int main(int argc, char **argv) +{ + int idx; + int num_compatible = sizeof(compatible_version) / sizeof(char *); + int num_incompatible = sizeof(incompatible_version) / sizeof(char *); + + printf("NSPR release %s:\n", PR_VERSION); + for (idx = 0; idx < num_compatible; idx++) { + if (PR_VersionCheck(compatible_version[idx]) == PR_FALSE) { + fprintf(stderr, "Should be compatible with version %s\n", + compatible_version[idx]); + exit(1); + } + printf("Compatible with version %s\n", compatible_version[idx]); + } + + for (idx = 0; idx < num_incompatible; idx++) { + if (PR_VersionCheck(incompatible_version[idx]) == PR_TRUE) { + fprintf(stderr, "Should be incompatible with version %s\n", + incompatible_version[idx]); + exit(1); + } + printf("Incompatible with version %s\n", incompatible_version[idx]); + } + + printf("PASS\n"); + return 0; +} diff --git a/nsprpub/pr/tests/version.c b/nsprpub/pr/tests/version.c new file mode 100644 index 0000000000..49e8a05d78 --- /dev/null +++ b/nsprpub/pr/tests/version.c @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prio.h" +#include "prprf.h" +#include "prlink.h" +#include "prvrsion.h" + +#include "plerror.h" +#include "plgetopt.h" + +PR_IMPORT(const PRVersionDescription *) libVersionPoint(void); + +int main(int argc, char **argv) +{ + PRIntn rv = 1; + PLOptStatus os; + PRIntn verbosity = 0; + PRLibrary *runtime = NULL; + const char *library_name = NULL; + const PRVersionDescription *version_info; + char buffer[100]; + PRExplodedTime exploded; + PLOptState *opt = PL_CreateOptState(argc, argv, "d"); + + PRFileDesc *err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 0: /* fully qualified library name */ + library_name = opt->value; + break; + case 'd': /* verbodity */ + verbosity += 1; + break; + default: + PR_fprintf(err, "Usage: version [-d] {fully qualified library name}\n"); + return 2; /* but not a lot else */ + } + } + PL_DestroyOptState(opt); + + if (NULL != library_name) + { + runtime = PR_LoadLibrary(library_name); + if (NULL == runtime) { + PL_FPrintError(err, "PR_LoadLibrary"); + return 3; + } else { + versionEntryPointType versionPoint = (versionEntryPointType) + PR_FindSymbol(runtime, "libVersionPoint"); + if (NULL == versionPoint) { + PL_FPrintError(err, "PR_FindSymbol"); + return 4; + } + version_info = versionPoint(); + } + } else { + version_info = libVersionPoint(); /* NSPR's version info */ + } + + (void)PR_fprintf(err, "Runtime library version information\n"); + PR_ExplodeTime( + version_info->buildTime, PR_GMTParameters, &exploded); + (void)PR_FormatTime( + buffer, sizeof(buffer), "%d %b %Y %H:%M:%S", &exploded); + (void)PR_fprintf(err, " Build time: %s GMT\n", buffer); + (void)PR_fprintf( + err, " Build time: %s\n", version_info->buildTimeString); + (void)PR_fprintf( + err, " %s V%u.%u.%u (%s%s%s)\n", + version_info->description, + version_info->vMajor, + version_info->vMinor, + version_info->vPatch, + (version_info->beta ? " beta " : ""), + (version_info->debug ? " debug " : ""), + (version_info->special ? " special" : "")); + (void)PR_fprintf(err, " filename: %s\n", version_info->filename); + (void)PR_fprintf(err, " security: %s\n", version_info->security); + (void)PR_fprintf(err, " copyright: %s\n", version_info->copyright); + (void)PR_fprintf(err, " comment: %s\n", version_info->comment); + rv = 0; + return rv; +} + +/* version.c */ diff --git a/nsprpub/pr/tests/writev.c b/nsprpub/pr/tests/writev.c new file mode 100644 index 0000000000..f72f00fb44 --- /dev/null +++ b/nsprpub/pr/tests/writev.c @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nspr.h" + +#include "plgetopt.h" + +#include <stdlib.h> +#include <string.h> + + +#ifndef IOV_MAX +#define IOV_MAX 16 +#endif + +#ifdef DEBUG +#define PORT_INC_DO +100 +#else +#define PORT_INC_DO +#endif +#ifdef IS_64 +#define PORT_INC_3264 +200 +#else +#define PORT_INC_3264 +#endif + +#define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 + +int PR_CALLBACK Writev(int argc, char **argv) +{ + + PRStatus rv; + PRNetAddr serverAddr; + PRFileDesc *clientSock, *debug = NULL; + + char *buffer = NULL; + PRIOVec *iov = NULL; + PRBool passed = PR_TRUE; + PRIntervalTime timein, elapsed, timeout; + PRIntervalTime tmo_min = 0x7fffffff, tmo_max = 0, tmo_elapsed = 0; + PRInt32 tmo_counted = 0, iov_index, loop, bytes, number_fragments; + PRInt32 message_length = 100, fragment_length = 100, messages = 100; + struct Descriptor { + PRInt32 length; + PRUint32 checksum; + } descriptor; + + /* + * USAGE + * -h dns name of host serving the connection (default = self) + * -m number of messages to send (default = 100) + * -s size of each message (default = 100) + * -f size of each message fragment (default = 100) + */ + + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dh:m:s:f:"); + + PR_STDIO_INIT(); + rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &serverAddr); + PR_ASSERT(PR_SUCCESS == rv); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'h': /* the remote host */ + { + PRIntn es = 0; + PRHostEnt host; + char buffer[1024]; + (void)PR_GetHostByName(opt->value, buffer, sizeof(buffer), &host); + es = PR_EnumerateHostEnt(es, &host, BASE_PORT, &serverAddr); + PR_ASSERT(es > 0); + } + break; + case 'd': /* debug mode */ + debug = PR_GetSpecialFD(PR_StandardError); + break; + case 'm': /* number of messages to send */ + messages = atoi(opt->value); + break; + case 's': /* total size of each message */ + message_length = atoi(opt->value); + break; + case 'f': /* size of each message fragment */ + fragment_length = atoi(opt->value); + break; + default: + break; + } + } + PL_DestroyOptState(opt); + + buffer = (char*)malloc(message_length); + + number_fragments = (message_length + fragment_length - 1) / fragment_length + 1; + while (IOV_MAX < number_fragments) + { + fragment_length = message_length / (IOV_MAX - 2); + number_fragments = (message_length + fragment_length - 1) / + fragment_length + 1; + if (NULL != debug) PR_fprintf(debug, + "Too many fragments - reset fragment length to %ld\n", fragment_length); + } + iov = (PRIOVec*)malloc(number_fragments * sizeof(PRIOVec)); + + iov[0].iov_base = (char*)&descriptor; + iov[0].iov_len = sizeof(descriptor); + for (iov_index = 1; iov_index < number_fragments; ++iov_index) + { + iov[iov_index].iov_base = buffer + (iov_index - 1) * fragment_length; + iov[iov_index].iov_len = fragment_length; + } + + for (bytes = 0; bytes < message_length; ++bytes) { + buffer[bytes] = (char)bytes; + } + + timeout = PR_SecondsToInterval(1); + + for (loop = 0; loop < messages; ++loop) + { + if (NULL != debug) { + PR_fprintf(debug, "[%d]socket ... ", loop); + } + clientSock = PR_NewTCPSocket(); + if (clientSock) + { + timein = PR_IntervalNow(); + if (NULL != debug) { + PR_fprintf(debug, "connecting ... "); + } + rv = PR_Connect(clientSock, &serverAddr, timeout); + if (PR_SUCCESS == rv) + { + descriptor.checksum = 0; + descriptor.length = (loop < (messages - 1)) ? message_length : 0; + if (0 == descriptor.length) { + number_fragments = 1; + } + else + for (iov_index = 0; iov_index < descriptor.length; ++iov_index) + { + PRUint32 overflow = descriptor.checksum & 0x80000000; + descriptor.checksum = (descriptor.checksum << 1); + if (0x00000000 != overflow) { + descriptor.checksum += 1; + } + descriptor.checksum += buffer[iov_index]; + } + if (NULL != debug) PR_fprintf( + debug, "sending %d bytes ... ", descriptor.length); + + /* then, at the last moment ... */ + descriptor.length = PR_ntohl(descriptor.length); + descriptor.checksum = PR_ntohl(descriptor.checksum); + + bytes = PR_Writev(clientSock, iov, number_fragments, timeout); + if (NULL != debug) { + PR_fprintf(debug, "closing ... "); + } + rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); + rv = PR_Close(clientSock); + if (NULL != debug) PR_fprintf( + debug, "%s\n", ((PR_SUCCESS == rv) ? "good" : "bad")); + elapsed = PR_IntervalNow() - timein; + if (elapsed < tmo_min) { + tmo_min = elapsed; + } + else if (elapsed > tmo_max) { + tmo_max = elapsed; + } + tmo_elapsed += elapsed; + tmo_counted += 1; + } + else + { + if (NULL != debug) PR_fprintf( + debug, "failed - retrying (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + PR_Close(clientSock); + } + } + else if (NULL != debug) + { + PR_fprintf(debug, "unable to create client socket\n"); + passed = PR_FALSE; + } + } + if (NULL != debug) { + if (0 == tmo_counted) { + PR_fprintf(debug, "No connection made\n"); + } else { + PR_fprintf( + debug, "\nTimings: %d [%d] %d (microseconds)\n", + PR_IntervalToMicroseconds(tmo_min), + PR_IntervalToMicroseconds(tmo_elapsed / tmo_counted), + PR_IntervalToMicroseconds(tmo_max)); + } + } + + PR_DELETE(buffer); + PR_DELETE(iov); + + PR_fprintf( + PR_GetSpecialFD(PR_StandardError), + "%s\n", (passed) ? "PASSED" : "FAILED"); + return (passed) ? 0 : 1; +} + +int main(int argc, char **argv) +{ + return (PR_VersionCheck(PR_VERSION)) ? + PR_Initialize(Writev, argc, argv, 4) : -1; +} /* main */ + +/* writev.c */ + + diff --git a/nsprpub/pr/tests/xnotify.c b/nsprpub/pr/tests/xnotify.c new file mode 100644 index 0000000000..7e6f0e2936 --- /dev/null +++ b/nsprpub/pr/tests/xnotify.c @@ -0,0 +1,429 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "plerror.h" +#include "plgetopt.h" + +#include "prinit.h" +#include "prprf.h" +#include "prio.h" +#include "prcvar.h" +#include "prmon.h" +#include "prcmon.h" +#include "prlock.h" +#include "prerror.h" +#include "prinit.h" +#include "prinrval.h" +#include "prthread.h" + +static PRLock *ml = NULL; +static PRIntervalTime base; +static PRFileDesc *err = NULL; + +typedef struct CMonShared +{ + PRInt32 o1, o2; +} CMonShared; + +typedef struct MonShared +{ + PRMonitor *o1, *o2; +} MonShared; + +typedef struct LockShared +{ + PRLock *o1, *o2; + PRCondVar *cv1, *cv2; +} LockShared; + +static void LogNow(const char *msg, PRStatus rv) +{ + PRIntervalTime now = PR_IntervalNow(); + PR_Lock(ml); + PR_fprintf(err, "%6ld: %s", (now - base), msg); + if (PR_FAILURE == rv) { + PL_FPrintError(err, " "); + } + else { + PR_fprintf(err, "\n"); + } + PR_Unlock(ml); +} /* LogNow */ + +static void Help(void) +{ + PR_fprintf(err, "Usage: [-[d][l][m][c]] [-h]\n"); + PR_fprintf(err, "\t-d debug mode (default: FALSE)\n"); + PR_fprintf(err, "\t-l test with locks (default: FALSE)\n"); + PR_fprintf(err, "\t-m tests with monitors (default: FALSE)\n"); + PR_fprintf(err, "\t-c tests with cmonitors (default: FALSE)\n"); + PR_fprintf(err, "\t-h help\n"); +} /* Help */ + +static void PR_CALLBACK T2CMon(void *arg) +{ + PRStatus rv; + CMonShared *shared = (CMonShared*)arg; + + PR_CEnterMonitor(&shared->o1); + LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS); + rv = PR_CWait(&shared->o1, PR_SecondsToInterval(5)); + if (PR_SUCCESS == rv) { + LogNow("T2 resuming on o1", rv); + } + else { + LogNow("T2 wait failed on o1", rv); + } + + rv = PR_CNotify(&shared->o1); + if (PR_SUCCESS == rv) { + LogNow("T2 notified o1", rv); + } + else { + LogNow("T2 notify on o1 failed", rv); + } + + PR_CExitMonitor(&shared->o1); +} /* T2CMon */ + +static void PR_CALLBACK T3CMon(void *arg) +{ + PRStatus rv; + CMonShared *shared = (CMonShared*)arg; + + PR_CEnterMonitor(&shared->o2); + LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS); + rv = PR_CWait(&shared->o2, PR_SecondsToInterval(5)); + if (PR_SUCCESS == rv) { + LogNow("T3 resuming on o2", rv); + } + else { + LogNow("T3 wait failed on o2", rv); + } + rv = PR_CNotify(&shared->o2); + LogNow("T3 notify on o2", rv); + PR_CExitMonitor(&shared->o2); + +} /* T3CMon */ + +static CMonShared sharedCM; + +static void T1CMon(void) +{ + PRStatus rv; + PRThread *t2, *t3; + + PR_fprintf(err, "\n**********************************\n"); + PR_fprintf(err, " CACHED MONITORS\n"); + PR_fprintf(err, "**********************************\n"); + + base = PR_IntervalNow(); + + PR_CEnterMonitor(&sharedCM.o1); + LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS); + rv = PR_CWait(&sharedCM.o1, PR_SecondsToInterval(3)); + if (PR_SUCCESS == rv) { + LogNow("T1 resuming on o1", rv); + } + else { + LogNow("T1 wait on o1 failed", rv); + } + PR_CExitMonitor(&sharedCM.o1); + + LogNow("T1 creating T2", PR_SUCCESS); + t2 = PR_CreateThread( + PR_USER_THREAD, T2CMon, &sharedCM, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + LogNow("T1 creating T3", PR_SUCCESS); + t3 = PR_CreateThread( + PR_USER_THREAD, T3CMon, &sharedCM, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + PR_CEnterMonitor(&sharedCM.o2); + LogNow("T1 waiting forever on o2", PR_SUCCESS); + rv = PR_CWait(&sharedCM.o2, PR_INTERVAL_NO_TIMEOUT); + if (PR_SUCCESS == rv) { + LogNow("T1 resuming on o2", rv); + } + else { + LogNow("T1 wait on o2 failed", rv); + } + PR_CExitMonitor(&sharedCM.o2); + + (void)PR_JoinThread(t2); + (void)PR_JoinThread(t3); + +} /* T1CMon */ + +static void PR_CALLBACK T2Mon(void *arg) +{ + PRStatus rv; + MonShared *shared = (MonShared*)arg; + + PR_EnterMonitor(shared->o1); + LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS); + rv = PR_Wait(shared->o1, PR_SecondsToInterval(5)); + if (PR_SUCCESS == rv) { + LogNow("T2 resuming on o1", rv); + } + else { + LogNow("T2 wait failed on o1", rv); + } + + rv = PR_Notify(shared->o1); + if (PR_SUCCESS == rv) { + LogNow("T2 notified o1", rv); + } + else { + LogNow("T2 notify on o1 failed", rv); + } + + PR_ExitMonitor(shared->o1); +} /* T2Mon */ + +static void PR_CALLBACK T3Mon(void *arg) +{ + PRStatus rv; + MonShared *shared = (MonShared*)arg; + + PR_EnterMonitor(shared->o2); + LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS); + rv = PR_Wait(shared->o2, PR_SecondsToInterval(5)); + if (PR_SUCCESS == rv) { + LogNow("T3 resuming on o2", rv); + } + else { + LogNow("T3 wait failed on o2", rv); + } + rv = PR_Notify(shared->o2); + LogNow("T3 notify on o2", rv); + PR_ExitMonitor(shared->o2); + +} /* T3Mon */ + +static MonShared sharedM; +static void T1Mon(void) +{ + PRStatus rv; + PRThread *t2, *t3; + + PR_fprintf(err, "\n**********************************\n"); + PR_fprintf(err, " MONITORS\n"); + PR_fprintf(err, "**********************************\n"); + + sharedM.o1 = PR_NewMonitor(); + sharedM.o2 = PR_NewMonitor(); + + base = PR_IntervalNow(); + + PR_EnterMonitor(sharedM.o1); + LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS); + rv = PR_Wait(sharedM.o1, PR_SecondsToInterval(3)); + if (PR_SUCCESS == rv) { + LogNow("T1 resuming on o1", rv); + } + else { + LogNow("T1 wait on o1 failed", rv); + } + PR_ExitMonitor(sharedM.o1); + + LogNow("T1 creating T2", PR_SUCCESS); + t2 = PR_CreateThread( + PR_USER_THREAD, T2Mon, &sharedM, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + LogNow("T1 creating T3", PR_SUCCESS); + t3 = PR_CreateThread( + PR_USER_THREAD, T3Mon, &sharedM, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + PR_EnterMonitor(sharedM.o2); + LogNow("T1 waiting forever on o2", PR_SUCCESS); + rv = PR_Wait(sharedM.o2, PR_INTERVAL_NO_TIMEOUT); + if (PR_SUCCESS == rv) { + LogNow("T1 resuming on o2", rv); + } + else { + LogNow("T1 wait on o2 failed", rv); + } + PR_ExitMonitor(sharedM.o2); + + (void)PR_JoinThread(t2); + (void)PR_JoinThread(t3); + + PR_DestroyMonitor(sharedM.o1); + PR_DestroyMonitor(sharedM.o2); + +} /* T1Mon */ + +static void PR_CALLBACK T2Lock(void *arg) +{ + PRStatus rv; + LockShared *shared = (LockShared*)arg; + + PR_Lock(shared->o1); + LogNow("T2 waiting 5 seconds on o1", PR_SUCCESS); + rv = PR_WaitCondVar(shared->cv1, PR_SecondsToInterval(5)); + if (PR_SUCCESS == rv) { + LogNow("T2 resuming on o1", rv); + } + else { + LogNow("T2 wait failed on o1", rv); + } + + rv = PR_NotifyCondVar(shared->cv1); + if (PR_SUCCESS == rv) { + LogNow("T2 notified o1", rv); + } + else { + LogNow("T2 notify on o1 failed", rv); + } + + PR_Unlock(shared->o1); +} /* T2Lock */ + +static void PR_CALLBACK T3Lock(void *arg) +{ + PRStatus rv; + LockShared *shared = (LockShared*)arg; + + PR_Lock(shared->o2); + LogNow("T3 waiting 5 seconds on o2", PR_SUCCESS); + rv = PR_WaitCondVar(shared->cv2, PR_SecondsToInterval(5)); + if (PR_SUCCESS == rv) { + LogNow("T3 resuming on o2", rv); + } + else { + LogNow("T3 wait failed on o2", rv); + } + rv = PR_NotifyCondVar(shared->cv2); + LogNow("T3 notify on o2", rv); + PR_Unlock(shared->o2); + +} /* T3Lock */ + +/* +** Make shared' a static variable for Win16 +*/ +static LockShared sharedL; + +static void T1Lock(void) +{ + PRStatus rv; + PRThread *t2, *t3; + sharedL.o1 = PR_NewLock(); + sharedL.o2 = PR_NewLock(); + sharedL.cv1 = PR_NewCondVar(sharedL.o1); + sharedL.cv2 = PR_NewCondVar(sharedL.o2); + + PR_fprintf(err, "\n**********************************\n"); + PR_fprintf(err, " LOCKS\n"); + PR_fprintf(err, "**********************************\n"); + + base = PR_IntervalNow(); + + PR_Lock(sharedL.o1); + LogNow("T1 waiting 3 seconds on o1", PR_SUCCESS); + rv = PR_WaitCondVar(sharedL.cv1, PR_SecondsToInterval(3)); + if (PR_SUCCESS == rv) { + LogNow("T1 resuming on o1", rv); + } + else { + LogNow("T1 wait on o1 failed", rv); + } + PR_Unlock(sharedL.o1); + + LogNow("T1 creating T2", PR_SUCCESS); + t2 = PR_CreateThread( + PR_USER_THREAD, T2Lock, &sharedL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + LogNow("T1 creating T3", PR_SUCCESS); + t3 = PR_CreateThread( + PR_USER_THREAD, T3Lock, &sharedL, PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + PR_Lock(sharedL.o2); + LogNow("T1 waiting forever on o2", PR_SUCCESS); + rv = PR_WaitCondVar(sharedL.cv2, PR_INTERVAL_NO_TIMEOUT); + if (PR_SUCCESS == rv) { + LogNow("T1 resuming on o2", rv); + } + else { + LogNow("T1 wait on o2 failed", rv); + } + PR_Unlock(sharedL.o2); + + (void)PR_JoinThread(t2); + (void)PR_JoinThread(t3); + + PR_DestroyLock(sharedL.o1); + PR_DestroyLock(sharedL.o2); + PR_DestroyCondVar(sharedL.cv1); + PR_DestroyCondVar(sharedL.cv2); +} /* T1Lock */ + +static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dhlmc"); + PRBool locks = PR_FALSE, monitors = PR_FALSE, cmonitors = PR_FALSE; + + err = PR_GetSpecialFD(PR_StandardError); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) { + continue; + } + switch (opt->option) + { + case 'd': /* debug mode (noop) */ + break; + case 'l': /* locks */ + locks = PR_TRUE; + break; + case 'm': /* monitors */ + monitors = PR_TRUE; + break; + case 'c': /* cached monitors */ + cmonitors = PR_TRUE; + break; + case 'h': /* needs guidance */ + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + ml = PR_NewLock(); + if (locks) { + T1Lock(); + } + if (monitors) { + T1Mon(); + } + if (cmonitors) { + T1CMon(); + } + + PR_DestroyLock(ml); + + PR_fprintf(err, "Done!\n"); + return 0; +} /* main */ + + +int main(int argc, char **argv) +{ + PRIntn rv; + + PR_STDIO_INIT(); + rv = PR_Initialize(RealMain, argc, argv, 0); + return rv; +} /* main */ +/* xnotify.c */ diff --git a/nsprpub/pr/tests/y2k.c b/nsprpub/pr/tests/y2k.c new file mode 100644 index 0000000000..31b35e7e88 --- /dev/null +++ b/nsprpub/pr/tests/y2k.c @@ -0,0 +1,799 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * file: y2k.c + * description: Test for y2k compliance for NSPR. + * + * Sep 1999. lth. Added "Sun" specified dates to the test data. + */ +/*********************************************************************** +** Includes +***********************************************************************/ +/* Used to get the command line option */ +#include "plgetopt.h" + +#include "prinit.h" +#include "prtime.h" +#include "prprf.h" +#include "prlog.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define PRINT_DETAILS + +int failed_already=0; +PRBool debug_mode = PR_FALSE; + +static char *dayOfWeek[] = +{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" }; +static char *month[] = +{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" +}; + +PRLogModuleInfo *lm; + +static void PrintExplodedTime(const PRExplodedTime *et) { + PRInt32 totalOffset; + PRInt32 hourOffset, minOffset; + const char *sign; + + /* Print day of the week, month, day, hour, minute, and second */ + printf("%s %s %2ld %02ld:%02ld:%02ld ", + dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday, + et->tm_hour, et->tm_min, et->tm_sec); + + /* Print year */ + printf("%hd ", et->tm_year); + + /* Print time zone */ + totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset; + if (totalOffset == 0) { + printf("UTC "); + } else { + sign = "+"; + if (totalOffset < 0) { + totalOffset = -totalOffset; + sign = "-"; + } + hourOffset = totalOffset / 3600; + minOffset = (totalOffset % 3600) / 60; + printf("%s%02ld%02ld ", sign, hourOffset, minOffset); + } +#ifdef PRINT_DETAILS + printf("{%d, %d, %d, %d, %d, %d, %d, %d, %d, { %d, %d}}\n",et->tm_usec, + et->tm_sec, + et->tm_min, + et->tm_hour, + et->tm_mday, + et->tm_month, + et->tm_year, + et->tm_wday, + et->tm_yday, + et->tm_params.tp_gmt_offset, + et->tm_params.tp_dst_offset); +#endif +} + +static int ExplodedTimeIsEqual(const PRExplodedTime *et1, + const PRExplodedTime *et2) +{ + if (et1->tm_usec == et2->tm_usec && + et1->tm_sec == et2->tm_sec && + et1->tm_min == et2->tm_min && + et1->tm_hour == et2->tm_hour && + et1->tm_mday == et2->tm_mday && + et1->tm_month == et2->tm_month && + et1->tm_year == et2->tm_year && + et1->tm_wday == et2->tm_wday && + et1->tm_yday == et2->tm_yday && + et1->tm_params.tp_gmt_offset == et2->tm_params.tp_gmt_offset && + et1->tm_params.tp_dst_offset == et2->tm_params.tp_dst_offset) { + return 1; + } else { + return 0; + } +} + +/* + * TEST 1: TestExplodeImplodeTime + * Description: + * For each given timestamp T (a PRTime value), call PR_ExplodeTime + * with GMT, US Pacific, and local time parameters. Compare the + * resulting calendar (exploded) time values with the expected + * values. + * + * Note: the expected local time values depend on the local time + * zone. The local time values stored in this test are for the US + * Pacific Time Zone. If you are running this test in a different + * time zone, you need to modify the values in the localt array. + * An example is provided below. + * + * Call PR_ImplodeTime for each of the exploded values and compare + * the resulting PRTime values with the original input. + * + * This test is run for the values of time T corresponding to the + * following dates: + * - 12/31/99 - before 2000 + * - 01/01/00 - after 2000 + * - Leap year - Feb 29, 2000 + * - March 1st, 2001 (after 1 year) + * - March 1st, 2005 (after second leap year) + * - 09/09/99 (used by some programs as an end of file marker) + * + * Call PR_Now, convert to calendar time using PR_ExplodeTime and + * manually check the result for correctness. The time should match + * the system clock. + * + * Tested functions: PR_Now, PR_ExplodeTime, PR_ImplodeTime, + * PR_LocalTimeParameters, PR_GMTParameters. + */ + +static PRTime prt[] = { + LL_INIT(220405, 2133125120), /* 946634400000000 */ + LL_INIT(220425, 2633779200), /* 946720800000000 */ + LL_INIT(221612, 2107598848), /* 951818400000000 */ + LL_INIT(228975, 663398400), /* 983440800000000 */ + LL_INIT(258365, 1974568960), /* 1109671200000000 */ + 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[] = { + { 0, 0, 0, 10, 31, 11, 1999, 5, 364, {0, 0}}, /* 1999/12/31 10:00:00 GMT */ + { 0, 0, 0, 10, 1, 0, 2000, 6, 0, {0, 0}}, /* 2000/01/01 10:00:00 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 */ + /* 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[] = { + { 0, 0, 0, 2, 31, 11, 1999, 5, 364, {-28800, 0}}, /* 1999/12/31 2:00:00 PST */ + { 0, 0, 0, 2, 1, 0, 2000, 6, 0, {-28800, 0}}, /* 2000/01/01 2:00:00 PST */ + { 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 */ + /* 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 */ +}; + +/* + * This test assumes that we are in US Pacific Time Zone. + * If you are running this test in a different time zone, + * you need to modify the localt array and fill in the + * expected results. The localt array for US Eastern Time + * Zone is provided as an example. + */ +static PRExplodedTime localt[] = { + { 0, 0, 0, 2, 31, 11, 1999, 5, 364, {-28800, 0}}, /* 1999/12/31 2:00:00 PST */ + { 0, 0, 0, 2, 1, 0, 2000, 6, 0, {-28800, 0}}, /* 2000/01/01 2:00:00 PST */ + { 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 */ + /* 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 +static PRExplodedTime localt[] = { + { 0, 0, 0, 5, 31, 11, 1999, 5, 364, {-18000, 0}}, /* 1999/12/31 2:00:00 EST */ + { 0, 0, 0, 5, 1, 0, 2000, 6, 0, {-18000, 0}}, /* 2000/01/01 2:00:00 EST */ + { 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 */ + /* 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 + +static PRStatus TestExplodeImplodeTime(void) +{ + PRTime prt_tmp; + PRTime now; + int idx; + int array_size = sizeof(prt) / sizeof(PRTime); + PRExplodedTime et_tmp; + char buf[1024]; + + for (idx = 0; idx < array_size; idx++) { + PR_snprintf(buf, sizeof(buf), "%lld", prt[idx]); + if (debug_mode) { + printf("Time stamp %s\n", buf); + } + 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); + if (LL_NE(prt_tmp, prt[idx])) { + fprintf(stderr, "PRTime not equal\n"); + exit(1); + } + if (debug_mode) { + printf("GMT: "); + PrintExplodedTime(&et_tmp); + printf("\n"); + } + + 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); + if (LL_NE(prt_tmp, prt[idx])) { + fprintf(stderr, "PRTime not equal\n"); + exit(1); + } + if (debug_mode) { + printf("US Pacific Time: "); + PrintExplodedTime(&et_tmp); + printf("\n"); + } + + 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); + if (LL_NE(prt_tmp, prt[idx])) { + fprintf(stderr, "not equal\n"); + exit(1); + } + if (debug_mode) { + printf("Local time:"); + PrintExplodedTime(&et_tmp); + printf("\n\n"); + } + } + + now = PR_Now(); + PR_ExplodeTime(now, PR_GMTParameters, &et_tmp); + printf("Current GMT is "); + PrintExplodedTime(&et_tmp); + printf("\n"); + prt_tmp = PR_ImplodeTime(&et_tmp); + if (LL_NE(prt_tmp, now)) { + fprintf(stderr, "not equal\n"); + exit(1); + } + PR_ExplodeTime(now, PR_USPacificTimeParameters, &et_tmp); + printf("Current US Pacific Time is "); + PrintExplodedTime(&et_tmp); + printf("\n"); + prt_tmp = PR_ImplodeTime(&et_tmp); + if (LL_NE(prt_tmp, now)) { + fprintf(stderr, "not equal\n"); + exit(1); + } + PR_ExplodeTime(now, PR_LocalTimeParameters, &et_tmp); + printf("Current local time is "); + PrintExplodedTime(&et_tmp); + printf("\n"); + prt_tmp = PR_ImplodeTime(&et_tmp); + if (LL_NE(prt_tmp, now)) { + fprintf(stderr, "not equal\n"); + exit(1); + } + printf("Please verify the results\n\n"); + + if (debug_mode) { + printf("Test 1 passed\n"); + } + return PR_SUCCESS; +} +/* End of Test 1: TestExplodeImplodeTime */ + +/* + * Test 2: Normalize Time + */ + +/* + * time increment for addition to PRExplodeTime + */ +typedef struct time_increment { + PRInt32 ti_usec; + PRInt32 ti_sec; + PRInt32 ti_min; + PRInt32 ti_hour; +} time_increment_t; + +/* + * Data for testing PR_Normalize + * Add the increment to base_time, normalize it to GMT and US Pacific + * Time zone. + */ +typedef struct normalize_test_data { + PRExplodedTime base_time; + time_increment_t increment; + PRExplodedTime expected_gmt_time; + PRExplodedTime expected_uspt_time; +} normalize_test_data_t; + + +/* + * Test data - the base time values cover dates of interest including y2k - 1, + * y2k + 1, y2k leap year, y2k leap date + 1year, + * y2k leap date + 4 years + */ +normalize_test_data_t normalize_test_array[] = { + /*usec sec min hour mday mo year wday yday {gmtoff, dstoff }*/ + + /* Fri 12/31/1999 19:32:48 PST */ + { {0, 48, 32, 19, 31, 11, 1999, 5, 364, { -28800, 0}}, + {0, 0, 30, 20}, + {0, 48, 2, 0, 2, 0, 2000, 0, 1, { 0, 0}}, /*Sun Jan 2 00:02:48 UTC 2000*/ + {0, 48, 2, 16, 1, 0, 2000, 6, 0, { -28800, 0}},/* Sat Jan 1 16:02:48 + PST 2000*/ + }, + /* Fri 99-12-31 23:59:02 GMT */ + { {0, 2, 59, 23, 31, 11, 1999, 5, 364, { 0, 0}}, + {0, 0, 45, 0}, + {0, 2, 44, 0, 1, 0, 2000, 6, 0, { 0, 0}},/* Sat Jan 1 00:44:02 UTC 2000*/ + {0, 2, 44, 16, 31, 11, 1999, 5, 364, { -28800, 0}}/*Fri Dec 31 16:44:02 + PST 1999*/ + }, + /* 99-12-25 12:00:00 GMT */ + { {0, 0, 0, 12, 25, 11, 1999, 6, 358, { 0, 0}}, + {0, 0, 0, 364 * 24}, + {0, 0, 0, 12, 23, 11, 2000, 6, 357, { 0, 0}},/*Sat Dec 23 12:00:00 + 2000 UTC*/ + {0, 0, 0, 4, 23, 11, 2000, 6, 357, { -28800, 0}}/*Sat Dec 23 04:00:00 + 2000 -0800*/ + }, + /* 00-01-1 00:00:00 PST */ + { {0, 0, 0, 0, 1, 0, 2000, 6, 0, { -28800, 0}}, + {0, 0, 0, 48}, + {0, 0, 0, 8, 3, 0, 2000, 1, 2, { 0, 0}},/*Mon Jan 3 08:00:00 2000 UTC*/ + {0, 0, 0, 0, 3, 0, 2000, 1, 2, { -28800, 0}}/*Mon Jan 3 00:00:00 2000 + -0800*/ + }, + /* 00-01-10 12:00:00 PST */ + { {0, 0, 0, 12, 10, 0, 2000, 1, 9, { -28800, 0}}, + {0, 0, 0, 364 * 5 * 24}, + {0, 0, 0, 20, 3, 0, 2005, 1, 2, { 0, 0}},/*Mon Jan 3 20:00:00 2005 UTC */ + {0, 0, 0, 12, 3, 0, 2005, 1, 2, { -28800, 0}}/*Mon Jan 3 12:00:00 + 2005 -0800*/ + }, + /* 00-02-28 15:39 GMT */ + { {0, 0, 39, 15, 28, 1, 2000, 1, 58, { 0, 0}}, + {0, 0, 0, 24}, + {0, 0, 39, 15, 29, 1, 2000, 2, 59, { 0, 0}}, /*Tue Feb 29 15:39:00 2000 + UTC*/ + {0, 0, 39, 7, 29, 1, 2000, 2, 59, { -28800, 0}}/*Tue Feb 29 07:39:00 + 2000 -0800*/ + }, + /* 01-03-01 12:00 PST */ + { {0, 0, 0, 12, 3, 0, 2001, 3, 2, { -28800, 0}},/*Wed Jan 3 12:00:00 + -0800 2001*/ + {0, 30, 30,45}, + {0, 30, 30, 17, 5, 0, 2001, 5, 4, { 0, 0}}, /*Fri Jan 5 17:30:30 2001 + UTC*/ + {0, 30, 30, 9, 5, 0, 2001, 5, 4, { -28800, 0}} /*Fri Jan 5 09:30:30 + 2001 -0800*/ + }, + /* 2004-04-26 12:00 GMT */ + { {0, 0, 0, 20, 3, 0, 2001, 3, 2, { 0, 0}}, + {0, 0, 30,0}, + {0, 0, 30, 20, 3, 0, 2001, 3, 2, { 0, 0}},/*Wed Jan 3 20:30:00 2001 UTC*/ + {0, 0, 30, 12, 3, 0, 2001, 3, 2, { -28800, 0}}/*Wed Jan 3 12:30:00 + 2001 -0800*/ + }, + /* 99-09-09 00:00 GMT */ + { {0, 0, 0, 0, 9, 8, 1999, 4, 251, { 0, 0}}, + {0, 0, 0, 12}, + {0, 0, 0, 12, 9, 8, 1999, 4, 251, { 0, 0}},/*Thu Sep 9 12:00:00 1999 UTC*/ + {0, 0, 0, 5, 9, 8, 1999, 4, 251, { -28800, 3600}}/*Thu Sep 9 05:00:00 + 1999 -0700*/ + } +}; + +void add_time_increment(PRExplodedTime *et1, time_increment_t *it) +{ + et1->tm_usec += it->ti_usec; + et1->tm_sec += it->ti_sec; + et1->tm_min += it->ti_min; + et1->tm_hour += it->ti_hour; +} + +/* +** TestNormalizeTime() -- Test PR_NormalizeTime() +** For each data item, add the time increment to the base_time and then +** normalize it for GMT and local time zones. This test assumes that +** the local time zone is the Pacific Time Zone. The normalized values +** should match the expected values in the data item. +** +*/ +PRStatus TestNormalizeTime(void) +{ + int idx, count; + normalize_test_data_t *itemp; + time_increment_t *itp; + + count = sizeof(normalize_test_array)/sizeof(normalize_test_array[0]); + for (idx = 0; idx < count; idx++) { + itemp = &normalize_test_array[idx]; + if (debug_mode) { + printf("%2d. %15s",idx +1,"Base time: "); + PrintExplodedTime(&itemp->base_time); + printf("\n"); + } + itp = &itemp->increment; + if (debug_mode) { + printf("%20s %2d hrs %2d min %3d sec\n","Add",itp->ti_hour, + itp->ti_min, itp->ti_sec); + } + add_time_increment(&itemp->base_time, &itemp->increment); + PR_NormalizeTime(&itemp->base_time, PR_LocalTimeParameters); + if (debug_mode) { + printf("%19s","PST time: "); + PrintExplodedTime(&itemp->base_time); + printf("\n"); + } + if (!ExplodedTimeIsEqual(&itemp->base_time, + &itemp->expected_uspt_time)) { + printf("PR_NormalizeTime failed\n"); + if (debug_mode) { + PrintExplodedTime(&itemp->expected_uspt_time); + } + return PR_FAILURE; + } + PR_NormalizeTime(&itemp->base_time, PR_GMTParameters); + if (debug_mode) { + printf("%19s","GMT time: "); + PrintExplodedTime(&itemp->base_time); + printf("\n"); + } + + if (!ExplodedTimeIsEqual(&itemp->base_time, + &itemp->expected_gmt_time)) { + printf("PR_NormalizeTime failed\n"); + return PR_FAILURE; + } + } + return PR_SUCCESS; +} + + +/* +** ParseTest. Structure defining a string time and a matching exploded time +** +*/ +typedef struct ParseTest +{ + char *sDate; /* string to be converted using PR_ParseTimeString() */ + PRExplodedTime et; /* expected result of the conversion */ +} ParseTest; + +static ParseTest parseArray[] = +{ + /* |<----- expected result ------------------------------------------->| */ + /* "string to test" usec sec min hour day mo year wday julian {gmtoff, dstoff }*/ + { "Thursday 1 Jan 1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "1 Jan 1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "1-Jan-1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "01-Jan-1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "January 1, 1970", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "January 1, 1970 00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "January 01, 1970 00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "January 01 1970 00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "January 01 1970 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "01-01-1970", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "01/01/1970", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "01/01/70", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "01/01/70 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "70/01/01 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "70/1/1 00:00:", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "00:00 Thursday, January 1, 1970",{ 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "1-Jan-70 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "70-01-01 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + { "70/01/01 00:00:00", { 000000, 00, 00, 00, 1, 0, 1970, 4, 0, {-28800, 0 }}}, + + /* 31-Dec-1969 */ + { "Wed 31 Dec 1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}}, + { "31 Dec 1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}}, + { "12/31/69 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}}, + { "12/31/1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}}, + { "12-31-69 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}}, + { "12-31-1969 00:00:00", { 000000, 00, 00, 00, 31, 11, 1969, 3, 364, {-28800, 0 }}}, + { "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 }}}, + { "12/31/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 }}}, + { "12-31-1999 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}}, + { "99-12-31 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}}, + { "99/12/31 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}}, + + /* 01-Jan-2000 */ + { "01 Jan 2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, + { "1/1/00 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, + { "1/1/2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, + { "1-1-00 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, + { "1-1-2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, + { "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 }}}, + { "2/29/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 }}}, + { "2-29-2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}}, + { "02-29-00 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}}, + { "02-29-2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}}, + + /* 01-Mar-2000 */ + { "01 Mar 2000 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}}, + { "3/1/00 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}}, + { "3/1/2000 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}}, + { "3-1-00 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}}, + { "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 }}}, + { "3/1/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 }}}, + { "3-1-2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}}, + { "03-01-01 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}}, + { "03-01-2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}}, + + /* 29-Feb-2004 */ + { "29 Feb 2004 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}}, + { "2/29/04 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}}, + { "2/29/2004 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}}, + { "2-29-04 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}}, + { "2-29-2004 00:00:00", { 000000, 00, 00, 00, 29, 1, 2004, 0, 59, {-28800, 0 }}}, + + /* 01-Mar-2004 */ + { "01 Mar 2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + { "3/1/04 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + { "3/1/2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + { "3-1-04 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + { "3-1-2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + { "03-01-04 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + { "03-01-2004 00:00:00", { 000000, 00, 00, 00, 1, 2, 2004, 1, 60, {-28800, 0 }}}, + + /* 01-Mar-2005 */ + { "01 Mar 2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, + { "3/1/05 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, + { "3/1/2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, + { "3-1-05 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, + { "3-1-2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, + { "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 }}}, + + /* last element. string must be null */ + { NULL } +}; /* end array of ParseTest */ + +/* +** TestParseTime() -- Test PR_ParseTimeString() for y2k compliance +** +** TestParseTime() loops thru the array parseArray. For each element in +** the array, he calls PR_ParseTimeString() with sDate as the conversion +** argument. The result (ct) is then converted to a PRExplodedTime structure +** and compared with the exploded time value (parseArray[n].et) in the +** array element; if equal, the element passes the test. +** +** The array parseArray[] contains entries that are interesting to the +** y2k problem. +** +** +*/ +static PRStatus TestParseTime( void ) +{ + ParseTest *ptp = parseArray; + PRTime ct; + PRExplodedTime cet; + char *sp = ptp->sDate; + PRStatus rc; + PRStatus rv = PR_SUCCESS; + + while ( sp != NULL) + { + rc = PR_ParseTimeString( sp, PR_FALSE, &ct ); + if ( PR_FAILURE == rc ) + { + printf("TestParseTime(): PR_ParseTimeString() failed to convert: %s\n", sp ); + rv = PR_FAILURE; + failed_already = 1; + } + else + { + PR_ExplodeTime( ct, PR_LocalTimeParameters, &cet ); + + if ( !ExplodedTimeIsEqual( &cet, &ptp->et )) + { + printf("TestParseTime(): Exploded time compare failed: %s\n", sp ); + if ( debug_mode ) + { + PrintExplodedTime( &cet ); + printf("\n"); + PrintExplodedTime( &ptp->et ); + printf("\n"); + } + + rv = PR_FAILURE; + failed_already = 1; + } + } + + /* point to next element in array, keep going */ + ptp++; + sp = ptp->sDate; + } /* end while() */ + + return( rv ); +} /* end TestParseTime() */ + +int main(int argc, char** argv) +{ + /* The command line argument: -d is used to determine if the test is being run + in debug mode. The regress tool requires only one line output:PASS or FAIL. + All of the printfs associated with this test has been handled with a if (debug_mode) + test. + Usage: test_name -d + */ + PLOptStatus os; + PLOptState *opt; + + PR_STDIO_INIT(); + opt = PL_CreateOptState(argc, argv, "d"); + 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; + default: + break; + } + } + PL_DestroyOptState(opt); + + /* main test */ + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + lm = PR_NewLogModule("test"); + + if ( PR_FAILURE == TestExplodeImplodeTime()) + { + PR_LOG( lm, PR_LOG_ERROR, + ("TestExplodeImplodeTime() failed")); + } + else { + printf("Test 1: Calendar Time Test passed\n"); + } + + if ( PR_FAILURE == TestNormalizeTime()) + { + PR_LOG( lm, PR_LOG_ERROR, + ("TestNormalizeTime() failed")); + } + else { + printf("Test 2: Normalize Time Test passed\n"); + } + + if ( PR_FAILURE == TestParseTime()) + { + PR_LOG( lm, PR_LOG_ERROR, + ("TestParseTime() failed")); + } + else { + printf("Test 3: Parse Time Test passed\n"); + } + + if (failed_already) { + return 1; + } + else { + return 0; + } +} /* end main() y2k.c */ + diff --git a/nsprpub/pr/tests/y2ktmo.c b/nsprpub/pr/tests/y2ktmo.c new file mode 100644 index 0000000000..b28467e4d5 --- /dev/null +++ b/nsprpub/pr/tests/y2ktmo.c @@ -0,0 +1,554 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * 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) +#if defined(WINCE) +#include <windows.h> +#else +#include <sys/types.h> +#include <sys/timeb.h> /* for _ftime */ +#endif +#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) +#if defined(WINCE) +static DWORD start_time_tick; +#else +static struct _timeb start_time_tb; +#endif +#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) && !defined(WINCE) + 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) +#if defined(WINCE) + elapsed_msecs = GetTickCount() - start_time_tick; +#else + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#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) && !defined(WINCE) + 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) +#if defined(WINCE) + elapsed_msecs = GetTickCount() - start_time_tick; +#else + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#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) && !defined(WINCE) + 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) +#if defined(WINCE) + elapsed_msecs = GetTickCount() - start_time_tick; +#else + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#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) && !defined(WINCE) + 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) +#if defined(WINCE) + elapsed_msecs = GetTickCount() - start_time_tick; +#else + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#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) && !defined(WINCE) + 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) +#if defined(WINCE) + elapsed_msecs = GetTickCount() - start_time_tick; +#else + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#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) && !defined(WINCE) + 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) +#if defined(WINCE) + elapsed_msecs = GetTickCount() - start_time_tick; +#else + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#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) +#ifdef WINCE + start_time_tick = GetTickCount(); +#else + _ftime(&start_time_tb); +#endif +#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; +} diff --git a/nsprpub/pr/tests/yield.c b/nsprpub/pr/tests/yield.c new file mode 100644 index 0000000000..eb55926b58 --- /dev/null +++ b/nsprpub/pr/tests/yield.c @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdio.h> +#include "prthread.h" +#include "prinit.h" +#ifndef XP_OS2 +#include "private/pprmisc.h" +#include <windows.h> +#else +#include "primpl.h" +#include <os2.h> +#endif + +#define THREADS 10 + + +void +threadmain(void *_id) +{ + int id = (int)_id; + int index; + + printf("thread %d alive\n", id); + for (index=0; index<10; index++) { + printf("thread %d yielding\n", id); + PR_Sleep(0); + printf("thread %d awake\n", id); + } + printf("thread %d dead\n", id); + +} + +int main(int argc, char **argv) +{ + int index; + PRThread *a[THREADS]; + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 5); + PR_STDIO_INIT(); + + for (index=0; index<THREADS; index++) { + a[index] = PR_CreateThread(PR_USER_THREAD, + threadmain, + (void *)index, + PR_PRIORITY_NORMAL, + index%2?PR_LOCAL_THREAD:PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + } + for(index=0; index<THREADS; index++) { + PR_JoinThread(a[index]); + } + printf("main dying\n"); +} diff --git a/nsprpub/pr/tests/zerolen.c b/nsprpub/pr/tests/zerolen.c new file mode 100644 index 0000000000..a84bd43228 --- /dev/null +++ b/nsprpub/pr/tests/zerolen.c @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Test: zerolen.c + * + * Description: a test for Bugzilla bug #17699. We perform + * the same test for PR_Writev, PR_Write, and PR_Send. In + * each test the server thread first fills up the connection + * to the client so that the next write operation will fail + * with EAGAIN. Then it calls PR_Writev, PR_Write, or PR_Send + * with a zero-length buffer. The client thread initially + * does not read so that the connection can be filled up. + * Then it empties the connection so that the server thread's + * PR_Writev, PR_Write, or PR_Send call can succeed. + * + * Bug #17699 is specific to the pthreads version on Unix, + * so on other platforms this test does nothing. + */ + +#ifndef XP_UNIX + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("PASS\n"); + return 0; +} + +#else /* XP_UNIX */ + +#include "nspr.h" +#include "private/pprio.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +static void ClientThread(void *arg) +{ + PRFileDesc *sock; + PRNetAddr addr; + PRUint16 port = (PRUint16) arg; + char buf[1024]; + PRInt32 nbytes; + + sock = PR_NewTCPSocket(); + if (NULL == sock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + if (PR_InitializeNetAddr(PR_IpAddrLoopback, port, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + if (PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + fprintf(stderr, "PR_Connect failed\n"); + exit(1); + } + /* + * Sleep 5 seconds to force the server thread to get EAGAIN. + */ + if (PR_Sleep(PR_SecondsToInterval(5)) == PR_FAILURE) { + fprintf(stderr, "PR_Sleep failed\n"); + exit(1); + } + /* + * Then start reading. + */ + while (nbytes = PR_Read(sock, buf, sizeof(buf)) > 0) { + /* empty loop body */ + } + if (-1 == nbytes) { + fprintf(stderr, "PR_Read failed\n"); + exit(1); + } + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } +} + +int main() +{ + PRFileDesc *listenSock; + PRFileDesc *acceptSock; + int osfd; + PRThread *clientThread; + PRNetAddr addr; + char buf[1024]; + PRInt32 nbytes; + PRIOVec iov; + + memset(buf, 0, sizeof(buf)); /* Initialize the buffer. */ + listenSock = PR_NewTCPSocket(); + if (NULL == listenSock) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + if (PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + if (PR_Bind(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + /* Find out what port number we are bound to. */ + if (PR_GetSockName(listenSock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_GetSockName failed\n"); + exit(1); + } + if (PR_Listen(listenSock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + + /* + * First test PR_Writev. + */ + clientThread = PR_CreateThread(PR_USER_THREAD, + ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)), + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == clientThread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (NULL == acceptSock) { + fprintf(stderr, "PR_Accept failed\n"); + exit(1); + } + osfd = PR_FileDesc2NativeHandle(acceptSock); + while (write(osfd, buf, sizeof(buf)) != -1) { + /* empty loop body */ + } + if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { + fprintf(stderr, "write failed\n"); + exit(1); + } + iov.iov_base = buf; + iov.iov_len = 0; + printf("calling PR_Writev with a zero-length buffer\n"); + fflush(stdout); + nbytes = PR_Writev(acceptSock, &iov, 1, PR_INTERVAL_NO_TIMEOUT); + if (nbytes != 0) { + fprintf(stderr, "PR_Writev should return 0 but returns %d\n", nbytes); + exit(1); + } + if (PR_Close(acceptSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (PR_JoinThread(clientThread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + + /* + * Then test PR_Write. + */ + clientThread = PR_CreateThread(PR_USER_THREAD, + ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)), + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == clientThread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (NULL == acceptSock) { + fprintf(stderr, "PR_Accept failed\n"); + exit(1); + } + osfd = PR_FileDesc2NativeHandle(acceptSock); + while (write(osfd, buf, sizeof(buf)) != -1) { + /* empty loop body */ + } + if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { + fprintf(stderr, "write failed\n"); + exit(1); + } + printf("calling PR_Write with a zero-length buffer\n"); + fflush(stdout); + nbytes = PR_Write(acceptSock, buf, 0); + if (nbytes != 0) { + fprintf(stderr, "PR_Write should return 0 but returns %d\n", nbytes); + exit(1); + } + if (PR_Close(acceptSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (PR_JoinThread(clientThread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + + /* + * Finally test PR_Send. + */ + clientThread = PR_CreateThread(PR_USER_THREAD, + ClientThread, (void *) PR_ntohs(PR_NetAddrInetPort(&addr)), + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == clientThread) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT); + if (NULL == acceptSock) { + fprintf(stderr, "PR_Accept failed\n"); + exit(1); + } + osfd = PR_FileDesc2NativeHandle(acceptSock); + while (write(osfd, buf, sizeof(buf)) != -1) { + /* empty loop body */ + } + if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { + fprintf(stderr, "write failed\n"); + exit(1); + } + printf("calling PR_Send with a zero-length buffer\n"); + fflush(stdout); + nbytes = PR_Send(acceptSock, buf, 0, 0, PR_INTERVAL_NO_TIMEOUT); + if (nbytes != 0) { + fprintf(stderr, "PR_Send should return 0 but returns %d\n", nbytes); + exit(1); + } + if (PR_Close(acceptSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (PR_JoinThread(clientThread) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + + if (PR_Close(listenSock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + printf("PASS\n"); + return 0; +} + +#endif /* XP_UNIX */ |