summaryrefslogtreecommitdiffstats
path: root/lib/libkse/test
diff options
context:
space:
mode:
authorjasone <jasone@FreeBSD.org>2000-04-24 21:07:45 +0000
committerjasone <jasone@FreeBSD.org>2000-04-24 21:07:45 +0000
commitb1932c6651c9c43c23902fdb79feba2c0e5374f0 (patch)
tree166a70288fa40c19e15b0a569c6a65f064c584c4 /lib/libkse/test
parentcf670bded417ef9f0446fb1326d76f4d9fcdb4cf (diff)
downloadFreeBSD-src-b1932c6651c9c43c23902fdb79feba2c0e5374f0.zip
FreeBSD-src-b1932c6651c9c43c23902fdb79feba2c0e5374f0.tar.gz
Automated regression test harness for libc_r. Existing tests are integrated,
a new test for POSIX semaphores was added, and examples of harness usage are included.
Diffstat (limited to 'lib/libkse/test')
-rw-r--r--lib/libkse/test/Makefile121
-rw-r--r--lib/libkse/test/README28
-rw-r--r--lib/libkse/test/hello_b.c13
-rw-r--r--lib/libkse/test/hello_d.c38
-rw-r--r--lib/libkse/test/hello_d.exp1
-rw-r--r--lib/libkse/test/hello_s.c47
-rw-r--r--lib/libkse/test/mutex_d.c1549
-rw-r--r--lib/libkse/test/mutex_d.exp290
-rw-r--r--lib/libkse/test/sem_d.c135
-rw-r--r--lib/libkse/test/sem_d.exp22
-rw-r--r--lib/libkse/test/sigsuspend_d.c288
-rw-r--r--lib/libkse/test/sigsuspend_d.exp8
-rw-r--r--lib/libkse/test/sigwait_d.c304
-rw-r--r--lib/libkse/test/sigwait_d.exp10
-rw-r--r--lib/libkse/test/verify505
15 files changed, 3356 insertions, 3 deletions
diff --git a/lib/libkse/test/Makefile b/lib/libkse/test/Makefile
index 90ee5ad..7b8bf1f 100644
--- a/lib/libkse/test/Makefile
+++ b/lib/libkse/test/Makefile
@@ -1,8 +1,123 @@
+#
# $FreeBSD$
#
-# Tests for libc_r functionality.
+# Automated test suite for libc_r (pthreads).
#
-SUBDIR= mutex sigsuspend sigwait
+# File lists.
+
+# Diff mode tests written in C.
+CDTESTS := hello_d.c mutex_d.c sem_d.c sigsuspend_d.c sigwait_d.c
+
+# Sequence mode tests written in C.
+CSTESTS := hello_s.c
+
+# C programs that are used internally by the perl-based tests. The build
+# system merely compiles these.
+BTESTS := hello_b.c
+
+# Diff mode tests written in perl.
+PDTESTS :=
+
+# Sequence mode tests written in perl.
+PSTESTS :=
+
+# Munge the file lists to their final executable names (strip the .c).
+CDTESTS := $(CDTESTS:R)
+CSTESTS := $(CSTESTS:R)
+BTESTS := $(BTESTS:R)
+
+CPPFLAGS := -D_LIBC_R_
+CFLAGS := -Wall -pipe -g3
+LDFLAGS_A := -static
+LDFLAGS_P := -pg
+LDFLAGS_S :=
+LIBS := -pthread
+
+# Flags passed to verify. "-v" or "-u" may be useful.
+VFLAGS :=
+
+all : default
+
+# Only use the following suffixes, in order to avoid any strange built-in rules.
+.SUFFIXES :
+.SUFFIXES : .c .o .d .pl
+
+# Clear out all paths, then set just one (default path) for the main build
+# directory.
+.PATH :
+.PATH : .
+
+# Build the C programs.
+.for bin in $(CDTESTS) $(CSTESTS) $(BTESTS)
+$(bin)_a : $(bin:S/$/&.c/)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $(bin:S/$/&.c/) -o $(@:S/$/&.o/)
+ $(CC) -o $@ $(@:S/$/&.o/) $(LDFLAGS_A) $(LIBS)
+ @$(SHELL) -ec "$(CC) -M $(CPPFLAGS) $(bin:S/$/&.c/) | sed \"s/\($(bin:T)\)\.o\([ :]*\)/$(bin:H:S!/!\\/!g)\/\1_a.o \2/g\" > $(@:R:S/$/&.d/)"
+
+$(bin)_p : $(bin:S/$/&.c/)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $(bin:S/$/&.c/) -o $(@:S/$/&.o/)
+ $(CC) -o $@ $(@:S/$/&.o/) $(LDFLAGS_P) $(LIBS)
+ @$(SHELL) -ec "$(CC) -M $(CPPFLAGS) $(bin:S/$/&.c/) | sed \"s/\($(bin:T)\)\.o\([ :]*\)/$(bin:H:S!/!\\/!g)\/\1_p.o \2/g\" > $(@:R:S/$/&.d/)"
+
+$(bin)_s : $(bin:S/$/&.c/)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $(bin:S/$/&.c/) -o $(@:S/$/&.o/)
+ $(CC) -o $@ $(@:S/$/&.o/) $(LDFLAGS_S) $(LIBS)
+ @$(SHELL) -ec "$(CC) -M $(CPPFLAGS) $(bin:S/$/&.c/) | sed \"s/\($(bin:T)\)\.o\([ :]*\)/$(bin:H:S!/!\\/!g)\/\1_s.o \2/g\" > $(@:R:S/$/&.d/)"
+.endfor
+
+# Dependency file inclusion.
+.for depfile in $(CDTESTS:R:S/$/&_a.d/) $(CSTESTS:R:S/$/&_a.d/) \
+ $(BTESTS:R:S/$/&_a.d/) $(CDTESTS:R:S/$/&_p.d/) \
+ $(CSTESTS:R:S/$/&_p.d/) $(BTESTS:R:S/$/&_p.d/) \
+ $(CDTESTS:R:S/$/&_s.d/) $(CSTESTS:R:S/$/&_s.d/) \
+ $(BTESTS:R:S/$/&_s.d/)
+.if exists($(depfile))
+.include "$(depfile)"
+.endif
+.endfor
+
+default : check
+
+tests_a : $(CDTESTS:S/$/&_a/) $(CSTESTS:S/$/&_a/) $(BTESTS:S/$/&_a/)
+tests_p : $(CDTESTS:S/$/&_p/) $(CSTESTS:S/$/&_p/) $(BTESTS:S/$/&_p/)
+tests_s : $(CDTESTS:S/$/&_s/) $(CSTESTS:S/$/&_s/) $(BTESTS:S/$/&_s/)
+
+tests : tests_a tests_p tests_s
+
+check_a : tests_a
+.for bin in $(CDTESTS) $(CSTESTS) $(BTESTS)
+ @cp $(bin)_a $(bin)
+.endfor
+ @echo "Test static library:"
+ @./verify $(VFLAGS) -- -d $(CDTESTS) $(PDTESTS) -s $(CSTESTS) $(PSTESTS)
+
+check_p : tests_p
+.for bin in $(CDTESTS) $(CSTESTS) $(BTESTS)
+ @cp $(bin)_p $(bin)
+.endfor
+ @echo "Test profile library:"
+ @./verify $(VFLAGS) -- -d $(CDTESTS) $(PDTESTS) -s $(CSTESTS) $(PSTESTS)
+
+check_s : tests_s
+.for bin in $(CDTESTS) $(CSTESTS) $(BTESTS)
+ @cp $(bin)_s $(bin)
+.endfor
+ @echo "Test shared library:"
+ @./verify $(VFLAGS) -- -d $(CDTESTS) $(PDTESTS) -s $(CSTESTS) $(PSTESTS)
+
+check : check_a check_p check_s
-.include <bsd.subdir.mk>
+clean :
+ rm -f *~
+ rm -f *.core
+ rm -f *.out
+ rm -f *.perf
+ rm -f *.diff
+ rm -f *.gmon
+ rm -f $(CDTESTS) $(CSTESTS) $(BTESTS)
+ rm -f $(CDTESTS:S/$/&_a/) $(CSTESTS:S/$/&_a/) $(BTESTS:S/$/&_a/)
+ rm -f $(CDTESTS:S/$/&_p/) $(CSTESTS:S/$/&_p/) $(BTESTS:S/$/&_p/)
+ rm -f $(CDTESTS:S/$/&_s/) $(CSTESTS:S/$/&_s/) $(BTESTS:S/$/&_s/)
+ rm -f *.d
+ rm -f *.o
diff --git a/lib/libkse/test/README b/lib/libkse/test/README
new file mode 100644
index 0000000..507ea4e
--- /dev/null
+++ b/lib/libkse/test/README
@@ -0,0 +1,28 @@
+$FreeBSD$
+
+This test suite is meant to test general functionality of pthreads, as well as
+provide a simple framework for regression tests. In general, this test suite
+can be used with any pthreads library, but in reality there are a number of
+libc_r-specific aspects to this test suite which would require some effort to
+get around if testing another pthreads library.
+
+This test suite assumes that libc_r is installed.
+
+There are two forms of test that the 'verify' script understands. The simpler
+form is the diff format, where the output of the test program is diff'ed with
+the correspondingly named .exp file. If there is diff output, the test fails.
+The sequence test format is somewhat more complex, and is documented in the
+command line usage output for verify. The advantage of this format is that it
+allows multiple tests to pass/fail within one program.
+
+There is no driving need for test naming consistency, but the existing tests
+generally follow these conventions:
+
+<name>_d.c <name>_d.exp : Diff mode C test and expected output file.
+<name>_s.c : Sequence mode C test.
+<name>_b*.c : Back end C program used by perl tests.
+<name>_d.pl <name>_d.pl.exp : Diff mode perl test and expected output file.
+<name>_s.pl : Sequence mode perl test.
+
+<name> is something descriptive, such as "pr14685" in the case of a PR-related
+regression test, or "mutex" in the case of a test of mutexes.
diff --git a/lib/libkse/test/hello_b.c b/lib/libkse/test/hello_b.c
new file mode 100644
index 0000000..2eefa7f
--- /dev/null
+++ b/lib/libkse/test/hello_b.c
@@ -0,0 +1,13 @@
+/****************************************************************************
+ *
+ * Back end C programs can be anything compilable.
+ *
+ * $FreeBSD$
+ *
+ ****************************************************************************/
+
+int
+main()
+{
+ return 0;
+}
diff --git a/lib/libkse/test/hello_d.c b/lib/libkse/test/hello_d.c
new file mode 100644
index 0000000..6d77526
--- /dev/null
+++ b/lib/libkse/test/hello_d.c
@@ -0,0 +1,38 @@
+/****************************************************************************
+ *
+ * Simple diff mode test.
+ *
+ * $FreeBSD$
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+void *
+entry(void * a_arg)
+{
+ fprintf(stderr, "Hello world\n");
+
+ return NULL;
+}
+
+int
+main()
+{
+ pthread_t thread;
+ int error;
+
+ error = pthread_create(&thread, NULL, entry, NULL);
+ if (error)
+ fprintf(stderr, "Error in pthread_create(): %s\n",
+ strerror(error));
+
+ error = pthread_join(thread, NULL);
+ if (error)
+ fprintf(stderr, "Error in pthread_join(): %s\n",
+ strerror(error));
+
+ return 0;
+}
diff --git a/lib/libkse/test/hello_d.exp b/lib/libkse/test/hello_d.exp
new file mode 100644
index 0000000..802992c
--- /dev/null
+++ b/lib/libkse/test/hello_d.exp
@@ -0,0 +1 @@
+Hello world
diff --git a/lib/libkse/test/hello_s.c b/lib/libkse/test/hello_s.c
new file mode 100644
index 0000000..942bf2d
--- /dev/null
+++ b/lib/libkse/test/hello_s.c
@@ -0,0 +1,47 @@
+/****************************************************************************
+ *
+ * Simple sequence mode test.
+ *
+ * $FreeBSD$
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+void *
+entry(void * a_arg)
+{
+ fprintf(stderr, "ok 1\n");
+ fprintf(stderr, "ok \n");
+ fprintf(stderr, "ok 3\n");
+
+ return NULL;
+}
+
+int
+main()
+{
+ pthread_t thread;
+ int error;
+
+ fprintf(stderr, "1..3\n");
+
+ fprintf(stderr, "Some random text\n");
+
+ error = pthread_create(&thread, NULL, entry, NULL);
+ fprintf(stderr, "More unimportant text\n");
+ if (error)
+ fprintf(stderr,"Error in pthread_create(): %s\n",
+ strerror(error));
+
+ error = pthread_join(thread, NULL);
+ if (error)
+ fprintf(stderr, "Error in pthread_join(): %s\n",
+ strerror(error));
+
+ fprintf(stderr, "Hello world\n");
+
+ return 0;
+}
diff --git a/lib/libkse/test/mutex_d.c b/lib/libkse/test/mutex_d.c
new file mode 100644
index 0000000..ea31299
--- /dev/null
+++ b/lib/libkse/test/mutex_d.c
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Daniel M. Eischen.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <assert.h>
+#include <errno.h>
+#include "pthread.h"
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+
+#if defined(_LIBC_R_)
+#include <pthread_np.h>
+#endif
+
+#ifndef NELEMENTS
+#define NELEMENTS(arr) (sizeof (arr) / sizeof (arr[0]))
+#endif
+
+#ifndef NUM_THREADS
+#define NUM_THREADS 10
+#endif
+
+#define MAX_THREAD_CMDS 10
+
+
+/*------------------------------------------------------------
+ * Types
+ *----------------------------------------------------------*/
+
+typedef enum {
+ STAT_INITIAL, /* initial state */
+ STAT_WAITCONDVAR, /* waiting for condition variable signal */
+ STAT_WAITMUTEX /* waiting for mutex lock */
+} thread_status_t;
+
+typedef enum {
+ FLAGS_REPORT_WAITCONDMUTEX = 0x01,
+ FLAGS_REPORT_WAITCONDVAR = 0x02,
+ FLAGS_REPORT_WAITMUTEX = 0x04,
+ FLAGS_REPORT_BUSY_LOOP = 0x08,
+ FLAGS_IS_BUSY = 0x10,
+ FLAGS_WAS_BUSY = 0x20
+} thread_flags_t;
+
+typedef enum {
+ CMD_NONE,
+ CMD_TAKE_MUTEX,
+ CMD_RELEASE_MUTEX,
+ CMD_WAIT_FOR_SIGNAL,
+ CMD_BUSY_LOOP,
+ CMD_PROTECTED_OP,
+ CMD_RELEASE_ALL
+} thread_cmd_id_t;
+
+typedef struct {
+ thread_cmd_id_t cmd_id;
+ pthread_mutex_t *mutex;
+ pthread_cond_t *cond;
+} thread_cmd_t;
+
+typedef struct {
+ pthread_cond_t cond_var;
+ thread_status_t status;
+ thread_cmd_t cmd;
+ int flags;
+ int priority;
+ int ret;
+ pthread_t tid;
+ u_int8_t id;
+} thread_state_t;
+
+typedef enum {
+ M_POSIX,
+ M_SS2_DEFAULT,
+ M_SS2_ERRORCHECK,
+ M_SS2_NORMAL,
+ M_SS2_RECURSIVE
+} mutex_kind_t;
+
+
+/*------------------------------------------------------------
+ * Constants
+ *----------------------------------------------------------*/
+
+const char *protocol_strs[] = {
+ "PTHREAD_PRIO_NONE",
+ "PTHREAD_PRIO_INHERIT",
+ "PTHREAD_PRIO_PROTECT"
+};
+
+const int protocols[] = {
+ PTHREAD_PRIO_NONE,
+ PTHREAD_PRIO_INHERIT,
+ PTHREAD_PRIO_PROTECT
+};
+
+const char *mutextype_strs[] = {
+ "POSIX (type not specified)",
+ "SS2 PTHREAD_MUTEX_DEFAULT",
+ "SS2 PTHREAD_MUTEX_ERRORCHECK",
+ "SS2 PTHREAD_MUTEX_NORMAL",
+ "SS2 PTHREAD_MUTEX_RECURSIVE"
+};
+
+const int mutex_types[] = {
+ 0, /* M_POSIX */
+ PTHREAD_MUTEX_DEFAULT, /* M_SS2_DEFAULT */
+ PTHREAD_MUTEX_ERRORCHECK, /* M_SS2_ERRORCHECK */
+ PTHREAD_MUTEX_NORMAL, /* M_SS2_NORMAL */
+ PTHREAD_MUTEX_RECURSIVE /* M_SS2_RECURSIVE */
+};
+
+
+/*------------------------------------------------------------
+ * Objects
+ *----------------------------------------------------------*/
+
+static int done = 0;
+static int trace_enabled = 0;
+static int use_global_condvar = 0;
+static thread_state_t states[NUM_THREADS];
+static int pipefd[2];
+
+static pthread_mutex_t waiter_mutex;
+static pthread_mutex_t cond_mutex;
+static pthread_cond_t cond_var;
+
+static FILE *logfile = stdout;
+static int error_count = 0, pass_count = 0, total = 0;
+
+
+/*------------------------------------------------------------
+ * Prototypes
+ *----------------------------------------------------------*/
+extern char *strtok_r(char *str, const char *sep, char **last);
+
+
+/*------------------------------------------------------------
+ * Functions
+ *----------------------------------------------------------*/
+
+#ifdef DEBUG
+static void
+kern_switch (pthread_t pthread_out, pthread_t pthread_in)
+{
+ if (pthread_out != NULL)
+ printf ("Swapping out thread 0x%x, ", (int) pthread_out);
+ else
+ printf ("Swapping out kernel thread, ");
+
+ if (pthread_in != NULL)
+ printf ("swapping in thread 0x%x\n", (int) pthread_in);
+ else
+ printf ("swapping in kernel thread.\n");
+}
+#endif
+
+
+static void
+log_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ fprintf (logfile, "FAIL: ");
+ vfprintf (logfile, fmt, ap);
+ error_count = error_count + 1;
+ total = total + 1;
+}
+
+
+static void
+log_pass (void)
+{
+ fprintf (logfile, "PASS\n");
+ pass_count = pass_count + 1;
+ total = total + 1;
+}
+
+
+static void
+log_trace (const char *fmt, ...)
+{
+ va_list ap;
+
+ if (trace_enabled) {
+ va_start (ap, fmt);
+ vfprintf (logfile, fmt, ap);
+ }
+}
+
+
+static void
+log (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (logfile, fmt, ap);
+}
+
+
+static void
+check_result (int expected, int actual)
+{
+ if (expected != actual)
+ log_error ("expected %d, returned %d\n", expected, actual);
+ else
+ log_pass ();
+}
+
+
+/*
+ * Check to see that the threads ran in the specified order.
+ */
+static void
+check_run_order (char *order)
+{
+ const char *sep = ":,";
+ char *tok, *last, *idstr, *endptr;
+ int expected_id, bytes, count = 0, errors = 0;
+ u_int8_t id;
+
+ assert ((tok = (char *) malloc (strlen(order) + 1)) != NULL);
+ strcpy (tok, order); /* tok has to be larger than order */
+ assert (ioctl (pipefd[0], FIONREAD, &bytes) == 0);
+ log_trace ("%d bytes read from FIFO.\n", bytes);
+
+ for (idstr = strtok_r (tok, sep, &last);
+ (idstr != NULL) && (count < bytes);
+ idstr = strtok_r (NULL, sep, &last)) {
+
+ /* Get the expected id: */
+ expected_id = (int) strtol (idstr, &endptr, 10);
+ assert ((endptr != NULL) && (*endptr == '\0'));
+
+ /* Read the actual id from the pipe: */
+ assert (read (pipefd[0], &id, sizeof (id)) == sizeof (id));
+ count = count + sizeof (id);
+
+ if (id != expected_id) {
+ log_trace ("Thread %d ran out of order.\n", id);
+ errors = errors + 1;
+ }
+ else {
+ log_trace ("Thread %d at priority %d reporting.\n",
+ (int) id, states[id].priority);
+ }
+ }
+
+ if (count < bytes) {
+ /* Clear the pipe: */
+ while (count < bytes) {
+ read (pipefd[0], &id, sizeof (id));
+ count = count + 1;
+ errors = errors + 1;
+ }
+ }
+ else if (bytes < count)
+ errors = errors + count - bytes;
+
+ if (errors == 0)
+ log_pass ();
+ else
+ log_error ("%d threads ran out of order", errors);
+}
+
+
+static void *
+waiter (void *arg)
+{
+ thread_state_t *statep = (thread_state_t *) arg;
+ pthread_mutex_t *held_mutex[MAX_THREAD_CMDS];
+ int held_mutex_owned[MAX_THREAD_CMDS];
+ sigset_t mask;
+ struct timeval tv1, tv2;
+ thread_cmd_t cmd;
+ int i, mutex_count = 0;
+
+ statep->status = STAT_INITIAL;
+
+ /* Block all signals except for interrupt.*/
+ sigfillset (&mask);
+ sigdelset (&mask, SIGINT);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ while (done == 0) {
+ /* Wait for signal from the main thread to continue. */
+ statep->status = STAT_WAITMUTEX;
+ log_trace ("Thread %d: locking cond_mutex.\n",
+ (int) statep->id);
+ pthread_mutex_lock (&cond_mutex);
+
+ /* Do we report our status. */
+ if (statep->flags & FLAGS_REPORT_WAITCONDMUTEX)
+ write (pipefd[1], &statep->id, sizeof (statep->id));
+ log_trace ("Thread %d: waiting for cond_var.\n",
+ (int) statep->id);
+
+ /* Wait for a command. */
+ statep->status = STAT_WAITCONDVAR;
+
+ /*
+ * The threads are allowed commanded to wait either on
+ * their own unique condition variable (so they may be
+ * separately signaled) or on one global condition variable
+ * (so they may be signaled together).
+ */
+ if (use_global_condvar != 0)
+ pthread_cond_wait (&cond_var, &cond_mutex);
+ else
+ pthread_cond_wait (&statep->cond_var, &cond_mutex);
+
+ /* Do we report our status? */
+ if (statep->flags & FLAGS_REPORT_WAITCONDVAR) {
+ write (pipefd[1], &statep->id, sizeof (statep->id));
+ log_trace ("Thread %d: wrote %d to pipe.\n",
+ (int) statep->id);
+ }
+ log_trace ("Thread %d: received cond_var signal.\n",
+ (int) statep->id);
+
+ /* Get a copy of the command before releasing the mutex. */
+ cmd = statep->cmd;
+
+ /* Clear the command after copying it. */
+ statep->cmd.cmd_id = CMD_NONE;
+
+ /* Unlock the condition variable mutex. */
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+
+ /* Peform the command.*/
+ switch (cmd.cmd_id) {
+ case CMD_TAKE_MUTEX:
+ statep->ret = pthread_mutex_lock (cmd.mutex);
+ if (statep->ret == 0) {
+ assert (mutex_count < sizeof (held_mutex));
+ held_mutex[mutex_count] = cmd.mutex;
+ held_mutex_owned[mutex_count] = 1;
+ mutex_count++;
+ }
+ else {
+ held_mutex_owned[mutex_count] = 0;
+ log_trace ("Thread id %d unable to lock mutex, "
+ "error = %d\n", (int) statep->id,
+ statep->ret);
+ }
+ break;
+
+ case CMD_RELEASE_MUTEX:
+ assert ((mutex_count <= sizeof (held_mutex)) &&
+ (mutex_count > 0));
+ mutex_count--;
+ if (held_mutex_owned[mutex_count] != 0)
+ assert (pthread_mutex_unlock
+ (held_mutex[mutex_count]) == 0);
+ break;
+
+ case CMD_WAIT_FOR_SIGNAL:
+ assert (pthread_mutex_lock (cmd.mutex) == 0);
+ assert (pthread_cond_wait (cmd.cond, cmd.mutex) == 0);
+ assert (pthread_mutex_unlock (cmd.mutex) == 0);
+ break;
+
+ case CMD_BUSY_LOOP:
+ log_trace ("Thread %d: Entering busy loop.\n",
+ (int) statep->id);
+ /* Spin for 15 seconds. */
+ assert (gettimeofday (&tv2, NULL) == 0);
+ tv1.tv_sec = tv2.tv_sec + 5;
+ tv1.tv_usec = tv2.tv_usec;
+ statep->flags |= FLAGS_IS_BUSY;
+ while (timercmp (&tv2, &tv1,<)) {
+ assert (gettimeofday (&tv2, NULL) == 0);
+ }
+ statep->flags &= ~FLAGS_IS_BUSY;
+ statep->flags |= FLAGS_WAS_BUSY;
+
+ /* Do we report our status? */
+ if (statep->flags & FLAGS_REPORT_BUSY_LOOP)
+ write (pipefd[1], &statep->id,
+ sizeof (statep->id));
+
+ log_trace ("Thread %d: Leaving busy loop.\n",
+ (int) statep->id);
+ break;
+
+ case CMD_PROTECTED_OP:
+ assert (pthread_mutex_lock (cmd.mutex) == 0);
+ statep->flags |= FLAGS_WAS_BUSY;
+ /* Do we report our status? */
+ if (statep->flags & FLAGS_REPORT_BUSY_LOOP)
+ write (pipefd[1], &statep->id,
+ sizeof (statep->id));
+
+ assert (pthread_mutex_unlock (cmd.mutex) == 0);
+ break;
+
+ case CMD_RELEASE_ALL:
+ assert ((mutex_count <= sizeof (held_mutex)) &&
+ (mutex_count > 0));
+ for (i = mutex_count - 1; i >= 0; i--) {
+ if (held_mutex_owned[i] != 0)
+ assert (pthread_mutex_unlock
+ (held_mutex[i]) == 0);
+ }
+ mutex_count = 0;
+ break;
+
+ case CMD_NONE:
+ default:
+ break;
+ }
+
+ /* Wait for the big giant waiter lock. */
+ statep->status = STAT_WAITMUTEX;
+ log_trace ("Thread %d: waiting for big giant lock.\n",
+ (int) statep->id);
+ pthread_mutex_lock (&waiter_mutex);
+ if (statep->flags & FLAGS_REPORT_WAITMUTEX)
+ write (pipefd[1], &statep->id, sizeof (statep->id));
+ log_trace ("Thread %d: got big giant lock.\n",
+ (int) statep->id);
+ statep->status = STAT_INITIAL;
+ pthread_mutex_unlock (&waiter_mutex);
+ }
+
+ log_trace ("Thread %d: Exiting thread 0x%x\n", (int) statep->id,
+ (int) pthread_self());
+ pthread_exit (arg);
+ return (NULL);
+}
+
+
+static void *
+lock_twice (void *arg)
+{
+ thread_state_t *statep = (thread_state_t *) arg;
+ sigset_t mask;
+
+ statep->status = STAT_INITIAL;
+
+ /* Block all signals except for interrupt.*/
+ sigfillset (&mask);
+ sigdelset (&mask, SIGINT);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ /* Wait for a signal to continue. */
+ log_trace ("Thread %d: locking cond_mutex.\n", (int) statep->id);
+ pthread_mutex_lock (&cond_mutex);
+
+ log_trace ("Thread %d: waiting for cond_var.\n", (int) statep->id);
+ statep->status = STAT_WAITCONDVAR;
+ pthread_cond_wait (&cond_var, &cond_mutex);
+
+ log_trace ("Thread %d: received cond_var signal.\n", (int) statep->id);
+
+ /* Unlock the condition variable mutex. */
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+
+ statep->status = STAT_WAITMUTEX;
+ /* Lock the mutex once. */
+ assert (pthread_mutex_lock (statep->cmd.mutex) == 0);
+
+ /* Lock it again and capture the error. */
+ statep->ret = pthread_mutex_lock (statep->cmd.mutex);
+ statep->status = 0;
+
+ assert (pthread_mutex_unlock (statep->cmd.mutex) == 0);
+
+ /* Unlock it again if it is locked recursively. */
+ if (statep->ret == 0)
+ pthread_mutex_unlock (statep->cmd.mutex);
+
+ log_trace ("Thread %d: Exiting thread 0x%x\n", (int) statep->id,
+ (int) pthread_self());
+ pthread_exit (arg);
+ return (NULL);
+}
+
+
+static void
+sighandler (int signo)
+{
+ log ("Signal handler caught signal %d, thread id 0x%x\n",
+ signo, (int) pthread_self());
+
+ if (signo == SIGINT)
+ done = 1;
+}
+
+
+static void
+send_cmd (int id, thread_cmd_id_t cmd)
+{
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ assert (states[id].status == STAT_WAITCONDVAR);
+ states[id].cmd.cmd_id = cmd;
+ states[id].cmd.mutex = NULL;
+ states[id].cmd.cond = NULL;
+ /* Clear the busy flags. */
+ states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY);
+ assert (pthread_cond_signal (&states[id].cond_var) == 0);
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+}
+
+
+static void
+send_mutex_cmd (int id, thread_cmd_id_t cmd, pthread_mutex_t *m)
+{
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ assert (states[id].status == STAT_WAITCONDVAR);
+ states[id].cmd.cmd_id = cmd;
+ states[id].cmd.mutex = m;
+ states[id].cmd.cond = NULL;
+ /* Clear the busy flags. */
+ states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY);
+ assert (pthread_cond_signal (&states[id].cond_var) == 0);
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+}
+
+
+static void
+send_mutex_cv_cmd (int id, thread_cmd_id_t cmd, pthread_mutex_t *m,
+ pthread_cond_t *cv)
+{
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ assert (states[id].status == STAT_WAITCONDVAR);
+ states[id].cmd.cmd_id = cmd;
+ states[id].cmd.mutex = m;
+ states[id].cmd.cond = cv;
+ /* Clear the busy flags. */
+ states[id].flags &= ~(FLAGS_WAS_BUSY | FLAGS_IS_BUSY);
+ assert (pthread_cond_signal (&states[id].cond_var) == 0);
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+}
+
+
+static void
+mutex_init_test (void)
+{
+ pthread_mutexattr_t mattr;
+ pthread_mutex_t mutex;
+ mutex_kind_t mkind;
+ int mproto, ret;
+
+ /*
+ * Initialize a mutex attribute.
+ *
+ * pthread_mutexattr_init not tested for: ENOMEM
+ */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+
+ /*
+ * Initialize a mutex.
+ *
+ * pthread_mutex_init not tested for: EAGAIN ENOMEM EPERM EBUSY
+ */
+ log ("Testing pthread_mutex_init\n");
+ log ("--------------------------\n");
+
+ for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
+ for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
+ /* Initialize the mutex attribute. */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+ assert (pthread_mutexattr_setprotocol (&mattr,
+ protocols[mproto]) == 0);
+
+ /*
+ * Ensure that the first mutex type is a POSIX
+ * compliant mutex.
+ */
+ if (mkind != M_POSIX) {
+ assert (pthread_mutexattr_settype (&mattr,
+ mutex_types[mkind]) == 0);
+ }
+
+ log (" Protocol %s, Type %s - ",
+ protocol_strs[mproto], mutextype_strs[mkind]);
+ ret = pthread_mutex_init (&mutex, &mattr);
+ check_result (/* expected */ 0, ret);
+ assert (pthread_mutex_destroy (&mutex) == 0);
+
+ /*
+ * Destroy a mutex attribute.
+ *
+ * XXX - There should probably be a magic number
+ * associated with a mutex attribute so that
+ * destroy can be reasonably sure the attribute
+ * is valid.
+ *
+ * pthread_mutexattr_destroy not tested for: EINVAL
+ */
+ assert (pthread_mutexattr_destroy (&mattr) == 0);
+ }
+ }
+}
+
+
+static void
+mutex_destroy_test (void)
+{
+ pthread_mutexattr_t mattr;
+ pthread_mutex_t mutex;
+ pthread_condattr_t cattr;
+ pthread_cond_t cv;
+ pthread_attr_t pattr;
+ int mproto, ret;
+ mutex_kind_t mkind;
+ thread_state_t state;
+
+ /*
+ * Destroy a mutex.
+ *
+ * XXX - There should probably be a magic number associated
+ * with a mutex so that destroy can be reasonably sure
+ * the mutex is valid.
+ *
+ * pthread_mutex_destroy not tested for:
+ */
+ log ("Testing pthread_mutex_destroy\n");
+ log ("-----------------------------\n");
+
+ assert (pthread_attr_init (&pattr) == 0);
+ assert (pthread_attr_setdetachstate (&pattr,
+ PTHREAD_CREATE_DETACHED) == 0);
+ state.flags = 0; /* No flags yet. */
+
+ for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
+ for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
+ /* Initialize the mutex attribute. */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+ assert (pthread_mutexattr_setprotocol (&mattr,
+ protocols[mproto]) == 0);
+
+ /*
+ * Ensure that the first mutex type is a POSIX
+ * compliant mutex.
+ */
+ if (mkind != M_POSIX) {
+ assert (pthread_mutexattr_settype (&mattr,
+ mutex_types[mkind]) == 0);
+ }
+
+ /* Create the mutex. */
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+
+ log (" Protocol %s, Type %s\n",
+ protocol_strs[mproto], mutextype_strs[mkind]);
+
+ log (" Destruction of unused mutex - ");
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+ ret = pthread_mutex_destroy (&mutex);
+ check_result (/* expected */ 0, ret);
+
+ log (" Destruction of mutex locked by self - ");
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+ assert (pthread_mutex_lock (&mutex) == 0);
+ ret = pthread_mutex_destroy (&mutex);
+ check_result (/* expected */ EBUSY, ret);
+ assert (pthread_mutex_unlock (&mutex) == 0);
+ assert (pthread_mutex_destroy (&mutex) == 0);
+
+ log (" Destruction of mutex locked by another "
+ "thread - ");
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+ send_mutex_cmd (0, CMD_TAKE_MUTEX, &mutex);
+ sleep (1);
+ ret = pthread_mutex_destroy (&mutex);
+ check_result (/* expected */ EBUSY, ret);
+ send_cmd (0, CMD_RELEASE_ALL);
+ sleep (1);
+ assert (pthread_mutex_destroy (&mutex) == 0);
+
+ log (" Destruction of mutex while being used in "
+ "cond_wait - ");
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+ assert (pthread_condattr_init (&cattr) == 0);
+ assert (pthread_cond_init (&cv, &cattr) == 0);
+ send_mutex_cv_cmd (0, CMD_WAIT_FOR_SIGNAL, &mutex, &cv);
+ sleep (1);
+ ret = pthread_mutex_destroy (&mutex);
+ check_result (/* expected */ EBUSY, ret);
+ pthread_cond_signal (&cv);
+ sleep (1);
+ assert (pthread_mutex_destroy (&mutex) == 0);
+ }
+ }
+}
+
+
+static void
+mutex_lock_test (void)
+{
+ pthread_mutexattr_t mattr;
+ pthread_mutex_t mutex;
+ pthread_attr_t pattr;
+ int mproto, ret;
+ mutex_kind_t mkind;
+ thread_state_t state;
+
+ /*
+ * Lock a mutex.
+ *
+ * pthread_lock not tested for:
+ */
+ log ("Testing pthread_mutex_lock\n");
+ log ("--------------------------\n");
+
+ assert (pthread_attr_init (&pattr) == 0);
+ assert (pthread_attr_setdetachstate (&pattr,
+ PTHREAD_CREATE_DETACHED) == 0);
+ state.flags = 0; /* No flags yet. */
+
+ for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
+ for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
+ /* Initialize the mutex attribute. */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+ assert (pthread_mutexattr_setprotocol (&mattr,
+ protocols[mproto]) == 0);
+
+ /*
+ * Ensure that the first mutex type is a POSIX
+ * compliant mutex.
+ */
+ if (mkind != M_POSIX) {
+ assert (pthread_mutexattr_settype (&mattr,
+ mutex_types[mkind]) == 0);
+ }
+
+ /* Create the mutex. */
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+
+ log (" Protocol %s, Type %s\n",
+ protocol_strs[mproto], mutextype_strs[mkind]);
+
+ log (" Lock on unlocked mutex - ");
+ ret = pthread_mutex_lock (&mutex);
+ check_result (/* expected */ 0, ret);
+ pthread_mutex_unlock (&mutex);
+
+ log (" Lock on invalid mutex - ");
+ ret = pthread_mutex_lock (NULL);
+ check_result (/* expected */ EINVAL, ret);
+
+ log (" Lock on mutex held by self - ");
+ assert (pthread_create (&state.tid, &pattr, lock_twice,
+ (void *) &state) == 0);
+ /* Let the thread start. */
+ sleep (1);
+ state.cmd.mutex = &mutex;
+ state.ret = 0xdeadbeef;
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ assert (pthread_cond_signal (&cond_var) == 0);
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+ /* Let the thread receive and process the command. */
+ sleep (1);
+
+ switch (mkind) {
+ case M_POSIX:
+ check_result (/* expected */ EDEADLK,
+ state.ret);
+ break;
+ case M_SS2_DEFAULT:
+ check_result (/* expected */ EDEADLK,
+ state.ret);
+ break;
+ case M_SS2_ERRORCHECK:
+ check_result (/* expected */ EDEADLK,
+ state.ret);
+ break;
+ case M_SS2_NORMAL:
+ check_result (/* expected */ 0xdeadbeef,
+ state.ret);
+ break;
+ case M_SS2_RECURSIVE:
+ check_result (/* expected */ 0, state.ret);
+ break;
+ }
+ pthread_mutex_destroy (&mutex);
+ pthread_mutexattr_destroy (&mattr);
+ }
+ }
+}
+
+
+static void
+mutex_unlock_test (void)
+{
+ const int test_thread_id = 0; /* ID of test thread */
+ pthread_mutexattr_t mattr;
+ pthread_mutex_t mutex;
+ int mproto, ret;
+ mutex_kind_t mkind;
+
+ /*
+ * Unlock a mutex.
+ *
+ * pthread_unlock not tested for:
+ */
+ log ("Testing pthread_mutex_unlock\n");
+ log ("----------------------------\n");
+
+ for (mproto = 0; mproto < NELEMENTS(protocols); mproto++) {
+ for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
+ /* Initialize the mutex attribute. */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+ assert (pthread_mutexattr_setprotocol (&mattr,
+ protocols[mproto]) == 0);
+
+ /*
+ * Ensure that the first mutex type is a POSIX
+ * compliant mutex.
+ */
+ if (mkind != M_POSIX) {
+ assert (pthread_mutexattr_settype (&mattr,
+ mutex_types[mkind]) == 0);
+ }
+
+ /* Create the mutex. */
+ assert (pthread_mutex_init (&mutex, &mattr) == 0);
+
+ log (" Protocol %s, Type %s\n",
+ protocol_strs[mproto], mutextype_strs[mkind]);
+
+ log (" Unlock on mutex held by self - ");
+ assert (pthread_mutex_lock (&mutex) == 0);
+ ret = pthread_mutex_unlock (&mutex);
+ check_result (/* expected */ 0, ret);
+
+ log (" Unlock on invalid mutex - ");
+ ret = pthread_mutex_unlock (NULL);
+ check_result (/* expected */ EINVAL, ret);
+
+ log (" Unlock on mutex locked by another thread - ");
+ send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &mutex);
+ sleep (1);
+ ret = pthread_mutex_unlock (&mutex);
+ switch (mkind) {
+ case M_POSIX:
+ check_result (/* expected */ EPERM, ret);
+ break;
+ case M_SS2_DEFAULT:
+ check_result (/* expected */ EPERM, ret);
+ break;
+ case M_SS2_ERRORCHECK:
+ check_result (/* expected */ EPERM, ret);
+ break;
+ case M_SS2_NORMAL:
+ check_result (/* expected */ EPERM, ret);
+ break;
+ case M_SS2_RECURSIVE:
+ check_result (/* expected */ EPERM, ret);
+ break;
+ }
+ if (ret == 0) {
+ /*
+ * If for some reason we were able to unlock
+ * the mutex, relock it so that the test
+ * thread has no problems releasing the mutex.
+ */
+ pthread_mutex_lock (&mutex);
+ }
+ send_cmd (test_thread_id, CMD_RELEASE_ALL);
+ sleep (1);
+
+ pthread_mutex_destroy (&mutex);
+ pthread_mutexattr_destroy (&mattr);
+ }
+ }
+}
+
+
+static void
+queueing_order_test (void)
+{
+ int i;
+
+ log ("Testing queueing order\n");
+ log ("----------------------\n");
+ assert (pthread_mutex_lock (&waiter_mutex) == 0);
+ /*
+ * Tell the threads to report when they take the waiters mutex.
+ */
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ for (i = 0; i < NUM_THREADS; i++) {
+ states[i].flags = FLAGS_REPORT_WAITMUTEX;
+ assert (pthread_cond_signal (&states[i].cond_var) == 0);
+ }
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+
+ /* Signal the threads to continue. */
+ sleep (1);
+
+ /* Use the global condition variable next time. */
+ use_global_condvar = 1;
+
+ /* Release the waiting threads and allow them to run again. */
+ assert (pthread_mutex_unlock (&waiter_mutex) == 0);
+ sleep (1);
+
+ log (" Queueing order on a mutex - ");
+ check_run_order ("9,8,7,6,5,4,3,2,1,0");
+ for (i = 0; i < NUM_THREADS; i = i + 1) {
+ /* Tell the threads to report when they've been signaled. */
+ states[i].flags = FLAGS_REPORT_WAITCONDVAR;
+ }
+
+ /*
+ * Prevent the threads from continuing their loop after we
+ * signal them.
+ */
+ assert (pthread_mutex_lock (&waiter_mutex) == 0);
+
+
+ log (" Queueing order on a condition variable - ");
+ /*
+ * Signal one thread to run and see that the highest priority
+ * thread executes.
+ */
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ assert (pthread_cond_signal (&cond_var) == 0);
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+ sleep (1);
+ if (states[NUM_THREADS - 1].status != STAT_WAITMUTEX)
+ log_error ("highest priority thread does not run.\n");
+
+ /* Signal the remaining threads. */
+ assert (pthread_mutex_lock (&cond_mutex) == 0);
+ assert (pthread_cond_broadcast (&cond_var) == 0);
+ assert (pthread_mutex_unlock (&cond_mutex) == 0);
+ sleep (1);
+
+ check_run_order ("9,8,7,6,5,4,3,2,1,0");
+ for (i = 0; i < NUM_THREADS; i = i + 1) {
+ /* Tell the threads not to report anything. */
+ states[i].flags = 0;
+ }
+
+ /* Use the thread unique condition variable next time. */
+ use_global_condvar = 0;
+
+ /* Allow the threads to continue their loop. */
+ assert (pthread_mutex_unlock (&waiter_mutex) == 0);
+ sleep (1);
+}
+
+
+static void
+mutex_prioceiling_test (void)
+{
+ const int test_thread_id = 0; /* ID of test thread */
+ pthread_mutexattr_t mattr;
+ struct sched_param param;
+ pthread_mutex_t m[3];
+ mutex_kind_t mkind;
+ int i, ret, policy, my_prio, old_ceiling;
+
+ log ("Testing priority ceilings\n");
+ log ("-------------------------\n");
+ for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
+
+ log (" Protype PTHREAD_PRIO_PROTECT, Type %s\n",
+ mutextype_strs[mkind]);
+
+ /*
+ * Initialize and create a mutex.
+ */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+
+ /* Get this threads current priority. */
+ assert (pthread_getschedparam (pthread_self(), &policy,
+ &param) == 0);
+ my_prio = param.sched_priority; /* save for later use */
+ log_trace ("Current scheduling policy %d, priority %d\n",
+ policy, my_prio);
+
+ /*
+ * Initialize and create 3 priority protection mutexes with
+ * default (max priority) ceilings.
+ */
+ assert (pthread_mutexattr_setprotocol(&mattr,
+ PTHREAD_PRIO_PROTECT) == 0);
+
+ /*
+ * Ensure that the first mutex type is a POSIX
+ * compliant mutex.
+ */
+ if (mkind != M_POSIX) {
+ assert (pthread_mutexattr_settype (&mattr,
+ mutex_types[mkind]) == 0);
+ }
+
+ for (i = 0; i < 3; i++)
+ assert (pthread_mutex_init (&m[i], &mattr) == 0);
+
+ /*
+ * Set the ceiling priorities for the 3 priority protection
+ * mutexes to, 5 less than, equal to, and 5 greater than,
+ * this threads current priority.
+ */
+ for (i = 0; i < 3; i++)
+ assert (pthread_mutex_setprioceiling (&m[i],
+ my_prio - 5 + 5*i, &old_ceiling) == 0);
+
+ /*
+ * Check that if we attempt to take a mutex whose priority
+ * ceiling is lower than our priority, we get an error.
+ */
+ log (" Lock with ceiling priority < thread priority - ");
+ ret = pthread_mutex_lock (&m[0]);
+ check_result (/* expected */ EINVAL, ret);
+ if (ret == 0)
+ pthread_mutex_unlock (&m[0]);
+
+ /*
+ * Check that we can take a mutex whose priority ceiling
+ * is equal to our priority.
+ */
+ log (" Lock with ceiling priority = thread priority - ");
+ ret = pthread_mutex_lock (&m[1]);
+ check_result (/* expected */ 0, ret);
+ if (ret == 0)
+ pthread_mutex_unlock (&m[1]);
+
+ /*
+ * Check that we can take a mutex whose priority ceiling
+ * is higher than our priority.
+ */
+ log (" Lock with ceiling priority > thread priority - ");
+ ret = pthread_mutex_lock (&m[2]);
+ check_result (/* expected */ 0, ret);
+ if (ret == 0)
+ pthread_mutex_unlock (&m[2]);
+
+ /*
+ * Have the test thread go into a busy loop for 5 seconds
+ * and see that it doesn't block this thread (since the
+ * priority ceiling of mutex 0 and the priority of the test
+ * thread are both less than the priority of this thread).
+ */
+ log (" Preemption with ceiling priority < thread "
+ "priority - ");
+ /* Have the test thread take mutex 0. */
+ send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[0]);
+ sleep (1);
+
+ log_trace ("Sending busy command.\n");
+ send_cmd (test_thread_id, CMD_BUSY_LOOP);
+ log_trace ("Busy sent, yielding\n");
+ pthread_yield ();
+ log_trace ("Returned from yield.\n");
+ if (states[test_thread_id].flags &
+ (FLAGS_IS_BUSY | FLAGS_WAS_BUSY))
+ log_error ("test thread inproperly preempted us.\n");
+ else {
+ /* Let the thread finish its busy loop. */
+ sleep (6);
+ if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0)
+ log_error ("test thread never finished.\n");
+ else
+ log_pass ();
+ }
+ states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
+
+ /* Have the test thread release mutex 0. */
+ send_cmd (test_thread_id, CMD_RELEASE_ALL);
+ sleep (1);
+
+ /*
+ * Have the test thread go into a busy loop for 5 seconds
+ * and see that it preempts this thread (since the priority
+ * ceiling of mutex 1 is the same as the priority of this
+ * thread). The test thread should not run to completion
+ * as its time quantum should expire before the 5 seconds
+ * are up.
+ */
+ log (" Preemption with ceiling priority = thread "
+ "priority - ");
+
+ /* Have the test thread take mutex 1. */
+ send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[1]);
+ sleep (1);
+
+ log_trace ("Sending busy\n");
+ send_cmd (test_thread_id, CMD_BUSY_LOOP);
+ log_trace ("Busy sent, yielding\n");
+ pthread_yield ();
+ log_trace ("Returned from yield.\n");
+ if ((states[test_thread_id].flags & FLAGS_IS_BUSY) == 0)
+ log_error ("test thread did not switch in on yield.\n");
+ else if (states[test_thread_id].flags & FLAGS_WAS_BUSY)
+ log_error ("test thread ran to completion.\n");
+ else {
+ /* Let the thread finish its busy loop. */
+ sleep (6);
+ if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0)
+ log_error ("test thread never finished.\n");
+ else
+ log_pass ();
+ }
+ states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
+
+ /* Have the test thread release mutex 1. */
+ send_cmd (test_thread_id, CMD_RELEASE_ALL);
+ sleep (1);
+
+ /*
+ * Set the scheduling policy of the test thread to SCHED_FIFO
+ * and have it go into a busy loop for 5 seconds. This
+ * thread is SCHED_RR, and since the priority ceiling of
+ * mutex 1 is the same as the priority of this thread, the
+ * test thread should run to completion once it is switched
+ * in.
+ */
+ log (" SCHED_FIFO scheduling and ceiling priority = "
+ "thread priority - ");
+ param.sched_priority = states[test_thread_id].priority;
+ assert (pthread_setschedparam (states[test_thread_id].tid,
+ SCHED_FIFO, &param) == 0);
+
+ /* Have the test thread take mutex 1. */
+ send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[1]);
+ sleep (1);
+
+ log_trace ("Sending busy\n");
+ send_cmd (test_thread_id, CMD_BUSY_LOOP);
+ log_trace ("Busy sent, yielding\n");
+ pthread_yield ();
+ log_trace ("Returned from yield.\n");
+ if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0) {
+ log_error ("test thread did not run to completion.\n");
+ /* Let the thread finish it's busy loop. */
+ sleep (6);
+ }
+ else
+ log_pass ();
+ states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
+
+ /* Restore the test thread scheduling parameters. */
+ param.sched_priority = states[test_thread_id].priority;
+ assert (pthread_setschedparam (states[test_thread_id].tid,
+ SCHED_RR, &param) == 0);
+
+ /* Have the test thread release mutex 1. */
+ send_cmd (test_thread_id, CMD_RELEASE_ALL);
+ sleep (1);
+
+ /*
+ * Have the test thread go into a busy loop for 5 seconds
+ * and see that it preempts this thread (since the priority
+ * ceiling of mutex 2 is the greater than the priority of
+ * this thread). The test thread should run to completion
+ * and block this thread because its active priority is
+ * higher.
+ */
+ log (" SCHED_FIFO scheduling and ceiling priority > "
+ "thread priority - ");
+ /* Have the test thread take mutex 2. */
+ send_mutex_cmd (test_thread_id, CMD_TAKE_MUTEX, &m[2]);
+ sleep (1);
+
+ log_trace ("Sending busy\n");
+ send_cmd (test_thread_id, CMD_BUSY_LOOP);
+ log_trace ("Busy sent, yielding\n");
+ pthread_yield ();
+ log_trace ("Returned from yield.\n");
+ if ((states[test_thread_id].flags & FLAGS_IS_BUSY) != 0) {
+ log_error ("test thread did not run to completion.\n");
+ /* Let the thread finish it's busy loop. */
+ sleep (6);
+ }
+ else if ((states[test_thread_id].flags & FLAGS_WAS_BUSY) == 0)
+ log_error ("test thread never finished.\n");
+ else
+ log_pass ();
+ states[test_thread_id].flags &= ~FLAGS_WAS_BUSY;
+
+ /* Have the test thread release mutex 2. */
+ send_cmd (test_thread_id, CMD_RELEASE_ALL);
+ sleep (1);
+
+ /* Destroy the mutexes. */
+ for (i = 0; i < 3; i++)
+ assert (pthread_mutex_destroy (&m[i]) == 0);
+ }
+}
+
+
+static void
+mutex_prioinherit_test (void)
+{
+ pthread_mutexattr_t mattr;
+ struct sched_param param;
+ pthread_mutex_t m[3];
+ mutex_kind_t mkind;
+ int i, policy, my_prio;
+
+ /* Get this threads current priority. */
+ assert (pthread_getschedparam (pthread_self(), &policy,
+ &param) == 0);
+ my_prio = param.sched_priority; /* save for later use */
+ log_trace ("Current scheduling policy %d, priority %d\n",
+ policy, my_prio);
+
+ log ("Testing priority inheritence\n");
+ log ("----------------------------\n");
+ for (mkind = M_POSIX; mkind <= M_SS2_RECURSIVE; mkind++) {
+
+ log (" Protype PTHREAD_PRIO_INHERIT, Type %s\n",
+ mutextype_strs[mkind]);
+
+ /*
+ * Initialize and create a mutex.
+ */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+
+ /*
+ * Initialize and create 3 priority inheritence mutexes with
+ * default (max priority) ceilings.
+ */
+ assert (pthread_mutexattr_setprotocol(&mattr,
+ PTHREAD_PRIO_INHERIT) == 0);
+
+ /*
+ * Ensure that the first mutex type is a POSIX
+ * compliant mutex.
+ */
+ if (mkind != M_POSIX) {
+ assert (pthread_mutexattr_settype (&mattr,
+ mutex_types[mkind]) == 0);
+ }
+
+ for (i = 0; i < 3; i++)
+ assert (pthread_mutex_init (&m[i], &mattr) == 0);
+
+ /*
+ * Test setup:
+ * Thread 4 - take mutex 0, 1
+ * Thread 2 - enter protected busy loop with mutex 0
+ * Thread 3 - enter protected busy loop with mutex 1
+ * Thread 4 - enter protected busy loop with mutex 2
+ * Thread 5 - enter busy loop
+ * Thread 6 - enter protected busy loop with mutex 0
+ * Thread 4 - releases mutexes 1 and 0.
+ *
+ * Expected results:
+ * Threads complete in order 4, 6, 5, 3, 2
+ */
+ log (" Simple inheritence test - ");
+
+ /*
+ * Command thread 4 to take mutexes 0 and 1.
+ */
+ send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]);
+ sleep (1); /* Allow command to be received. */
+ send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[1]);
+ sleep (1);
+
+ /*
+ * Tell the threads to report themselves when they are
+ * at the bottom of their loop (waiting on wait_mutex).
+ */
+ for (i = 0; i < NUM_THREADS; i++)
+ states[i].flags |= FLAGS_REPORT_WAITMUTEX;
+
+ /*
+ * Command thread 2 to take mutex 0 and thread 3 to take
+ * mutex 1, both via a protected operation command. Since
+ * thread 4 owns mutexes 0 and 1, both threads 2 and 3
+ * will block until the mutexes are released by thread 4.
+ */
+ log_trace ("Commanding protected operation to thread 2.\n");
+ send_mutex_cmd (2, CMD_PROTECTED_OP, &m[0]);
+ log_trace ("Commanding protected operation to thread 3.\n");
+ send_mutex_cmd (3, CMD_PROTECTED_OP, &m[1]);
+ sleep (1);
+
+ /*
+ * Command thread 4 to take mutex 2 via a protected operation
+ * and thread 5 to enter a busy loop for 5 seconds. Since
+ * thread 5 has higher priority than thread 4, thread 5 will
+ * enter the busy loop before thread 4 is activated.
+ */
+ log_trace ("Commanding protected operation to thread 4.\n");
+ send_mutex_cmd (4, CMD_PROTECTED_OP, &m[2]);
+ log_trace ("Commanding busy loop to thread 5.\n");
+ send_cmd (5, CMD_BUSY_LOOP);
+ sleep (1);
+ if ((states[5].flags & FLAGS_IS_BUSY) == 0)
+ log_error ("thread 5 is not running.\n");
+ log_trace ("Commanding protected operation thread 6.\n");
+ send_mutex_cmd (6, CMD_PROTECTED_OP, &m[0]);
+ sleep (1);
+ if ((states[4].flags & FLAGS_WAS_BUSY) == 0)
+ log_error ("thread 4 failed to inherit priority.\n");
+ states[4].flags = 0;
+ send_cmd (4, CMD_RELEASE_ALL);
+ sleep (5);
+ check_run_order ("4,6,5,3,2");
+
+ /*
+ * Clear the flags.
+ */
+ for (i = 0; i < NUM_THREADS; i++)
+ states[i].flags = 0;
+
+ /*
+ * Test setup:
+ * Thread 2 - enter busy loop (SCHED_FIFO)
+ * Thread 4 - take mutex 0
+ * Thread 4 - priority change to same priority as thread 2
+ * Thread 4 - release mutex 0
+ *
+ * Expected results:
+ * Since thread 4 owns a priority mutex, it should be
+ * placed at the front of the run queue (for its new
+ * priority slot) when its priority is lowered to the
+ * same priority as thread 2. If thread 4 did not own
+ * a priority mutex, then it would have been added to
+ * the end of the run queue and thread 2 would have
+ * executed until it blocked (because it's scheduling
+ * policy is SCHED_FIFO).
+ *
+ */
+ log (" Inheritence test with change of priority - ");
+
+ /*
+ * Change threads 2 and 4 scheduling policies to be
+ * SCHED_FIFO.
+ */
+ param.sched_priority = states[2].priority;
+ assert (pthread_setschedparam (states[2].tid, SCHED_FIFO,
+ &param) == 0);
+ param.sched_priority = states[4].priority;
+ assert (pthread_setschedparam (states[4].tid, SCHED_FIFO,
+ &param) == 0);
+
+ /*
+ * Command thread 4 to take mutex 0.
+ */
+ send_mutex_cmd (4, CMD_TAKE_MUTEX, &m[0]);
+ sleep (1);
+
+ /*
+ * Command thread 2 to enter busy loop.
+ */
+ send_cmd (2, CMD_BUSY_LOOP);
+ sleep (1); /* Allow command to be received. */
+
+ /*
+ * Command thread 4 to enter busy loop.
+ */
+ send_cmd (4, CMD_BUSY_LOOP);
+ sleep (1); /* Allow command to be received. */
+
+ /* Have threads 2 and 4 report themselves. */
+ states[2].flags = FLAGS_REPORT_WAITMUTEX;
+ states[4].flags = FLAGS_REPORT_WAITMUTEX;
+
+ /* Change the priority of thread 4. */
+ param.sched_priority = states[2].priority;
+ assert (pthread_setschedparam (states[4].tid, SCHED_FIFO,
+ &param) == 0);
+ sleep (5);
+ check_run_order ("4,2");
+
+ /* Clear the flags */
+ states[2].flags = 0;
+ states[4].flags = 0;
+
+ /* Reset the policies. */
+ param.sched_priority = states[2].priority;
+ assert (pthread_setschedparam (states[2].tid, SCHED_RR,
+ &param) == 0);
+ param.sched_priority = states[4].priority;
+ assert (pthread_setschedparam (states[4].tid, SCHED_RR,
+ &param) == 0);
+
+ send_cmd (4, CMD_RELEASE_MUTEX);
+ sleep (1);
+
+ /* Destroy the mutexes. */
+ for (i = 0; i < 3; i++)
+ assert (pthread_mutex_destroy (&m[i]) == 0);
+ }
+}
+
+
+int main (int argc, char *argv[])
+{
+ pthread_mutexattr_t mattr;
+ pthread_condattr_t cattr;
+ pthread_attr_t pattr;
+ int i, policy, main_prio;
+ void * exit_status;
+ sigset_t mask;
+ struct sigaction act;
+ struct sched_param param;
+
+ assert (pthread_getschedparam (pthread_self (), &policy, &param) == 0);
+ main_prio = param.sched_priority;
+
+ /* Setupt our signal mask. */
+ sigfillset (&mask);
+ sigdelset (&mask, SIGINT);
+ sigprocmask (SIG_SETMASK, &mask, NULL);
+
+ /* Install a signal handler for SIGINT */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGINT);
+ act.sa_handler = sighandler;
+ act.sa_flags = SA_RESTART;
+ sigaction (SIGINT, &act, NULL);
+
+ /*
+ * Initialize the thread attribute.
+ */
+ assert (pthread_attr_init (&pattr) == 0);
+ assert (pthread_attr_setdetachstate (&pattr,
+ PTHREAD_CREATE_JOINABLE) == 0);
+
+ /*
+ * Initialize and create the waiter and condvar mutexes.
+ */
+ assert (pthread_mutexattr_init (&mattr) == 0);
+ assert (pthread_mutex_init (&waiter_mutex, &mattr) == 0);
+ assert (pthread_mutex_init (&cond_mutex, &mattr) == 0);
+
+ /*
+ * Initialize and create a condition variable.
+ */
+ assert (pthread_condattr_init (&cattr) == 0);
+ assert (pthread_cond_init (&cond_var, &cattr) == 0);
+
+ /* Create a pipe to catch the results of thread wakeups. */
+ assert (pipe (pipefd) == 0);
+
+#ifdef DEBUG
+ assert (pthread_switch_add_np (kern_switch) == 0);
+#endif
+
+ /*
+ * Create the waiting threads.
+ */
+ for (i = 0; i < NUM_THREADS; i++) {
+ assert (pthread_cond_init (&states[i].cond_var, &cattr) == 0);
+ states[i].id = (u_int8_t) i; /* NUM_THREADS must be <= 256 */
+ states[i].status = 0;
+ states[i].cmd.cmd_id = CMD_NONE;
+ states[i].flags = 0; /* No flags yet. */
+ assert (pthread_create (&states[i].tid, &pattr, waiter,
+ (void *) &states[i]) == 0);
+ param.sched_priority = main_prio - 10 + i;
+ states[i].priority = param.sched_priority;
+ assert (pthread_setschedparam (states[i].tid, SCHED_OTHER,
+ &param) == 0);
+#if defined(_LIBC_R_)
+ {
+ char buf[30];
+
+ snprintf (buf, sizeof(buf), "waiter_%d", i);
+ pthread_set_name_np (states[i].tid, buf);
+ }
+#endif
+ }
+
+ /* Allow the threads to start. */
+ sleep (1);
+ log_trace ("Done creating threads.\n");
+
+ log ("\n");
+ mutex_init_test ();
+ log ("\n");
+ mutex_destroy_test ();
+ log ("\n");
+ mutex_lock_test ();
+ log ("\n");
+ mutex_unlock_test ();
+ log ("\n");
+ queueing_order_test ();
+ log ("\n");
+ mutex_prioinherit_test ();
+ log ("\n");
+ mutex_prioceiling_test ();
+ log ("\n");
+
+ log ("Total tests %d, passed %d, failed %d\n",
+ total, pass_count, error_count);
+
+ /* Set the done flag and signal the threads to exit. */
+ log_trace ("Setting done flag.\n");
+ done = 1;
+
+ /*
+ * Wait for the threads to finish.
+ */
+ log_trace ("Trying to join threads.\n");
+ for (i = 0; i < NUM_THREADS; i++) {
+ send_cmd (i, CMD_NONE);
+ assert (pthread_join (states[i].tid, &exit_status) == 0);
+ }
+
+ /* Clean up after ourselves. */
+ close (pipefd[0]);
+ close (pipefd[1]);
+
+ if (error_count != 0)
+ exit (EX_OSERR); /* any better ideas??? */
+ else
+ exit (EX_OK);
+}
diff --git a/lib/libkse/test/mutex_d.exp b/lib/libkse/test/mutex_d.exp
new file mode 100644
index 0000000..de8a4e4
--- /dev/null
+++ b/lib/libkse/test/mutex_d.exp
@@ -0,0 +1,290 @@
+
+Testing pthread_mutex_init
+--------------------------
+ Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified) - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified) - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified) - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE - PASS
+
+Testing pthread_mutex_destroy
+-----------------------------
+ Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified)
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified)
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified)
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Destruction of unused mutex - PASS
+ Destruction of mutex locked by self - PASS
+ Destruction of mutex locked by another thread - PASS
+ Destruction of mutex while being used in cond_wait - PASS
+
+Testing pthread_mutex_lock
+--------------------------
+ Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified)
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified)
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified)
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Lock on unlocked mutex - PASS
+ Lock on invalid mutex - PASS
+ Lock on mutex held by self - PASS
+
+Testing pthread_mutex_unlock
+----------------------------
+ Protocol PTHREAD_PRIO_NONE, Type POSIX (type not specified)
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_NORMAL
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_NONE, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type POSIX (type not specified)
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type POSIX (type not specified)
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+ Protocol PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Unlock on mutex held by self - PASS
+ Unlock on invalid mutex - PASS
+ Unlock on mutex locked by another thread - PASS
+
+Testing queueing order
+----------------------
+ Queueing order on a mutex - PASS
+ Queueing order on a condition variable - PASS
+
+Testing priority inheritence
+----------------------------
+ Protype PTHREAD_PRIO_INHERIT, Type POSIX (type not specified)
+ Simple inheritence test - PASS
+ Inheritence test with change of priority - PASS
+ Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Simple inheritence test - PASS
+ Inheritence test with change of priority - PASS
+ Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Simple inheritence test - PASS
+ Inheritence test with change of priority - PASS
+ Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Simple inheritence test - PASS
+ Inheritence test with change of priority - PASS
+ Protype PTHREAD_PRIO_INHERIT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Simple inheritence test - PASS
+ Inheritence test with change of priority - PASS
+
+Testing priority ceilings
+-------------------------
+ Protype PTHREAD_PRIO_PROTECT, Type POSIX (type not specified)
+ Lock with ceiling priority < thread priority - PASS
+ Lock with ceiling priority = thread priority - PASS
+ Lock with ceiling priority > thread priority - PASS
+ Preemption with ceiling priority < thread priority - PASS
+ Preemption with ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority > thread priority - PASS
+ Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_DEFAULT
+ Lock with ceiling priority < thread priority - PASS
+ Lock with ceiling priority = thread priority - PASS
+ Lock with ceiling priority > thread priority - PASS
+ Preemption with ceiling priority < thread priority - PASS
+ Preemption with ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority > thread priority - PASS
+ Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_ERRORCHECK
+ Lock with ceiling priority < thread priority - PASS
+ Lock with ceiling priority = thread priority - PASS
+ Lock with ceiling priority > thread priority - PASS
+ Preemption with ceiling priority < thread priority - PASS
+ Preemption with ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority > thread priority - PASS
+ Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_NORMAL
+ Lock with ceiling priority < thread priority - PASS
+ Lock with ceiling priority = thread priority - PASS
+ Lock with ceiling priority > thread priority - PASS
+ Preemption with ceiling priority < thread priority - PASS
+ Preemption with ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority > thread priority - PASS
+ Protype PTHREAD_PRIO_PROTECT, Type SS2 PTHREAD_MUTEX_RECURSIVE
+ Lock with ceiling priority < thread priority - PASS
+ Lock with ceiling priority = thread priority - PASS
+ Lock with ceiling priority > thread priority - PASS
+ Preemption with ceiling priority < thread priority - PASS
+ Preemption with ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority = thread priority - PASS
+ SCHED_FIFO scheduling and ceiling priority > thread priority - PASS
+
+Total tests 212, passed 212, failed 0
diff --git a/lib/libkse/test/sem_d.c b/lib/libkse/test/sem_d.c
new file mode 100644
index 0000000..1551ae8
--- /dev/null
+++ b/lib/libkse/test/sem_d.c
@@ -0,0 +1,135 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************
+ *
+ * sem test.
+ *
+ * $FreeBSD$
+ *
+ ****************************************************************************/
+
+#define _REENTRANT
+
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <semaphore.h>
+#include <pthread.h>
+
+#define NTHREADS 10
+
+void *
+entry(void * a_arg)
+{
+ sem_t * sem = (sem_t *) a_arg;
+
+ sem_wait(sem);
+ fprintf(stderr, "Got semaphore\n");
+
+ return NULL;
+}
+
+int
+main()
+{
+ sem_t sem_a, sem_b;
+ pthread_t threads[NTHREADS];
+ unsigned i;
+ int val;
+
+ fprintf(stderr, "Test begin\n");
+
+#ifdef _LIBC_R_
+ assert(-1 == sem_init(&sem_b, 1, 0));
+ assert(EPERM == errno);
+#endif
+
+ assert(0 == sem_init(&sem_b, 0, 0));
+ assert(0 == sem_getvalue(&sem_b, &val));
+ assert(0 == val);
+
+ assert(0 == sem_post(&sem_b));
+ assert(0 == sem_getvalue(&sem_b, &val));
+ assert(1 == val);
+
+ assert(0 == sem_wait(&sem_b));
+ assert(-1 == sem_trywait(&sem_b));
+ assert(EAGAIN == errno);
+ assert(0 == sem_post(&sem_b));
+ assert(0 == sem_trywait(&sem_b));
+ assert(0 == sem_post(&sem_b));
+ assert(0 == sem_wait(&sem_b));
+ assert(0 == sem_post(&sem_b));
+
+#ifdef _LIBC_R_
+ assert(SEM_FAILED == sem_open("/foo", O_CREAT | O_EXCL, 0644, 0));
+ assert(ENOSYS == errno);
+
+ assert(-1 == sem_close(&sem_b));
+ assert(ENOSYS == errno);
+
+ assert(-1 == sem_unlink("/foo"));
+ assert(ENOSYS == errno);
+#endif
+
+ assert(0 == sem_destroy(&sem_b));
+
+ assert(0 == sem_init(&sem_a, 0, 0));
+
+ for (i = 0; i < NTHREADS; i++) {
+ pthread_create(&threads[i], NULL, entry, (void *) &sem_a);
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ assert(0 == sem_post(&sem_a));
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ pthread_join(threads[i], NULL);
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ pthread_create(&threads[i], NULL, entry, (void *) &sem_a);
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ assert(0 == sem_post(&sem_a));
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ pthread_join(threads[i], NULL);
+ }
+
+ assert(0 == sem_destroy(&sem_a));
+
+ fprintf(stderr, "Test end\n");
+ return 0;
+}
diff --git a/lib/libkse/test/sem_d.exp b/lib/libkse/test/sem_d.exp
new file mode 100644
index 0000000..b0de3da
--- /dev/null
+++ b/lib/libkse/test/sem_d.exp
@@ -0,0 +1,22 @@
+Test begin
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Got semaphore
+Test end
diff --git a/lib/libkse/test/sigsuspend_d.c b/lib/libkse/test/sigsuspend_d.c
new file mode 100644
index 0000000..d2420ed
--- /dev/null
+++ b/lib/libkse/test/sigsuspend_d.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Daniel M. Eischen.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_LIBC_R_)
+#include <pthread_np.h>
+#endif
+
+static int sigcounts[NSIG + 1];
+static int sigfifo[NSIG + 1];
+static int fifo_depth = 0;
+static sigset_t suspender_mask;
+static pthread_t suspender_tid;
+
+
+static void *
+sigsuspender (void *arg)
+{
+ int save_count, status, i;
+ sigset_t run_mask;
+
+ /* Run with all signals blocked. */
+ sigfillset (&run_mask);
+ sigprocmask (SIG_SETMASK, &run_mask, NULL);
+
+ /* Allow these signals to wake us up during a sigsuspend. */
+ sigfillset (&suspender_mask); /* Default action */
+ sigdelset (&suspender_mask, SIGINT); /* terminate */
+ sigdelset (&suspender_mask, SIGHUP); /* terminate */
+ sigdelset (&suspender_mask, SIGQUIT); /* create core image */
+ sigdelset (&suspender_mask, SIGURG); /* ignore */
+ sigdelset (&suspender_mask, SIGIO); /* ignore */
+ sigdelset (&suspender_mask, SIGUSR2); /* terminate */
+
+ while (sigcounts[SIGINT] == 0) {
+ save_count = sigcounts[SIGUSR2];
+
+ status = sigsuspend (&suspender_mask);
+ if ((status == 0) || (errno != EINTR)) {
+ fprintf (stderr, "Unable to suspend for signals, "
+ "errno %d, return value %d\n",
+ errno, status);
+ exit (1);
+ }
+ for (i = 0; i < fifo_depth; i++)
+ fprintf (stderr, "Sigsuspend woke up by signal %d\n",
+ sigfifo[i]);
+ fifo_depth = 0;
+ }
+
+ pthread_exit (arg);
+ return (NULL);
+}
+
+
+static void
+sighandler (int signo)
+{
+ sigset_t set, suspend_set;
+ pthread_t self;
+
+ if ((signo >= 0) && (signo <= NSIG))
+ sigcounts[signo]++;
+
+ /*
+ * If we are running on behalf of the suspender thread,
+ * ensure that we have the correct mask set.
+ */
+ self = pthread_self ();
+ if (self == suspender_tid) {
+ sigfifo[fifo_depth] = signo;
+ fifo_depth++;
+ fprintf (stderr,
+ " -> Suspender thread signal handler caught signal %d\n",
+ signo);
+
+ /* Get the current signal mask. */
+ sigprocmask (SIG_SETMASK, NULL, &set);
+
+ /* The handler should run with the current signal masked. */
+ suspend_set = suspender_mask;
+ sigaddset(&suspend_set, signo);
+
+ if (memcmp(&set, &suspend_set, sizeof(set)))
+ fprintf (stderr,
+ " >>> FAIL: sigsuspender signal handler running "
+ "with incorrect mask.\n");
+ }
+ else
+ fprintf (stderr,
+ " -> Main thread signal handler caught signal %d\n",
+ signo);
+}
+
+
+static void
+send_thread_signal (pthread_t tid, int signo)
+{
+ if (pthread_kill (tid, signo) != 0) {
+ fprintf (stderr, "Unable to send thread signal, errno %d.\n",
+ errno);
+ exit (1);
+ }
+}
+
+
+static void
+send_process_signal (int signo)
+{
+ if (kill (getpid (), signo) != 0) {
+ fprintf (stderr, "Unable to send process signal, errno %d.\n",
+ errno);
+ exit (1);
+ }
+}
+
+
+int main (int argc, char *argv[])
+{
+ pthread_attr_t pattr;
+ void * exit_status;
+ struct sigaction act;
+ sigset_t oldset;
+ sigset_t newset;
+
+ /* Initialize our signal counts. */
+ memset ((void *) sigcounts, 0, NSIG * sizeof (int));
+
+ /* Ignore signal SIGIO. */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGIO);
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ sigaction (SIGIO, &act, NULL);
+
+ /* Install a signal handler for SIGURG. */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGURG);
+ act.sa_handler = sighandler;
+ act.sa_flags = SA_RESTART;
+ sigaction (SIGURG, &act, NULL);
+
+ /* Install a signal handler for SIGXCPU */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGXCPU);
+ sigaction (SIGXCPU, &act, NULL);
+
+ /* Get our current signal mask. */
+ sigprocmask (SIG_SETMASK, NULL, &oldset);
+
+ /* Mask out SIGUSR1 and SIGUSR2. */
+ newset = oldset;
+ sigaddset (&newset, SIGUSR1);
+ sigaddset (&newset, SIGUSR2);
+ sigprocmask (SIG_SETMASK, &newset, NULL);
+
+ /* Install a signal handler for SIGUSR1 */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGUSR1);
+ sigaction (SIGUSR1, &act, NULL);
+
+ /* Install a signal handler for SIGUSR2 */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGUSR2);
+ sigaction (SIGUSR2, &act, NULL);
+
+ /*
+ * Initialize the thread attribute.
+ */
+ if ((pthread_attr_init (&pattr) != 0) ||
+ (pthread_attr_setdetachstate (&pattr,
+ PTHREAD_CREATE_JOINABLE) != 0)) {
+ fprintf (stderr, "Unable to initialize thread attributes.\n");
+ exit (1);
+ }
+
+ /*
+ * Create the sigsuspender thread.
+ */
+ if (pthread_create (&suspender_tid, &pattr, sigsuspender, NULL) != 0) {
+ fprintf (stderr, "Unable to create thread, errno %d.\n", errno);
+ exit (1);
+ }
+#if defined(_LIBC_R)
+ pthread_set_name_np (suspender_tid, "sigsuspender");
+#endif
+
+ /*
+ * Verify that an ignored signal doesn't cause a wakeup.
+ * We don't have a handler installed for SIGIO.
+ */
+ send_thread_signal (suspender_tid, SIGIO);
+ sleep (1);
+ send_process_signal (SIGIO);
+ sleep (1);
+ if (sigcounts[SIGIO] != 0)
+ fprintf (stderr, "FAIL: sigsuspend wakes up for ignored signal "
+ "SIGIO.\n");
+
+ /*
+ * Verify that a signal with a default action of ignore, for
+ * which we have a signal handler installed, will release a
+ * sigsuspend.
+ */
+ send_thread_signal (suspender_tid, SIGURG);
+ sleep (1);
+ send_process_signal (SIGURG);
+ sleep (1);
+ if (sigcounts[SIGURG] != 2)
+ fprintf (stderr,
+ "FAIL: sigsuspend doesn't wake up for SIGURG.\n");
+
+ /*
+ * Verify that a SIGUSR2 signal will release a sigsuspended
+ * thread.
+ */
+ send_thread_signal (suspender_tid, SIGUSR2);
+ sleep (1);
+ send_process_signal (SIGUSR2);
+ sleep (1);
+ if (sigcounts[SIGUSR2] != 2)
+ fprintf (stderr,
+ "FAIL: sigsuspend doesn't wake up for SIGUSR2.\n");
+
+ /*
+ * Verify that a signal, blocked in both the main and
+ * sigsuspender threads, does not cause the signal handler
+ * to be called.
+ */
+ send_thread_signal (suspender_tid, SIGUSR1);
+ sleep (1);
+ send_process_signal (SIGUSR1);
+ sleep (1);
+ if (sigcounts[SIGUSR1] != 0)
+ fprintf (stderr, "FAIL: signal hander called for SIGUSR1.\n");
+
+ /*
+ * Verify that we can still kill the process for a signal
+ * not being waited on by sigwait.
+ */
+ send_process_signal (SIGPIPE);
+ fprintf (stderr, "FAIL: SIGPIPE did not terminate process.\n");
+
+ /*
+ * Wait for the thread to finish.
+ */
+ pthread_join (suspender_tid, &exit_status);
+
+ return (0);
+}
diff --git a/lib/libkse/test/sigsuspend_d.exp b/lib/libkse/test/sigsuspend_d.exp
new file mode 100644
index 0000000..901fa50
--- /dev/null
+++ b/lib/libkse/test/sigsuspend_d.exp
@@ -0,0 +1,8 @@
+ -> Suspender thread signal handler caught signal 16
+Sigsuspend woke up by signal 16
+ -> Suspender thread signal handler caught signal 16
+Sigsuspend woke up by signal 16
+ -> Suspender thread signal handler caught signal 31
+Sigsuspend woke up by signal 31
+ -> Suspender thread signal handler caught signal 31
+Sigsuspend woke up by signal 31
diff --git a/lib/libkse/test/sigwait_d.c b/lib/libkse/test/sigwait_d.c
new file mode 100644
index 0000000..ab57979
--- /dev/null
+++ b/lib/libkse/test/sigwait_d.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Daniel M. Eischen.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_LIBC_R_)
+#include <pthread_np.h>
+#endif
+
+static int sigcounts[NSIG + 1];
+static sigset_t wait_mask;
+static pthread_mutex_t waiter_mutex;
+
+
+static void *
+sigwaiter (void *arg)
+{
+ int signo;
+ sigset_t mask;
+
+ /* Block SIGHUP */
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGHUP);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ while (sigcounts[SIGINT] == 0) {
+ if (sigwait (&wait_mask, &signo) != 0) {
+ fprintf (stderr,
+ "Unable to wait for signal, errno %d\n",
+ errno);
+ exit (1);
+ }
+ sigcounts[signo]++;
+ fprintf (stderr, "Sigwait caught signal %d\n", signo);
+
+ /* Allow the main thread to prevent the sigwait. */
+ pthread_mutex_lock (&waiter_mutex);
+ pthread_mutex_unlock (&waiter_mutex);
+ }
+
+ pthread_exit (arg);
+ return (NULL);
+}
+
+
+static void
+sighandler (int signo)
+{
+ fprintf (stderr, " -> Signal handler caught signal %d\n", signo);
+
+ if ((signo >= 0) && (signo <= NSIG))
+ sigcounts[signo]++;
+}
+
+static void
+send_thread_signal (pthread_t tid, int signo)
+{
+ if (pthread_kill (tid, signo) != 0) {
+ fprintf (stderr, "Unable to send thread signal, errno %d.\n",
+ errno);
+ exit (1);
+ }
+}
+
+static void
+send_process_signal (int signo)
+{
+ if (kill (getpid (), signo) != 0) {
+ fprintf (stderr, "Unable to send process signal, errno %d.\n",
+ errno);
+ exit (1);
+ }
+}
+
+
+int main (int argc, char *argv[])
+{
+ pthread_mutexattr_t mattr;
+ pthread_attr_t pattr;
+ pthread_t tid;
+ void * exit_status;
+ struct sigaction act;
+
+ /* Initialize our signal counts. */
+ memset ((void *) sigcounts, 0, NSIG * sizeof (int));
+
+ /* Setup our wait mask. */
+ sigemptyset (&wait_mask); /* Default action */
+ sigaddset (&wait_mask, SIGHUP); /* terminate */
+ sigaddset (&wait_mask, SIGINT); /* terminate */
+ sigaddset (&wait_mask, SIGQUIT); /* create core image */
+ sigaddset (&wait_mask, SIGURG); /* ignore */
+ sigaddset (&wait_mask, SIGIO); /* ignore */
+ sigaddset (&wait_mask, SIGUSR1); /* terminate */
+
+ /* Ignore signals SIGHUP and SIGIO. */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGHUP);
+ sigaddset (&act.sa_mask, SIGIO);
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ sigaction (SIGHUP, &act, NULL);
+ sigaction (SIGIO, &act, NULL);
+
+ /* Install a signal handler for SIGURG */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGURG);
+ act.sa_handler = sighandler;
+ act.sa_flags = SA_RESTART;
+ sigaction (SIGURG, &act, NULL);
+
+ /* Install a signal handler for SIGXCPU */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGXCPU);
+ sigaction (SIGXCPU, &act, NULL);
+
+ /*
+ * Initialize the thread attribute.
+ */
+ if ((pthread_attr_init (&pattr) != 0) ||
+ (pthread_attr_setdetachstate (&pattr,
+ PTHREAD_CREATE_JOINABLE) != 0)) {
+ fprintf (stderr, "Unable to initialize thread attributes.\n");
+ exit (1);
+ }
+
+ /*
+ * Initialize and create a mutex.
+ */
+ if ((pthread_mutexattr_init (&mattr) != 0) ||
+ (pthread_mutex_init (&waiter_mutex, &mattr) != 0)) {
+ fprintf (stderr, "Unable to create waiter mutex.\n");
+ exit (1);
+ }
+
+ /*
+ * Create the sigwaiter thread.
+ */
+ if (pthread_create (&tid, &pattr, sigwaiter, NULL) != 0) {
+ fprintf (stderr, "Unable to create thread.\n");
+ exit (1);
+ }
+#if defined(_LIBC_R_)
+ pthread_set_name_np (tid, "sigwaiter");
+#endif
+
+ /*
+ * Verify that an ignored signal doesn't cause a wakeup.
+ * We don't have a handler installed for SIGIO.
+ */
+ send_thread_signal (tid, SIGIO);
+ sleep (1);
+ send_process_signal (SIGIO);
+ sleep (1);
+ if (sigcounts[SIGIO] != 0)
+ fprintf (stderr,
+ "FAIL: sigwait wakes up for ignored signal SIGIO.\n");
+
+ /*
+ * Verify that a signal with a default action of ignore, for
+ * which we have a signal handler installed, will release a sigwait.
+ */
+ send_thread_signal (tid, SIGURG);
+ sleep (1);
+ send_process_signal (SIGURG);
+ sleep (1);
+ if (sigcounts[SIGURG] != 2)
+ fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGURG.\n");
+
+ /*
+ * Verify that a signal with a default action that terminates
+ * the process will release a sigwait.
+ */
+ send_thread_signal (tid, SIGUSR1);
+ sleep (1);
+ send_process_signal (SIGUSR1);
+ sleep (1);
+ if (sigcounts[SIGUSR1] != 2)
+ fprintf (stderr,
+ "FAIL: sigwait doesn't wake up for SIGUSR1.\n");
+
+ /*
+ * Verify that if we install a signal handler for a previously
+ * ignored signal, an occurrence of this signal will release
+ * the (already waiting) sigwait.
+ */
+
+ /* Install a signal handler for SIGHUP. */
+ sigemptyset (&act.sa_mask);
+ sigaddset (&act.sa_mask, SIGHUP);
+ act.sa_handler = sighandler;
+ act.sa_flags = SA_RESTART;
+ sigaction (SIGHUP, &act, NULL);
+
+ /* Sending SIGHUP should release the sigwait. */
+ send_process_signal (SIGHUP);
+ sleep (1);
+ send_thread_signal (tid, SIGHUP);
+ sleep (1);
+ if (sigcounts[SIGHUP] != 2)
+ fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGHUP.\n");
+
+ /*
+ * Verify that a pending signal in the waiters mask will
+ * cause sigwait to return the pending signal. We do this
+ * by taking the waiters mutex and signaling the waiter to
+ * release him from the sigwait. The waiter will block
+ * on taking the mutex, and we can then send the waiter a
+ * signal which should be added to his pending signals.
+ * The next time the waiter does a sigwait, he should
+ * return with the pending signal.
+ */
+ sigcounts[SIGHUP] = 0;
+ pthread_mutex_lock (&waiter_mutex);
+ /* Release the waiter from sigwait. */
+ send_process_signal (SIGHUP);
+ sleep (1);
+ if (sigcounts[SIGHUP] != 1)
+ fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGHUP.\n");
+ /*
+ * Add SIGHUP to the process pending signals. Since there is
+ * a signal handler installed for SIGHUP and this signal is
+ * blocked from the waiter thread and unblocked in the main
+ * thread, the signal handler should be called once for SIGHUP.
+ */
+ send_process_signal (SIGHUP);
+ /* Release the waiter thread and allow him to run. */
+ pthread_mutex_unlock (&waiter_mutex);
+ sleep (1);
+ if (sigcounts[SIGHUP] != 2)
+ fprintf (stderr,
+ "FAIL: sigwait doesn't return for pending SIGHUP.\n");
+
+ /*
+ * Repeat the above test using pthread_kill and SIGUSR1.
+ */
+ sigcounts[SIGUSR1] = 0;
+ pthread_mutex_lock (&waiter_mutex);
+ /* Release the waiter from sigwait. */
+ send_thread_signal (tid, SIGUSR1);
+ sleep (1);
+ if (sigcounts[SIGUSR1] != 1)
+ fprintf (stderr,
+ "FAIL: sigwait doesn't wake up for SIGUSR1.\n");
+ /* Add SIGHUP to the waiters pending signals. */
+ send_thread_signal (tid, SIGUSR1);
+ /* Release the waiter thread and allow him to run. */
+ pthread_mutex_unlock (&waiter_mutex);
+ sleep (1);
+ if (sigcounts[SIGUSR1] != 2)
+ fprintf (stderr,
+ "FAIL: sigwait doesn't return for pending SIGUSR1.\n");
+
+ /*
+ * Verify that we can still kill the process for a signal
+ * not being waited on by sigwait.
+ */
+ send_process_signal (SIGPIPE);
+ fprintf (stderr, "FAIL: SIGPIPE did not terminate process.\n");
+
+ /*
+ * Wait for the thread to finish.
+ */
+ pthread_join (tid, &exit_status);
+
+ return (0);
+}
diff --git a/lib/libkse/test/sigwait_d.exp b/lib/libkse/test/sigwait_d.exp
new file mode 100644
index 0000000..2e9b2c4
--- /dev/null
+++ b/lib/libkse/test/sigwait_d.exp
@@ -0,0 +1,10 @@
+Sigwait caught signal 16
+Sigwait caught signal 16
+Sigwait caught signal 30
+Sigwait caught signal 30
+Sigwait caught signal 1
+Sigwait caught signal 1
+Sigwait caught signal 1
+ -> Signal handler caught signal 1
+Sigwait caught signal 30
+Sigwait caught signal 30
diff --git a/lib/libkse/test/verify b/lib/libkse/test/verify
new file mode 100644
index 0000000..bf8d94a
--- /dev/null
+++ b/lib/libkse/test/verify
@@ -0,0 +1,505 @@
+#!/usr/bin/perl -w
+#-*-mode:perl-*-
+#############################################################################
+#
+# Copyright (C) 1999-2000 Jason Evans <jasone@freebsd.org>.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice(s), this list of conditions and the following disclaimer as
+# the first lines of this file unmodified other than the possible
+# addition of one or more copyright notices.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice(s), this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#############################################################################
+#
+# Test harness.
+#
+# $FreeBSD$
+#
+#############################################################################
+
+# Shut off buffering.
+select(STDOUT);
+$| = 1;
+
+#
+# Parse command-line arguments.
+#
+use Getopt::Long;
+Getopt::Long::config("bundling"); # Allow -hv rather than forcing -h -v.
+
+# Set option defaults for optional arguments.
+$opt_help = 0;
+$opt_verbose = 0;
+$opt_quiet = 0;
+$opt_srcdir = ".";
+$opt_ustats = 0;
+$opt_zero = 0;
+
+$opt_retval =
+&GetOptions("h|help" => \$opt_help,
+ "v|verbose" => \$opt_verbose,
+ "q|quiet" => \$opt_quiet,
+ "s|srcdir=s" => \$opt_srcdir,
+ "u|ustats" => \$opt_ustats,
+ "z|zero" => \$opt_zero
+ );
+
+$mode = "d";
+for ($i = 0; $i <= $#ARGV; $i++)
+{
+ if (($ARGV[$i] eq "-s") || ($ARGV[$i] eq "--sequence"))
+ {
+ $mode = "s";
+ }
+ elsif (($ARGV[$i] eq "-d") || ($ARGV[$i] eq "--diff"))
+ {
+ $mode = "d";
+ }
+ elsif (-x $ARGV[$i])
+ {
+ if ($mode eq "s")
+ {
+ @STESTS = (@STESTS, $ARGV[$i]);
+ }
+ else
+ {
+ @DTESTS = (@DTESTS, $ARGV[$i]);
+ }
+ }
+ else
+ {
+ if (!$opt_quiet)
+ {
+ print STDERR "Cannot run \"$ARGV[$i]\"\n";
+ }
+ }
+}
+
+if ($opt_help)
+{
+ &usage();
+ exit(0);
+}
+
+if ($opt_retval == 0)
+{
+ &usage();
+ exit 1;
+}
+
+if ($opt_verbose && $opt_quiet)
+{
+ print STDERR "-v and -q are incompatible\n";
+ &usage();
+ exit 1;
+}
+
+if ($#STESTS + $#DTESTS + 2 == 0)
+{
+ print STDERR "No tests specified\n";
+ &usage();
+ exit 1;
+}
+
+if ($opt_verbose)
+{
+ print STDERR "Option values: h:$opt_help, v:$opt_verbose, q:$opt_quiet, "
+ . "u:$opt_ustats\n";
+ printf STDERR "Sequence tests (%d total): @STESTS\n", $#STESTS + 1;
+ printf STDERR "Diff tests (%d total): @DTESTS\n", $#DTESTS + 1;
+}
+
+#
+# Create and print header.
+#
+@TSTATS =
+(
+ "--------------------------------------------------------------------------\n",
+ "Test c_user c_system c_total chng\n",
+ " passed/FAILED h_user h_system h_total %% chng\n"
+ );
+
+if (!$opt_quiet)
+{
+ foreach $line (@TSTATS)
+ {
+ printf STDOUT "$line";
+ }
+}
+
+#
+# Run sequence test(s).
+#
+$total_utime = 0.0; # Total user time.
+$total_stime = 0.0; # Total system time.
+$total_hutime = 0.0; # Total historical user time.
+$total_hstime = 0.0; # Total historical system time.
+$total_ntime = 0.0; # Total time for tests that have historical data.
+
+foreach $test (@STESTS)
+{
+ # sequence mode.
+ $okay = 1;
+
+ ($okay, $utime, $stime) = &run_test($test);
+
+ if (open (STEST_OUT, "<./$test.out"))
+ {
+ $num_failed_subtests = 0;
+
+ $_ = <STEST_OUT>;
+
+ if ($_ =~ /1\.\.(\d+)/)
+ {
+ $num_subtests = $1;
+ }
+ else
+ {
+ $num_subtests = 0;
+ $okay = 0;
+ if ($opt_verbose)
+ {
+ print STDERR "Malformed 1..n line: \"$_\"\n";
+ }
+ }
+ for ($subtest = 1; $subtest <= $num_subtests; $subtest++)
+ {
+ while (defined($line = <STEST_OUT>))
+ {
+ if ($line =~ /^not\s+ok\s+(\d+)?/)
+ {
+ $not = 1;
+ $test_num = $1;
+ last;
+ }
+ elsif ($line =~ /^ok\s+(\d+)?/)
+ {
+ $not = 0;
+ $test_num = $1;
+ last;
+ }
+ }
+ if (defined($line))
+ {
+ if (defined($test_num) && ($test_num != $subtest))
+ {
+ # There was no output printed for one or more tests.
+ for (; $subtest < $test_num; $subtest++)
+ {
+ $num_failed_subtests++;
+ }
+ }
+ if ($not)
+ {
+ $num_failed_subtests++;
+ }
+ }
+ else
+ {
+ for (; $subtest <= $num_subtests; $subtest++)
+ {
+ $num_failed_subtests++;
+ }
+ }
+ }
+
+ if (0 < $num_failed_subtests)
+ {
+ $okay = 0;
+ }
+ }
+ else
+ {
+ if (!$opt_quiet)
+ {
+ print STDERR "Cannot open output file \"$test.out\"\n";
+ }
+ exit 1;
+ }
+
+ ($hutime, $hstime) = &print_stats($test, $okay,
+ $num_failed_subtests, $num_subtests,
+ $utime, $stime);
+ $total_hutime += $hutime;
+ $total_hstime += $hstime;
+
+ if ($okay)
+ {
+ $total_utime += $utime;
+ $total_stime += $stime;
+ }
+ else
+ {
+ @FAILED_TESTS = (@FAILED_TESTS, $test);
+ }
+
+ # If there were historical data, add the run time to the total time to
+ # compare against the historical run time.
+ if (0 < ($hutime + $hstime))
+ {
+ $total_ntime += $utime + $stime;
+ }
+}
+foreach $test (@DTESTS)
+{
+ # Diff mode.
+ $okay = 1;
+
+ ($okay, $utime, $stime) = &run_test($test);
+
+ if (-e "./$test.out" && -e "$opt_srcdir/$test.exp")
+ {
+ `diff ./$test.out $opt_srcdir/$test.exp > ./$test.diff 2>&1`;
+ if ($?)
+ {
+ # diff returns non-zero if there is a difference.
+ $okay = 0;
+ }
+ }
+ else
+ {
+ $okay = 0;
+ if ($opt_verbose)
+ {
+ print STDERR
+ "Nonexistent expected output file \"$opt_srcdir/$test.exp\"\n";
+ print STDERR "\$opt_srcdir is \"$opt_srcdir\"\n";
+ }
+ }
+
+ ($hutime, $hstime) = &print_stats($test, $okay, 0, 0, $utime, $stime);
+ $total_hutime += $hutime;
+ $total_hstime += $hstime;
+
+ if ($okay)
+ {
+ $total_utime += $utime;
+ $total_stime += $stime;
+ }
+ else
+ {
+ @FAILED_TESTS = (@FAILED_TESTS, $test);
+ }
+
+ # If there were historical data, add the run time to the total time to
+ # compare against the historical run time.
+ if (0 < ($hutime + $hstime))
+ {
+ $total_ntime += $utime + $stime;
+ }
+}
+
+# Print summary stats.
+$tt_str = sprintf ("%d / %d passed (%5.2f%%%%)",
+ ($#STESTS + $#DTESTS + 2) - ($#FAILED_TESTS + 1),
+ $#STESTS + $#DTESTS + 2,
+ (($#STESTS + $#DTESTS + 2) - ($#FAILED_TESTS + 1))
+ / ($#STESTS + $#DTESTS + 2) * 100);
+
+$t_str = sprintf ("Totals %7.2f %7.2f %7.2f"
+ . " %7.2f\n"
+ . " %s %7.2f %7.2f %7.2f %7.2f%%%%\n",
+ $total_utime, $total_stime, $total_utime + $total_stime,
+ ($total_ntime - ($total_hutime + $total_hstime)),
+ $tt_str . ' ' x (40 - length($tt_str)),
+ $total_hutime, $total_hstime, $total_hutime + $total_hstime,
+ ($total_hutime + $total_hstime == 0.0) ? 0.0 :
+ (($total_ntime
+ - ($total_hutime + $total_hstime))
+ / ($total_hutime + $total_hstime) * 100));
+
+@TSTATS = ("--------------------------------------------------------------------------\n",
+ $t_str,
+ "--------------------------------------------------------------------------\n"
+ );
+if (!$opt_quiet)
+{
+ foreach $line (@TSTATS)
+ {
+ printf STDOUT "$line";
+ }
+}
+
+# End of main execution.
+
+sub run_test
+{
+ my ($test) = @_;
+ my ($okay) = 1;
+ my ($tutime, $tstime);
+ my ($utime, $stime, $cutime, $cstime);
+ my (@TSTATS);
+ my ($t_str);
+
+ @TSTATS = ("--------------------------------------------------------------------------\n");
+
+ $t_str = sprintf ("%s%s", $test, ' ' x (40 - length($test)));
+ @TSTATS = (@TSTATS, $t_str);
+ @STATS = (@STATS, @TSTATS);
+ if (!$opt_quiet)
+ {
+ foreach $line (@TSTATS)
+ {
+ printf STDOUT "$line";
+ }
+ }
+
+ ($utime, $stime, $cutime, $cstime) = times;
+ `./$test $opt_srcdir > ./$test.out 2>&1`;
+ ($utime, $stime, $tutime, $tstime) = times;
+
+ # Subtract the before time from the after time.
+ $tutime -= $cutime;
+ $tstime -= $cstime;
+
+ if ($opt_zero)
+ {
+ if ($?)
+ {
+ $okay = 0;
+ if ($opt_verbose)
+ {
+ print STDERR "\"./$test > ./$test.out 2>&1\" returned $?\n";
+ }
+ }
+ }
+
+ return ($okay, $tutime, $tstime);
+}
+
+sub print_stats
+{
+ my ($test, $okay, $failed_subtests, $subtests, $utime, $stime) = @_;
+ my ($hutime, $hstime);
+# my (TEST_PERF);
+ my (@TSTATS);
+ my ($t_str, $pass_str);
+
+ $pass_str = $okay ? "passed" : "*** FAILED ***";
+ if ((0 != $subtests) && (!$okay))
+ {
+ $pass_str = $pass_str . " ($failed_subtests/$subtests failed)";
+ }
+ $pass_str = $pass_str . ' ' x (39 - length($pass_str));
+
+ if (-r "$test.perf")
+ {
+ if (!open (TEST_PERF, "<./$test.perf"))
+ {
+ print STDERR "Unable to open \"./$test.perf\"\n";
+ exit 1;
+ }
+ $_ = <TEST_PERF>;
+
+ ($hutime, $hstime) = split;
+ close TEST_PERF;
+
+ $t_str = sprintf (" %7.2f %7.2f %7.2f %7.2f\n"
+ . " %s %7.2f %7.2f %7.2f %7.2f%%%%\n",
+ $utime, $stime, $utime + $stime,
+ ($utime + $stime) - ($hutime + $hstime),
+ $pass_str,
+ $hutime, $hstime, $hutime + $hstime,
+ (($hutime + $hstime) == 0.0) ? 0.0 :
+ ((($utime + $stime) - ($hutime + $hstime))
+ / ($hutime + $hstime) * 100));
+ }
+ else
+ {
+ $hutime = 0.0;
+ $hstime = 0.0;
+
+ $t_str = sprintf (" %7.2f %7.2f %7.2f \n"
+ . " %s\n",
+ $utime, $stime, $utime + $stime,
+ $pass_str);
+ }
+ @TSTATS = ($t_str);
+ if (!$opt_quiet)
+ {
+ foreach $line (@TSTATS)
+ {
+ printf STDOUT "$line";
+ }
+ }
+
+ if ($okay && $opt_ustats)
+ {
+ if (!open (TEST_PERF, ">./$test.perf"))
+ {
+ if (!$opt_quiet)
+ {
+ print STDERR "Unable to update \"$test.perf\"\n";
+ }
+ }
+ else
+ {
+ print TEST_PERF "$utime $stime\n";
+ close TEST_PERF;
+ }
+ }
+
+ return ($hutime, $hstime);
+}
+
+sub usage
+{
+ print <<EOF;
+$0 usage:
+ $0 [<options>] -- {[-s | -d | --sequence | --diff] <test>+}+
+
+ Option | Description
+ --------------+-------------------------------------------------------------
+ -h --help | Print usage and exit.
+ -v --verbose | Verbose (incompatible with quiet).
+ -q --quiet | Quiet (incompatible with verbose).
+ -s --srcdir | Path to source tree (default is ".").
+ -u --ustats | Update historical statistics (stored in "<test>.perf".
+ -z --zero | Consider non-zero exit code to be an error.
+ --------------+-------------------------------------------------------------
+
+ Flag | Description
+ --------------+-------------------------------------------------------------
+ -s --sequence | Sequence mode (default).
+ | Output to stdout of the following form is expected:
+ |
+ | -----------------
+ | 1..<n>
+ | {not} ok [1]
+ | {not} ok [2]
+ | ...
+ | {not} ok [n]
+ | -----------------
+ |
+ | 1 <= <n> < 2^31
+ |
+ | Lines which do not match the patterns shown above are
+ | ignored, except that the 1..<n> construct must be the first
+ | line of output seen.
+ |
+ -d --diff | Diff mode (incompatible with sequence mode).
+ | If <test>.exp exists, diff it with the output from <test> to
+ | determine success or failure.
+ --------------+-------------------------------------------------------------
+EOF
+}
OpenPOWER on IntegriCloud