summaryrefslogtreecommitdiff
path: root/tests/eyal1.c
diff options
context:
space:
mode:
authorbje <bje>1998-09-13 00:23:19 +0000
committerbje <bje>1998-09-13 00:23:19 +0000
commit1079e9e1d036a9a5d44ecf4c99ff42eb29366db3 (patch)
tree3b1a390f65e4ac1a75550128c9cee10d362240bf /tests/eyal1.c
parent8536584f1efdd34256c037193c69e45ca35e336d (diff)
1998-09-13 Ben Elliston <bje@cygnus.com>
* eyal1.c: New file; contributed by Eyal Lebedinsky <eyal@eyal.emu.id.au>.
Diffstat (limited to 'tests/eyal1.c')
-rw-r--r--tests/eyal1.c345
1 files changed, 345 insertions, 0 deletions
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);
+}