/* Lab4 Examine Multiprocessor issues using threads to simulate multiple CPU's */ #include "../inc/local_mutex.h" #include #include #include #include #include #include #include #include #include /* #define DEBUG 1 #define TRACE 1 #include "../inc/types.h" #include "../inc/debug.h" */ #define enter(a) #define rtnval(a) return(a) #define rtn_noval(a) return #define TRUE 1 #define FALSE 0 #define Log_debug(b) static char const ident[] = "$Id: lab4_1.c,v 1.1 2004/04/13 01:36:19 gmiller Exp $"; void *do_one_thing (int *); void *do_another_thing (int *); void do_wrap_up (int, int); pthread_cond_t start_cv = PTHREAD_COND_INITIALIZER; pthread_mutex_t start_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t done_cv = PTHREAD_COND_INITIALIZER; pthread_mutex_t done_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER; int r1 = 0, r2 = 0, done = 0; /* Funtion to implement a "pretty" version of the output code so that the main functions do get too UGLY. */ void t_printf (char *fmt, ...) { va_list ap; static char my_fn[] = "t_printf"; //enter(my_fn); pthread_mutex_lock (&print_mutex); va_start (ap, fmt); vprintf (fmt, ap); va_end (ap); fflush (stdout); pthread_mutex_unlock (&print_mutex); //rtn_noval(); } /* Process 1 CPU work */ void * do_one_thing (int *pnum_times) { int i, j, x; static char my_fn[] = "do_one_thing"; enter(my_fn); /* Lock start_mutex so we can wait for a startup signal on the condition variable. */ pthread_mutex_lock (&start_mutex); t_printf ("waiting do_one_thing\n"); fflush (stdout); /* pthread_cond_wait set's up wait on condition variable and unlocks the mutex and then waits for the condition variable to signal. Then it will lock the mutex */ pthread_cond_wait (&start_cv, &start_mutex); /* Ok ... we got control of the mutex so it is locked. We don't need it locked so we unlock it to allow another thread to do it's thing. */ pthread_mutex_unlock (&start_mutex); /* Since our threads can do printf over each other I have chosen to control output with a mutex so none of the messages from each thread will over-write each other. ** Note ** This should be done in a function or macro to keep from making the code so UGLY. */ //sleep(1); t_printf ("Starting ... do_one_thing\n"); Log_debug ("Starting ... do_one_thing"); for (i = 0; i < 100; i++) { t_printf ("+"); for (j = 0; j < 100000; j++) x = x + i; (*pnum_times)++; } Log_debug ("Ready to lock done in do_one_thing"); pthread_mutex_lock (&done_mutex); Log_debug ("Done locked in do_one_thing"); t_printf ("@("); done++; Log_debug ("Send signal for done in do_one_thing"); pthread_cond_signal (&done_cv); Log_debug ("Sent signal for done in do_one_thing"); Log_debug ("Ready to unlock done in do_one_thing"); pthread_mutex_unlock (&done_mutex); Log_debug ("Done unlocked in do_one_thing"); t_printf ("!("); // sleep (10); pthread_exit ((void *) NULL); rtnval(NULL); } void * do_another_thing (int *pnum_times) { int i, j, x; static char my_fn[] = "do_another_thing"; enter(my_fn); pthread_mutex_lock (&start_mutex); t_printf ("waiting do_another_thing\n"); fflush (stdout); pthread_cond_wait (&start_cv, &start_mutex); pthread_mutex_unlock (&start_mutex); //sleep(1); t_printf ("Starting ... do_another_thing\n"); Log_debug ("Starting ... do_another_thing"); for (i = 0; i < 100; i++) { t_printf ("-"); for (j = 0; j < 100000; j++) x = x + i; (*pnum_times)++; } Log_debug ("Ready to lock done in do_another_thing"); pthread_mutex_lock (&done_mutex); Log_debug ("Done locked in do_another_thing"); t_printf ("@)"); done++; Log_debug ("Send signal for done in do_another_thing"); pthread_cond_signal (&done_cv); Log_debug ("Sent signal for done in do_another_thing"); Log_debug ("Ready to unlock done in do_another_thing"); pthread_mutex_unlock (&done_mutex); Log_debug ("Done unlocked in do_another_thing"); t_printf ("!)"); // sleep (10); pthread_exit ((void *) NULL); rtnval(NULL); } void do_wrap_up (int one_times, int another_times) { int total; static char my_fn[] = "do_wrap_up"; enter(my_fn); t_printf ("\n"); total = one_times + another_times; t_printf ("wrap up: doing another %d, another %d, total %d\n", one_times, another_times, total); fflush (stdout); rtn_noval(); } int main () { pthread_t thread1, thread2; int complete = FALSE; static char my_fn[] = "lab4_1"; enter(my_fn); // set_debug (); pthread_create (&thread1, NULL, (void *) do_one_thing, (void *) &r1); pthread_create (&thread2, NULL, (void *) do_another_thing, (void *) &r2); sleep (5); t_printf ("Ready to start threads ..... \n"); fflush (stdout); pthread_mutex_lock (&start_mutex); pthread_cond_broadcast (&start_cv); pthread_mutex_unlock (&start_mutex); while (complete == FALSE) { t_printf ("&"); Log_debug ("Inside check loop"); /* Condition wait required the mutex to be locked before the call so the conf=dition variable can be modified. After the modification the mutex is unlocked to initiate the wait. When woken up the mutex is again locked to that we have freedom to checke the condition variaible again. */ pthread_mutex_lock (&done_mutex); if (done >= 2) { complete = TRUE; pthread_mutex_unlock (&done_mutex); continue; } Log_debug ("Now do cond_wait"); pthread_cond_wait (&done_cv, &done_mutex); Log_debug ("cond_wait complete"); t_printf ("%"); if (done >= 2) complete = TRUE; Log_debug ("1. Done needs unlocking"); pthread_mutex_unlock (&done_mutex); Log_debug ("1. Done is unlocked"); t_printf ("#"); } //sleep(1); Log_debug ("Loop has exited"); t_printf ("^"); t_printf ("\nAll threads complete\n"); fflush (stdout); pthread_join(thread1, NULL); pthread_join(thread2, NULL); do_wrap_up (r1, r2); fflush (stdout); rtnval(0); }