diff options
Diffstat (limited to 'lib/libpthread/test')
| -rw-r--r-- | lib/libpthread/test/Makefile | 115 | ||||
| -rw-r--r-- | lib/libpthread/test/README | 28 | ||||
| -rw-r--r-- | lib/libpthread/test/guard_b.c | 150 | ||||
| -rw-r--r-- | lib/libpthread/test/guard_b.exp | 3 | ||||
| -rwxr-xr-x | lib/libpthread/test/guard_s.pl | 69 | ||||
| -rw-r--r-- | lib/libpthread/test/hello_b.c | 13 | ||||
| -rw-r--r-- | lib/libpthread/test/hello_d.c | 38 | ||||
| -rw-r--r-- | lib/libpthread/test/hello_d.exp | 1 | ||||
| -rw-r--r-- | lib/libpthread/test/hello_s.c | 47 | ||||
| -rw-r--r-- | lib/libpthread/test/join_leak_d.c | 108 | ||||
| -rw-r--r-- | lib/libpthread/test/join_leak_d.exp | 2 | ||||
| -rw-r--r-- | lib/libpthread/test/mutex_d.c | 1552 | ||||
| -rw-r--r-- | lib/libpthread/test/mutex_d.exp | 290 | ||||
| -rwxr-xr-x | lib/libpthread/test/propagate_s.pl | 74 | ||||
| -rw-r--r-- | lib/libpthread/test/sem_d.c | 133 | ||||
| -rw-r--r-- | lib/libpthread/test/sem_d.exp | 22 | ||||
| -rw-r--r-- | lib/libpthread/test/sigsuspend_d.c | 288 | ||||
| -rw-r--r-- | lib/libpthread/test/sigsuspend_d.exp | 8 | ||||
| -rw-r--r-- | lib/libpthread/test/sigwait_d.c | 304 | ||||
| -rw-r--r-- | lib/libpthread/test/sigwait_d.exp | 10 | ||||
| -rwxr-xr-x | lib/libpthread/test/verify | 474 | 
21 files changed, 3729 insertions, 0 deletions
diff --git a/lib/libpthread/test/Makefile b/lib/libpthread/test/Makefile new file mode 100644 index 000000000000..0eb530c3ef14 --- /dev/null +++ b/lib/libpthread/test/Makefile @@ -0,0 +1,115 @@ +# +# $FreeBSD$ +# +# Automated test suite for libc_r (pthreads). +# + +# File lists. + +# Tests written in C. +CTESTS := hello_d.c hello_s.c join_leak_d.c mutex_d.c sem_d.c sigsuspend_d.c \ +	sigwait_d.c + +# C programs that are used internally by the tests.  The build system merely +# compiles these. +BTESTS := guard_b.c hello_b.c + +# Tests written in perl. +PTESTS := guard_s.pl propagate_s.pl + +# Munge the file lists to their final executable names (strip the .c). +CTESTS := $(CTESTS:R) +BTESTS := $(BTESTS:R) + +CPPFLAGS := -D_LIBC_R_ -D_REENTRANT +CFLAGS := -Wall -pipe -g3 +LDFLAGS_A := -static +LDFLAGS_P := -pg +LDFLAGS_S := +LIBS := -lc_r + +# 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 $(CTESTS) $(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 $(CTESTS:R:S/$/&_a.d/) $(BTESTS:R:S/$/&_a.d/) \ +		$(CTESTS:R:S/$/&_p.d/) $(BTESTS:R:S/$/&_p.d/) \ +		$(CTESTS:R:S/$/&_s.d/) $(BTESTS:R:S/$/&_s.d/) +.if exists($(depfile)) +.include "$(depfile)" +.endif +.endfor + +default : check + +tests_a : $(CTESTS:S/$/&_a/) $(BTESTS:S/$/&_a/) +tests_p : $(CTESTS:S/$/&_p/) $(BTESTS:S/$/&_p/) +tests_s : $(CTESTS:S/$/&_s/) $(BTESTS:S/$/&_s/) + +tests : tests_a tests_p tests_s + +check_a : tests_a +.for bin in $(CTESTS) $(BTESTS) +	@cp $(bin)_a $(bin) +.endfor +	@echo "Test static library:" +	@./verify $(VFLAGS) $(CTESTS) $(PTESTS) + +check_p : tests_p +.for bin in $(CTESTS) $(BTESTS) +	@cp $(bin)_p $(bin) +.endfor +	@echo "Test profile library:" +	@./verify $(VFLAGS) $(CTESTS) $(PTESTS) + +check_s : tests_s +.for bin in $(CTESTS) $(BTESTS) +	@cp $(bin)_s $(bin) +.endfor +	@echo "Test shared library:" +	@./verify $(VFLAGS) $(CTESTS) $(PTESTS) + +check : check_a check_p check_s + +clean : +	rm -f *~ +	rm -f *.core +	rm -f *.out +	rm -f *.perf +	rm -f *.diff +	rm -f *.gmon +	rm -f $(CTESTS) $(BTESTS) +	rm -f $(CTESTS:S/$/&_a/) $(BTESTS:S/$/&_a/) +	rm -f $(CTESTS:S/$/&_p/) $(BTESTS:S/$/&_p/) +	rm -f $(CTESTS:S/$/&_s/) $(BTESTS:S/$/&_s/) +	rm -f *.d +	rm -f *.o diff --git a/lib/libpthread/test/README b/lib/libpthread/test/README new file mode 100644 index 000000000000..507ea4e19f74 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/guard_b.c b/lib/libpthread/test/guard_b.c new file mode 100644 index 000000000000..e2f1a3aa33f7 --- /dev/null +++ b/lib/libpthread/test/guard_b.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2001 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 + *    unmodified other than the allowable 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. + * + * $FreeBSD$ + * + * Test thread stack guard functionality. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <pthread.h> + +#define FRAME_SIZE	1024 +#define FRAME_OVERHEAD	  40 + +struct args +{ +	void	*top;	/* Top of thread's initial stack frame. */ +	int	cur;	/* Recursion depth. */ +	int	max;	/* Maximum recursion depth. */ +}; + +void * +recurse(void *args) +{ +	int		top; +	struct args	*parms = (struct args *)args; +	char		filler[FRAME_SIZE - FRAME_OVERHEAD]; + +	/* Touch the memory in this stack frame. */ +	top = 0xa5; +	memset(filler, 0xa5, sizeof(filler)); + +	if (parms->top == NULL) { +		/* Initial stack frame. */ +		parms->top = (void*)⊤ +	} + +	/* +	 * Make sure frame size is what we expect.  Getting this right involves +	 * hand tweaking, so just print a warning rather than aborting. +	 */ +	if (parms->top - (void *)&top != FRAME_SIZE * parms->cur) { +		fprintf(stderr, "Stack size (%d) != expected (%d), frame %d\n", +		    parms->top - (void *)&top, FRAME_SIZE * parms->cur, +		    parms->cur); +	} + +	parms->cur++; +	if (parms->cur < parms->max) +		recurse(args); + +	return NULL; +} + + +int +main(int argc, char **argv) +{ +	size_t		def_stacksize, def_guardsize; +	size_t		stacksize, guardsize; +	pthread_t	thread; +	pthread_attr_t	attr; +	struct args	args; + +	if (argc != 3) { +		fprintf(stderr, "Usage: guard_b <stacksize> <guardsize>\n"); +		exit(1); +	} +	fprintf(stderr, "Test begin\n"); + +	stacksize = strtoul(argv[1], NULL, 10); +	guardsize = strtoul(argv[2], NULL, 10); + +	assert(pthread_attr_init(&attr) == 0); +	/* +	 * Exercise the attribute APIs more thoroughly than is strictly +	 * necessary for the meat of this test program. +	 */ +	assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0); +	assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0); +	if (def_stacksize != stacksize) { +		assert(pthread_attr_setstacksize(&attr, stacksize) == 0); +		assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0); +		assert(def_stacksize == stacksize); +	} +	if (def_guardsize != guardsize) { +		assert(pthread_attr_setguardsize(&attr, guardsize) == 0); +		assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0); +		assert(def_guardsize >= guardsize); +	} + +	/* +	 * Create a thread that will come just short of overflowing the thread +	 * stack.  We need to leave a bit of breathing room in case the thread +	 * is context switched, and we also have to take care not to call any +	 * functions in the deepest stack frame. +	 */ +	args.top = NULL; +	args.cur = 0; +	args.max = (stacksize / FRAME_SIZE) - 1; +	fprintf(stderr, "No overflow:\n"); +	assert(pthread_create(&thread, &attr, recurse, &args) == 0); +	assert(pthread_join(thread, NULL) == 0); +	 +	/* +	 * Create a thread that will barely of overflow the thread stack.  This +	 * should cause a segfault. +	 */ +	args.top = NULL; +	args.cur = 0; +	args.max = (stacksize / FRAME_SIZE) + 1; +	fprintf(stderr, "Overflow:\n"); +	assert(pthread_create(&thread, &attr, recurse, &args) == 0); +	assert(pthread_join(thread, NULL) == 0); + +	/* Not reached. */ +	fprintf(stderr, "Unexpected success\n"); +	abort(); + +	return 0; +} diff --git a/lib/libpthread/test/guard_b.exp b/lib/libpthread/test/guard_b.exp new file mode 100644 index 000000000000..8e5b9e426a21 --- /dev/null +++ b/lib/libpthread/test/guard_b.exp @@ -0,0 +1,3 @@ +Test begin +No overflow: +Overflow: diff --git a/lib/libpthread/test/guard_s.pl b/lib/libpthread/test/guard_s.pl new file mode 100755 index 000000000000..7802ff3c38d6 --- /dev/null +++ b/lib/libpthread/test/guard_s.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2001 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 +#    unmodified other than the allowable 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. +# +# $FreeBSD$ +# +# Test thread stack guard functionality.  The C test program needs to be driven +# by this script because it segfaults when the stack guard is hit. +# + +print "1..30\n"; + +$i = 0; +# Iterates 10 times. +for ($stacksize = 65536; $stacksize < 131072; $stacksize += 7168) +{ +    # Iterates 3 times (1024, 4096, 7168). +    for ($guardsize = 1024; $guardsize < 8192; $guardsize += 3072) +    { +	$i++; + +	print "stacksize: $stacksize, guardsize: $guardsize\n"; + +	`./guard_b $stacksize $guardsize >guard_b.out 2>&1`; + +	if (! -f "./guard_b.out") +	{ +	    print "not ok $i\n"; +	} +	else +	{ +	    `diff guard_b.exp guard_b.out >guard_b.diff 2>&1`; +	    if ($?) +	    { +		# diff returns non-zero if there is a difference. +		print "not ok $i\n"; +	    } +	    else +	    { +		print "ok $i\n"; +	    } +	} +    } +} diff --git a/lib/libpthread/test/hello_b.c b/lib/libpthread/test/hello_b.c new file mode 100644 index 000000000000..2eefa7f48bfe --- /dev/null +++ b/lib/libpthread/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/libpthread/test/hello_d.c b/lib/libpthread/test/hello_d.c new file mode 100644 index 000000000000..6d77526f16c7 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/hello_d.exp b/lib/libpthread/test/hello_d.exp new file mode 100644 index 000000000000..802992c4220d --- /dev/null +++ b/lib/libpthread/test/hello_d.exp @@ -0,0 +1 @@ +Hello world diff --git a/lib/libpthread/test/hello_s.c b/lib/libpthread/test/hello_s.c new file mode 100644 index 000000000000..942bf2dae0ae --- /dev/null +++ b/lib/libpthread/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/libpthread/test/join_leak_d.c b/lib/libpthread/test/join_leak_d.c new file mode 100644 index 000000000000..6532ca5bfc74 --- /dev/null +++ b/lib/libpthread/test/join_leak_d.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2001 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. + * + * $FreeBSD$ + * + * Test for leaked joined threads. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> +#include <pthread.h> + +#define	NITERATIONS	16384 +#define	MAXGROWTH	16384 + +void * +thread_entry(void *a_arg) +{ +	return NULL; +} + +int +main(void) +{ +	pthread_t	thread; +	int		i, error; +	char		*brk, *nbrk; +	unsigned	growth; + +	fprintf(stderr, "Test begin\n"); + +	/* Get an initial brk value. */ +	brk = sbrk(0); + +	/* Create threads and join them, one at a time. */ +	for (i = 0; i < NITERATIONS; i++) { +		if ((error = pthread_create(&thread, NULL, thread_entry, NULL)) +		    != 0) { +			fprintf(stderr, "Error in pthread_create(): %s\n", +			    strerror(error)); +			exit(1); +		} +		if ((error = pthread_join(thread, NULL)) != 0) { +			fprintf(stderr, "Error in pthread_join(): %s\n", +			    strerror(error)); +			exit(1); +		} +	} + +	/* Get a final brk value. */ +	nbrk = sbrk(0); + +	/* +	 * Check that the amount of heap space allocated is below an acceptable +	 * threshold.  We could just compare brk and nbrk, but the test could +	 * conceivably break if the internals of the threads library changes. +	 */ +	if (nbrk > brk) { +		/* Heap grows up. */ +		growth = nbrk - brk; +	} else if (nbrk <= brk) { +		/* Heap grows down, or no growth. */ +		growth = brk - nbrk; +	} + +	if (growth > MAXGROWTH) { +		fprintf(stderr, "Heap growth exceeded maximum (%u > %u)\n", +		    growth, MAXGROWTH); +	} +#if (0) +	else { +		fprintf(stderr, "Heap growth acceptable (%u <= %u)\n", +		    growth, MAXGROWTH); +	} +#endif + +	fprintf(stderr, "Test end\n"); +	return 0; +} diff --git a/lib/libpthread/test/join_leak_d.exp b/lib/libpthread/test/join_leak_d.exp new file mode 100644 index 000000000000..369a88dd2404 --- /dev/null +++ b/lib/libpthread/test/join_leak_d.exp @@ -0,0 +1,2 @@ +Test begin +Test end diff --git a/lib/libpthread/test/mutex_d.c b/lib/libpthread/test/mutex_d.c new file mode 100644 index 000000000000..21bf84850060 --- /dev/null +++ b/lib/libpthread/test/mutex_d.c @@ -0,0 +1,1552 @@ +/* + * 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 + +static void log_error(const char *, ...) __printflike(1, 2); +static void log_trace (const char *, ...) __printflike(1, 2); +static void log (const char *, ...) __printflike(1, 2); + +/*------------------------------------------------------------ + * 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, +		    ¶m) == 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, ¶m) == 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, ¶m) == 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, +	    ¶m) == 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, +		    ¶m) == 0); +		param.sched_priority = states[4].priority; +		assert (pthread_setschedparam (states[4].tid, SCHED_FIFO, +		    ¶m) == 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, +		    ¶m) == 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, +		    ¶m) == 0); +		param.sched_priority = states[4].priority; +		assert (pthread_setschedparam (states[4].tid, SCHED_RR, +		    ¶m) == 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, ¶m) == 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, +		    ¶m) == 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/libpthread/test/mutex_d.exp b/lib/libpthread/test/mutex_d.exp new file mode 100644 index 000000000000..de8a4e42d8e2 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/propagate_s.pl b/lib/libpthread/test/propagate_s.pl new file mode 100755 index 000000000000..9cd5fb054ae1 --- /dev/null +++ b/lib/libpthread/test/propagate_s.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl -w +# +# 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. +# +########################################################################### +# +# Verify that no cancellation points are propagated inside of libc_r. +# +# $FreeBSD$ +# + +@CPOINTS = ("aio_suspend", "close", "creat", "fcntl", "fsync", "mq_receive", +	    "mq_send", "msync", "nanosleep", "open", "pause", +	    "pthread_cond_timedwait", "pthread_cond_wait", "pthread_join", +	    "pthread_testcancel", "read", "sem_wait", "sigsuspend", +	    "sigtimedwait", "sigwait", "sigwaitinfo", "sleep", "system", +	    "tcdrain", "wait", "waitpid", "write"); + +print "1..1\n"; + +$cpoints = join '\|', @CPOINTS; +$regexp = "\" U \\(" . $cpoints . "\\\)\$\""; + +`nm -a /usr/lib/libc.a |grep $regexp >propagate_s.out`; +if (!open (NMOUT, "<./propagate_s.out")) +{ +    print "not ok 1\n"; +} +else +{ +    $propagations = 0;  + +    while (<NMOUT>) +    { +	$propagations++; +	print "$_\n"; +    } +    if ($propagations != 0) +    { +	print "$propagations propagation(s)\n"; +	print "not ok 1\n"; +    } +    else +    { +	print "ok 1\n"; +    } +    close NMOUT; +    unlink "propagate_s.out"; +} diff --git a/lib/libpthread/test/sem_d.c b/lib/libpthread/test/sem_d.c new file mode 100644 index 000000000000..b834591852d9 --- /dev/null +++ b/lib/libpthread/test/sem_d.c @@ -0,0 +1,133 @@ +/**************************************************************************** + * + * 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$ + * + ****************************************************************************/ + +#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/libpthread/test/sem_d.exp b/lib/libpthread/test/sem_d.exp new file mode 100644 index 000000000000..b0de3da1f5e6 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/sigsuspend_d.c b/lib/libpthread/test/sigsuspend_d.c new file mode 100644 index 000000000000..d2420ed84456 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/sigsuspend_d.exp b/lib/libpthread/test/sigsuspend_d.exp new file mode 100644 index 000000000000..901fa50dd2d1 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/sigwait_d.c b/lib/libpthread/test/sigwait_d.c new file mode 100644 index 000000000000..f3ccd6b98491 --- /dev/null +++ b/lib/libpthread/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 SIGUSR1 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/libpthread/test/sigwait_d.exp b/lib/libpthread/test/sigwait_d.exp new file mode 100644 index 000000000000..2e9b2c492525 --- /dev/null +++ b/lib/libpthread/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/libpthread/test/verify b/lib/libpthread/test/verify new file mode 100755 index 000000000000..2863e5c3fa0c --- /dev/null +++ b/lib/libpthread/test/verify @@ -0,0 +1,474 @@ +#!/usr/bin/perl -w +#-*-mode:perl-*- +############################################################################# +# +# Copyright (C) 1999-2001 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_objdir = "."; +$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, +            "o|objdir=s" => \$opt_objdir, +	    "u|ustats" => \$opt_ustats, +	    "z|zero" => \$opt_zero +	    ); + +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 ($#ARGV + 1 == 0) +{ +    print STDERR "No tests specified\n"; +    &usage(); +    exit 1; +} + +if ($opt_verbose) +{ +    print STDERR "Option values: h:$opt_help, v:$opt_verbose, " +	. "s:\"$opt_srcdir\", o:\"$opt_objdir\" " +	. "q:$opt_quiet, u:$opt_ustats, z:$opt_zero\n"; +    printf STDERR "Tests (%d total): @ARGV\n", $#ARGV + 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 (@ARGV) +{ +    # Strip out any whitespace in $test. +    $test =~ s/^\s*(.*)\s*$/$1/; + +    $okay = 1; + +    if (-e "$opt_srcdir/$test.exp") +    { +	# Diff mode. + +	($okay, $utime, $stime) = &run_test($test); + +	if (-e "$opt_objdir/$test.out") +	{ +	    `diff $opt_srcdir/$test.exp $opt_objdir/$test.out > $opt_objdir/$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 output file \"$opt_objdir/$test.out\"\n"; +	    } +	} + +	($hutime, $hstime) = &print_stats($test, $okay, 0, 0, $utime, $stime); +    } +    else +    { +	# Sequence mode. + +	($okay, $utime, $stime) = &run_test($test); + +	if (open (STEST_OUT, "<$opt_objdir/$test.out")) +	{ +	    $num_subtests = 0; +	    $num_failed_subtests = 0; + +	    while (defined($line = <STEST_OUT>)) +	    { +		if ($line =~ /1\.\.(\d+)/) +		{ +		    $num_subtests = $1; +		    last; +		} +	    } +	    if ($num_subtests == 0) +	    { +		$okay = 0; +		if ($opt_verbose) +		{ +		    print STDERR "Malformed or missing 1..n line\n"; +		} +	    } +	    else +	    { +		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 \"$opt_objdir/$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; +    } +} + +# Print summary stats. +$tt_str = sprintf ("%d / %d passed (%5.2f%%%%)", +		   ($#ARGV + 1) - ($#FAILED_TESTS + 1), +		   $#ARGV + 1, +		   (($#ARGV + 1) - ($#FAILED_TESTS + 1)) +		   / ($#ARGV + 1) * 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"; +    } +} + +if ($#FAILED_TESTS >= 0) +{ +    # One or more tests failed, so return an error. +    exit 1; +} +# End of main execution. + +sub run_test +{ +    my ($test) = @_; +    my ($okay) = 1; +    my ($tutime, $tstime); +    my ($utime, $stime, $cutime, $cstime); +    my (@TSTATS, @TPATH); +    my ($t_str); +    my ($srcdir, $objdir); + +    # Get the path component of $test, if any. +    @TPATH = split(/\//, $test); +    pop(@TPATH); +    $srcdir = join('/', ($opt_srcdir, @TPATH)); +    $objdir = join('/', ($opt_objdir, @TPATH)); + +    @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; +    `$opt_objdir/$test $srcdir $objdir > $opt_objdir/$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 +		    "\"$opt_objdir/$test > $opt_objdir/$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, "<$opt_objdir/$test.perf")) +	{ +	    print STDERR "Unable to open \"$opt_objdir/$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, ">$opt_objdir/$test.perf")) +	{ +	    if (!$opt_quiet) +	    { +		print STDERR "Unable to update \"$opt_objdir/$test.perf\"\n"; +	    } +	} +	else +	{ +	    print TEST_PERF "$utime $stime\n"; +	    close TEST_PERF; +	} +    } + +    return ($hutime, $hstime); +} + +sub usage +{ +    print <<EOF; +$0 usage: +    $0 [<options>] <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 "."). +    -o --objdir   | Path to object tree (default is "."). +    -u --ustats   | Update historical statistics (stored in "<test>.perf". +    -z --zero     | Consider non-zero exit code to be an error. +    --------------+------------------------------------------------------------- + +    If <test>.exp exists, <test>'s output is diff'ed with <test>.exp.  Any +    difference is considered failure. + +    If <test>.exp does not exist, 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. +EOF +}  | 
