|  | /* Simple POSIX threads program. | 
|  | * | 
|  | * | 
|  | * -------------------------------------------------------------------------- | 
|  | * | 
|  | *      Pthreads-win32 - POSIX Threads Library for Win32 | 
|  | *      Copyright(C) 1998 John E. Bossom | 
|  | *      Copyright(C) 1999,2005 Pthreads-win32 contributors | 
|  | * | 
|  | *      Contact Email: rpj@callisto.canberra.edu.au | 
|  | * | 
|  | *      The current list of contributors is contained | 
|  | *      in the file CONTRIBUTORS included with the source | 
|  | *      code distribution. The list can also be seen at the | 
|  | *      following World Wide Web location: | 
|  | *      http://sources.redhat.com/pthreads-win32/contributors.html | 
|  | * | 
|  | *      This library is free software; you can redistribute it and/or | 
|  | *      modify it under the terms of the GNU Lesser General Public | 
|  | *      License as published by the Free Software Foundation; either | 
|  | *      version 2 of the License, or (at your option) any later version. | 
|  | * | 
|  | *      This library is distributed in the hope that it will be useful, | 
|  | *      but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | *      Lesser General Public License for more details. | 
|  | * | 
|  | *      You should have received a copy of the GNU Lesser General Public | 
|  | *      License along with this library in the file COPYING.LIB; | 
|  | *      if not, write to the Free Software Foundation, Inc., | 
|  | *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | 
|  | * | 
|  | * -------------------------------------------------------------------------- | 
|  | * | 
|  | * 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 "test.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <math.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 = 10; | 
|  | static int		nwork = 100; | 
|  | 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 void | 
|  | die (int ret) | 
|  | { | 
|  | if (NULL != tcs) | 
|  | { | 
|  | free (tcs); | 
|  | tcs = NULL; | 
|  | } | 
|  |  | 
|  | if (ret) | 
|  | exit (ret); | 
|  | } | 
|  |  | 
|  |  | 
|  | static double | 
|  | waste_time (int n) | 
|  | { | 
|  | int		i; | 
|  | double	f, g, h, s; | 
|  |  | 
|  | s = 0.0; | 
|  |  | 
|  | /* | 
|  | * Useless work. | 
|  | */ | 
|  | for (i = n*100; i > 0; --i) | 
|  | { | 
|  | f = rand (); | 
|  | g = rand (); | 
|  | h = rand (); | 
|  | s += 2.0 * f * g / (h != 0.0 ? (h * h) : 1.0); | 
|  | } | 
|  | return s; | 
|  | } | 
|  |  | 
|  | static int | 
|  | do_work_unit (int who, int n) | 
|  | { | 
|  | int		i; | 
|  | static int	nchars = 0; | 
|  | double	f = 0.0; | 
|  |  | 
|  | if (quiet) | 
|  | i = 0; | 
|  | else { | 
|  | /* | 
|  | * get lock on stdout | 
|  | */ | 
|  | assert(pthread_mutex_lock (&mutex_stdout) == 0); | 
|  |  | 
|  | /* | 
|  | * do our job | 
|  | */ | 
|  | i = printf ("%c", "0123456789abcdefghijklmnopqrstuvwxyz"[who]); | 
|  |  | 
|  | if (!(++nchars % 50)) | 
|  | printf ("\n"); | 
|  |  | 
|  | fflush (stdout); | 
|  |  | 
|  | /* | 
|  | * release lock on stdout | 
|  | */ | 
|  | assert(pthread_mutex_unlock (&mutex_stdout) == 0); | 
|  | } | 
|  |  | 
|  | n = rand () % 10000;	/* ignore incoming 'n' */ | 
|  | f = waste_time (n); | 
|  |  | 
|  | /* This prevents the statement above from being optimised out */ | 
|  | if (f > 0.0) | 
|  | return(n); | 
|  |  | 
|  | return (n); | 
|  | } | 
|  |  | 
|  | static int | 
|  | print_server (void *ptr) | 
|  | { | 
|  | int		mywork; | 
|  | int		n; | 
|  | TC		*tc = (TC *)ptr; | 
|  |  | 
|  | assert(pthread_mutex_lock (&tc->mutex_started) == 0); | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | assert(pthread_mutex_lock (&tc->mutex_start) == 0); | 
|  | assert(pthread_mutex_unlock (&tc->mutex_start) == 0); | 
|  | assert(pthread_mutex_lock (&tc->mutex_ended) == 0); | 
|  | assert(pthread_mutex_unlock (&tc->mutex_started) == 0); | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  |  | 
|  | /* | 
|  | * get lock on todo list | 
|  | */ | 
|  | assert(pthread_mutex_lock (&mutex_todo) == 0); | 
|  |  | 
|  | mywork = todo; | 
|  | if (todo >= 0) | 
|  | { | 
|  | ++todo; | 
|  | if (todo >= nwork) | 
|  | todo = -1; | 
|  | } | 
|  | assert(pthread_mutex_unlock (&mutex_todo) == 0); | 
|  |  | 
|  | if (mywork < 0) | 
|  | break; | 
|  |  | 
|  | assert((n = do_work_unit (tc->id, mywork)) >= 0); | 
|  | tc->work += n; | 
|  | } | 
|  |  | 
|  | assert(pthread_mutex_lock (&tc->mutex_end) == 0); | 
|  | assert(pthread_mutex_unlock (&tc->mutex_end) == 0); | 
|  | assert(pthread_mutex_lock (&tc->mutex_started) == 0); | 
|  | assert(pthread_mutex_unlock (&tc->mutex_ended) == 0); | 
|  |  | 
|  | if (-2 == mywork) | 
|  | break; | 
|  | } | 
|  |  | 
|  | assert(pthread_mutex_unlock (&tc->mutex_started) == 0); | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dosync (void) | 
|  | { | 
|  | int		i; | 
|  |  | 
|  | for (i = 0; i < nthreads; ++i) | 
|  | { | 
|  | assert(pthread_mutex_lock (&tcs[i].mutex_end) == 0); | 
|  | assert(pthread_mutex_unlock (&tcs[i].mutex_start) == 0); | 
|  | assert(pthread_mutex_lock (&tcs[i].mutex_started) == 0); | 
|  | assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Now threads do their work | 
|  | */ | 
|  | for (i = 0; i < nthreads; ++i) | 
|  | { | 
|  | assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0); | 
|  | assert(pthread_mutex_unlock (&tcs[i].mutex_end) == 0); | 
|  | assert(pthread_mutex_lock (&tcs[i].mutex_ended) == 0); | 
|  | assert(pthread_mutex_unlock (&tcs[i].mutex_ended) == 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dowork (void) | 
|  | { | 
|  | todo = 0; | 
|  | dosync(); | 
|  |  | 
|  | todo = 0; | 
|  | dosync(); | 
|  | } | 
|  |  | 
|  | int | 
|  | main (int argc, char *argv[]) | 
|  | { | 
|  | int		i; | 
|  |  | 
|  | assert(NULL != (tcs = (TC *) calloc (nthreads, sizeof (*tcs)))); | 
|  |  | 
|  | /* | 
|  | * Launch threads | 
|  | */ | 
|  | for (i = 0; i < nthreads; ++i) | 
|  | { | 
|  | tcs[i].id = i; | 
|  |  | 
|  | assert(pthread_mutex_init (&tcs[i].mutex_start, NULL) == 0); | 
|  | assert(pthread_mutex_init (&tcs[i].mutex_started, NULL) == 0); | 
|  | assert(pthread_mutex_init (&tcs[i].mutex_end, NULL) == 0); | 
|  | assert(pthread_mutex_init (&tcs[i].mutex_ended, NULL) == 0); | 
|  |  | 
|  | tcs[i].work = 0; | 
|  |  | 
|  | assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0); | 
|  | assert((tcs[i].stat = | 
|  | pthread_create (&tcs[i].thread, | 
|  | NULL, | 
|  | (void *(*)(void *))print_server, | 
|  | (void *) &tcs[i]) | 
|  | ) == 0); | 
|  |  | 
|  | /* | 
|  | * Wait for thread initialisation | 
|  | */ | 
|  | { | 
|  | int trylock = 0; | 
|  |  | 
|  | while (trylock == 0) | 
|  | { | 
|  | trylock = pthread_mutex_trylock(&tcs[i].mutex_started); | 
|  | assert(trylock == 0 || trylock == EBUSY); | 
|  |  | 
|  | if (trylock == 0) | 
|  | { | 
|  | assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | dowork (); | 
|  |  | 
|  | /* | 
|  | * Terminate threads | 
|  | */ | 
|  | todo = -2;	/* please terminate */ | 
|  | dosync(); | 
|  |  | 
|  | for (i = 0; i < nthreads; ++i) | 
|  | { | 
|  | if (0 == tcs[i].stat) | 
|  | assert(pthread_join (tcs[i].thread, NULL) == 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * destroy locks | 
|  | */ | 
|  | assert(pthread_mutex_destroy (&mutex_stdout) == 0); | 
|  | assert(pthread_mutex_destroy (&mutex_todo) == 0); | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  |  | 
|  | assert(pthread_mutex_unlock(&tcs[i].mutex_start) == 0); | 
|  |  | 
|  | assert(pthread_mutex_destroy (&tcs[i].mutex_start) == 0); | 
|  | assert(pthread_mutex_destroy (&tcs[i].mutex_started) == 0); | 
|  | assert(pthread_mutex_destroy (&tcs[i].mutex_end) == 0); | 
|  | assert(pthread_mutex_destroy (&tcs[i].mutex_ended) == 0); | 
|  | } | 
|  |  | 
|  | die (0); | 
|  |  | 
|  | return (0); | 
|  | } |