| /* 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); |
| } |