diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /nsprpub/pr/tests | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nsprpub/pr/tests')
181 files changed, 47678 insertions, 0 deletions
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 */ |