diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/ChangeLog | 5 | ||||
| -rw-r--r-- | tests/eyal1.c | 345 | 
2 files changed, 350 insertions, 0 deletions
| diff --git a/tests/ChangeLog b/tests/ChangeLog index dd323d9..081218f 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,8 @@ +1998-09-13  Ben Elliston  <bje@cygnus.com> + +	* eyal1.c: New file; contributed by Eyal Lebedinsky +	<eyal@eyal.emu.id.au>. +  1998-09-12  Ben Elliston  <bje@cygnus.com>  	* exit2.c (func): Return a value. diff --git a/tests/eyal1.c b/tests/eyal1.c new file mode 100644 index 0000000..e32d29e --- /dev/null +++ b/tests/eyal1.c @@ -0,0 +1,345 @@ +/* Simple POSIX threads program. + * + * Author: Eyal Lebedinsky eyal@eyal.emu.id.au + * Written: Sep 1998. + * Version Date: 12 Sep 1998 + * + * Do we need to lock stdout or is it thread safe? + * + * Used: + *	pthread_t + *	pthread_attr_t + *	pthread_create() + *	pthread_join() + *	pthread_mutex_t + *	PTHREAD_MUTEX_INITIALIZER + *	pthread_mutex_init() [not used now] + *	pthread_mutex_destroy() + *	pthread_mutex_lock() + *	pthread_mutex_trylock() + *	pthread_mutex_unlock() + * + * What this program does is establish a work queue (implemented using + * four mutexes for each thread). It then schedules work (by storing + * a number in 'todo') and releases the threads. When the work is done + * the threads will block. The program then repeats the same thing once + * more (just to test the logic) and when the work is done it destroyes + * the threads. + * + * The 'work' we do is simply burning CPU cycles in a loop. + * The 'todo' work queue is trivial - each threads pops one element + * off it by incrementing it, the poped number is the 'work' to do. + * When 'todo' reaches the limit (nwork) the queue is considered + * empty. + * + * The number displayed at the end is the amount of work each thread + * did, so we can see if the load was properly distributed. + * + * The program was written to test a threading setup (not seen here) + * rather than to demonstrate correct usage of the pthread facilities. + * + * Note how each thread is given access to a thread control structure + * (TC) which is used for communicating to/from the main program (e.g. + * the threads knows its 'id' and also filles in the 'work' done). +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include <pthread.h> + + +struct thread_control { +	int		id; +	pthread_t	thread;		/* thread id */ +	pthread_mutex_t	mutex_start; +	pthread_mutex_t	mutex_started; +	pthread_mutex_t	mutex_end; +	pthread_mutex_t	mutex_ended; +	long		work;		/* work done */ +	int		stat;		/* pthread_init status */ +}; +typedef struct thread_control	TC; + +static TC		*tcs = NULL; +static int		nthreads = 2; +static int		nwork = 0; +static int		quiet = 0; + +static int		todo = -1; + +static pthread_mutex_t	mutex_todo = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t	mutex_stdout = PTHREAD_MUTEX_INITIALIZER; + +/*static pthread_attr_t    pthread_attr_default;*/ + + +static void +die (int ret) +{ +	if (NULL != tcs) { +		free (tcs); +		tcs = NULL; +	} + +	if (ret) +		exit (ret); +} + + +static void +waste_time (int n) +{ +	int		i; +	double		f; + +	f = rand (); + +	for (i = n*100; i > 0; --i) { +		f = sqrt (f) * f + 10000.0; +	} +} + +static int +do_work_unit (int who, int n) +{ +	int		i; +	static int	nchars = 0; + +	if (quiet) +		i = 0; +	else { +/* get lock on stdout +*/ +		if (pthread_mutex_lock (&mutex_stdout)) +			return (-1); + +/* do our job +*/ +		i = printf ("%c", +			"0123456789abcdefghijklmnopqrstuvwxyz"[who]); +		if (!(++nchars % 50)) +			printf ("\n"); +		fflush (stdout); + +/* release lock on stdout +*/ +		if (pthread_mutex_unlock (&mutex_stdout)) +			return (-2); +	} + +	n = rand () % 10000;	/* ignore incoming 'n' */ +	waste_time (n); + +	return (n); +} + +static int +print_server (void *ptr) +{ +	int		mywork; +	int		n; +	TC		*tc = (TC *)ptr; + +	if (pthread_mutex_lock (&tc->mutex_started)) +		return (-1); + +	for (;;) { +		if (pthread_mutex_lock (&tc->mutex_start)) +			return (-2); +		if (pthread_mutex_unlock (&tc->mutex_start)) +			return (-3); +		if (pthread_mutex_lock (&tc->mutex_ended)) +			return (-4); +		if (pthread_mutex_unlock (&tc->mutex_started)) +			return (-5); + +		for (;;) { + +/* get lock on todo list +*/ +			if (pthread_mutex_lock (&mutex_todo)) +				return (-6); +			mywork = todo; +			if (todo >= 0) { +				++todo; +				if (todo >= nwork) +					todo = -1; +			} +			if (pthread_mutex_unlock (&mutex_todo)) +				return (-7); + +			if (mywork < 0) +				break; + +			if ((n = do_work_unit (tc->id, mywork)) < 0) +				return (-8); +			tc->work += n; +		} + +		if (pthread_mutex_lock (&tc->mutex_end)) +			return (-9); +		if (pthread_mutex_unlock (&tc->mutex_end)) +			return (-10); +		if (pthread_mutex_lock (&tc->mutex_started)) +			return (-11); +		if (pthread_mutex_unlock (&tc->mutex_ended)) +			return (-12); + +		if (-2 == mywork) +			break; +	} + +	if (pthread_mutex_unlock (&tc->mutex_started)) +		return (-13); + +	return (0); +} + +static int +dosync (void) +{ +	int		i; + +	for (i = 0; i < nthreads; ++i) { +		if (pthread_mutex_lock (&tcs[i].mutex_end)) +			return (-1); +		if (pthread_mutex_unlock (&tcs[i].mutex_start)) +			return (-2); +		if (pthread_mutex_lock (&tcs[i].mutex_started)) +			return (-3); +		if (pthread_mutex_unlock (&tcs[i].mutex_started)) +			return (-4); +	} + +/* Now threads do their work +*/ +	for (i = 0; i < nthreads; ++i) { +		if (pthread_mutex_lock (&tcs[i].mutex_start)) +			return (-5); +		if (pthread_mutex_unlock (&tcs[i].mutex_end)) +			return (-6); +		if (pthread_mutex_lock (&tcs[i].mutex_ended)) +			return (-7); +		if (pthread_mutex_unlock (&tcs[i].mutex_ended)) +			return (-8); +	} + +	return (0); +} + +static int +dowork (void) +{ +	todo = 0; +	if (dosync () < 0) +		return (-1); + +	todo = 0; +	if (dosync () < 0) +		return (-2); + +	return (0); +} + +int +main (int argc, char *argv[]) +{ +	int		i; +	int		nargs; + +	nthreads  = 1; +	nwork = 100; +	nargs = 0; +	for (i = 1; i < argc; ++i) { +		if (!strcmp ("-q", argv[i])) { +			quiet = 1; +			continue; +		} +		if (!strcmp ("-h", argv[i])) { +			printf ("usage: pthreads [nthreads] [nwork] [-q]\n"); +			exit (0); +		} +		switch (++nargs) { +		case 1: +			nthreads  = atoi (argv[i]); +			if (nthreads > 36) { +				printf ("max 36 threads allowed\n"); +				die (1); +			} +			break; +		case 2: +			nwork = atoi (argv[i]); +			break; +		default: +			printf ("bad argument '%s'\n", argv[i]); +			die (1); +			break; +		} +	} + +	if (NULL == (tcs = calloc (nthreads, sizeof (*tcs)))) +		die (1); + +/* Launch threads +*/ +	for (i = 0; i < nthreads; ++i) { +		tcs[i].id = i; +		pthread_mutex_init (&tcs[i].mutex_start, NULL); +		pthread_mutex_init (&tcs[i].mutex_started, NULL); +		pthread_mutex_init (&tcs[i].mutex_end, NULL); +		pthread_mutex_init (&tcs[i].mutex_ended, NULL); +		tcs[i].work = 0; +		if (pthread_mutex_lock (&tcs[i].mutex_start)) +			{} +		tcs[i].stat = pthread_create (&tcs[i].thread, +			NULL /*&pthread_attr_default*/, +			(void*)&print_server, (void *)&tcs[i]); + +/* Wait for thread initialisation +*/ +		while (!pthread_mutex_trylock (&tcs[i].mutex_started)) +			pthread_mutex_unlock (&tcs[i].mutex_started); +	} + +	dowork (); + +/* Terminate threads +*/ +	todo = -2;	/* please terminate */ +	if (dosync () < 0) +		die (2); + +	for (i = 0; i < nthreads; ++i) { +		if (0 == tcs[i].stat) +			pthread_join (tcs[i].thread, NULL); +	} + +/* destroy locks +*/ +	pthread_mutex_destroy (&mutex_stdout); +	pthread_mutex_destroy (&mutex_todo); + +/* Cleanup +*/ +	printf ("\n"); + +/* Show results +*/ +	for (i = 0; i < nthreads; ++i) { +		printf ("%2d ", i); +		if (0 == tcs[i].stat) +			printf ("%10ld\n", tcs[i].work); +		else +			printf ("failed %d\n", tcs[i].stat); +		pthread_mutex_destroy (&tcs[i].mutex_start); +		pthread_mutex_destroy (&tcs[i].mutex_started); +		pthread_mutex_destroy (&tcs[i].mutex_end); +		pthread_mutex_destroy (&tcs[i].mutex_ended); +	} + +	die (0); + +	return (0); +} | 
