From 1079e9e1d036a9a5d44ecf4c99ff42eb29366db3 Mon Sep 17 00:00:00 2001 From: bje Date: Sun, 13 Sep 1998 00:23:19 +0000 Subject: 1998-09-13 Ben Elliston * eyal1.c: New file; contributed by Eyal Lebedinsky . --- tests/ChangeLog | 5 + tests/eyal1.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 tests/eyal1.c 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 + + * eyal1.c: New file; contributed by Eyal Lebedinsky + . + 1998-09-12 Ben Elliston * 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 +#include +#include + +#include + + +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); +} -- cgit v1.2.3