Pseudo code for the pthread based donut problem PRODUCER CONSUMER get prod mutex get cons mutex check space count check donut count loop: loop: if space count == 0 if donut count == 0 wait prod_condx_var wait cons_condx_var put donut in queue take donut from queue dec space count dec donut count unlock prod mutex unlock cons mutex get cons mutex get prod mutex inc donut count inc space count unlock cons mutex unlock prod mutex signal cons_condx_var signal prod_condx_var Remember, when a condx_wait is called the associated mutex is implicitly released by the system and when the wait returns the system guarantees that the associated mutex has been re-acquired for the waking thread and that it is "safe" to proceed. THE FOLLOWING CODE EXAMPLES SHOULD PROVIDE HELP WITH THE pthread IMPLEMENTATION OF THE DONUTS PROBLEM....THIS VERSION INCLUDES A SIGNAL MANAGEMENT THREAD WHICH RESPONDS TO A SIGTERM (signal #15) SIGNAL....I INCLUDED IT AS A WAY OF STOPPING A RUN WHICH GETS INTO A DEADLOCK....JUST START YOUR PROGRAM IN THE BACKGROUND, AND IF IT STOPS MAKING PROGRESS SEND IT SIGTERM FROM THE KEYBOARD (i.e. shell prompt>> kill PID#)....THIS USE OF KILL WILL SEND SIGTERM BY DEFAULT....THE PROGRAM ALSO HAS TIME STAMP PROCEDURES WHICH COLLECT INFORMATION ABOUT HOW LONG (wall clock, not execution time) IT TOOK A RUN TO COMPLETE....YOU CAN USE THE SIGNAL CODE VERBATIM (I will discuss signal management with threads in class) compile line: gcc -o my_th_donuts my_th_donuts.c -lpthread /*****************************************************************/ /* INCLUDE FILES, DEFINES AND DECLARATIONS AS SEEN IN CLASS */ /* THESE BELONG IN A .h FILE */ /*****************************************************************/ #include #include #include #include #define NUMFLAVORS 4 #define NUMSLOTS 50 #define NUMCONSUMERS 5 struct donut_ring { int flavor [NUMFLAVORS] [NUMSLOTS]; int outptr [NUMFLAVORS]; int in_ptr [NUMFLAVORS]; int serial [NUMFLAVORS]; }; /**********************************************************************/ /* SIGNAL WAITER, PRODUCER AND CONSUMER THREAD FUNCTIONS */ /**********************************************************************/ void *sig_waiter ( void *arg ); void *producer ( void *arg ); void *consumer ( void *arg) ; void sig_handler ( int ); /********************** END HEADER FILE *******************/ /****************************/ /* GLOBAL VARIABLES */ /****************************/ #include "project_header.h" struct donut_ring shared_ring; int space_count [NUMFLAVORS]; int donut_count [NUMFLAVORS]; pthread_mutex_t prod [NUMFLAVORS]; pthread_mutex_t cons [NUMFLAVORS]; pthread_cond_t prod_cond [NUMFLAVORS]; pthread_cond_t cons_cond [NUMFLAVORS]; pthread_t thread_id [NUMCONSUMERS+1], sig_wait_id; int main ( int argc, char *argv[] ) { int i, j, k, nsigs; struct timeval randtime, first_time, last_time; struct sigaction new_act; int arg_array[NUMCONSUMERS + 1]; sigset_t all_signals; int sigs[] = { SIGBUS, SIGSEGV, SIGFPE }; pthread_attr_t thread_attr; struct sched_param sched_struct; /**********************************************************************/ /* INITIAL TIMESTAMP VALUE FOR PERFORMANCE MEASURE */ /**********************************************************************/ gettimeofday (&first_time, (struct timezone *) 0 ); for ( i = 0; i < NUMCONSUMERS + 1; i++ ) { arg_array [i] = i; /** SET ARRAY OF ARGUMENT VALUES **/ } /**********************************************************************/ /* GENERAL PTHREAD MUTEX AND CONDITION INIT AND GLOBAL INIT */ /**********************************************************************/ for ( i = 0; i < NUMFLAVORS; i++ ) { pthread_mutex_init ( &prod [i], NULL ); pthread_mutex_init ( &cons [i], NULL ); thread_cond_init ( &prod_cond [i], NULL ); pthread_cond_init ( &cons_cond [i], NULL ); shared_ring.outptr [i] = 0; shared_ring.in_ptr [i] = 0; shared_ring.serial [i] = 0; space_count [i] = NUMSLOTS; donut_count [i] = 0; } /**********************************************************************/ /* SETUP FOR MANAGING THE SIGTERM SIGNAL, BLOCK ALL SIGNALS */ /**********************************************************************/ sigfillset (&all_signals ); nsigs = sizeof ( sigs ) / sizeof ( int ); for ( i = 0; i < nsigs; i++ ) sigdelset ( &all_signals, sigs [i] ); sigprocmask ( SIG_BLOCK, &all_signals, NULL ); sigfillset (&all_signals ); for( i = 0; i < nsigs; i++ ) { new_act.sa_handler = sig_handler; new_act.sa_mask = all_signals; new_act.sa_flags = 0; if ( sigaction ( sigs[i], &new_act, NULL ) == -1 ){ perror("can't set signals: "); exit(1); } } printf ( "just before threads created\n" ); /*********************************************************************/ /* CREATE SIGNAL HANDLER THREAD, PRODUCER AND CONSUMERS */ /*********************************************************************/ if ( pthread_create (&sig_wait_id, NULL, sig_waiter, NULL) != 0 ){ printf ( "pthread_create failed " ); exit ( 3 ); } pthread_attr_init ( &thread_attr ); pthread_attr_setinheritsched ( &thread_attr, PTHREAD_INHERIT_SCHED ); #ifdef GLOBAL sched_struct.sched_priority = sched_get_priority_max(SCHED_OTHER); pthread_attr_setinheritsched ( &thread_attr, PTHREAD_EXPLICIT_SCHED ); pthread_attr_setschedpolicy ( &thread_attr, SCHED_OTHER ); pthread_attr_setschedparam ( &thread_attr, &sched_struct ); pthread_attr_setscope ( &thread_attr, PTHREAD_SCOPE_SYSTEM ); #endif if ( pthread_create (&thread_id[0], &thread_attr, producer, NULL ) != 0 ) { printf ( "pthread_create failed " ); exit ( 3 ); } for ( i = 1; i < NUMCONSUMERS + 1; i++ ) { if ( pthread_create ( &thread_id [i], &thread_attr, consumer, ( void * )&arg_array [i]) != 0 ){ printf ( "pthread_create failed" ); exit ( 3 ); } } printf ( "just after threads created\n" ); /*********************************************************************/ /* WAIT FOR ALL CONSUMERS TO FINISH, SIGNAL WAITER WILL */ /* NOT FINISH UNLESS A SIGTERM ARRIVES AND WILL THEN EXIT */ /* THE ENTIRE PROCESS....OTHERWISE MAIN THREAD WILL EXIT */ /* THE PROCESS WHEN ALL CONSUMERS ARE FINISHED */ /*********************************************************************/ for ( i = 1; i < NUMCONSUMERS + 1; i++ ) pthread_join ( thread_id [i], NULL ); /*****************************************************************/ /* GET FINAL TIMESTAMP, CALCULATE ELAPSED SEC AND USEC */ /*****************************************************************/ gettimeofday (&last_time, ( struct timezone * ) 0 ); if ( ( i = last_time.tv_sec - first_time.tv_sec) == 0 ) j = last_time.tv_usec - first_time.tv_usec; else{ if ( last_time.tv_usec - first_time.tv_usec < 0 ) { i--; j = 1000000 + ( last_time.tv_usec - first_time.tv_usec ); } else { j = last_time.tv_usec - first_time.tv_usec; } } printf ( "Elapsed consumer time is %d sec and %d usec\n", i, j ); printf ( "\n\n ALL CONSUMERS FINISHED, KILLING PROCESS\n\n" ); exit ( 0 ); } /*********************************************/ /* INITIAL PART OF PRODUCER..... */ /*********************************************/ void *producer ( void *arg ) { int i, j, k; unsigned short xsub [3]; struct timeval randtime; gettimeofday ( &randtime, ( struct timezone * ) 0 ); xsub [0] = ( ushort )randtime.tv_usec; xsub [1] = ( ushort ) ( randtime.tv_usec >> 16 ); xsub [2] = ( ushort ) ( pthread_self ); while ( 1 ) { j = nrand48 ( xsub ) & 3; pthread_mutex_lock ( &prod [j] ); while ( space_count [j] == 0 ) { pthread_cond_wait ( &prod_cond [j], &prod [j] ); } . . . pthread_mutex_unlock ( &prod [j] ); . . . etc......... return NULL; } /*********************************************/ /* ON YOUR OWN FOR THE CONSUMER......... */ /*********************************************/ void *consumer ( void *arg ) { int i, j, k, m, id; unsigned short xsub [3]; struct timeval randtime; id = *( int * ) arg; gettimeofday ( &randtime, ( struct timezone * ) 0 ); xsub [0] = ( ushort )randtime.tv_usec; xsub [1] = ( ushort ) ( randtime.tv_usec >> 16 ); xsub [2] = ( ushort ) ( pthread_self ); for( i = 0; i < 10; i++ ) { for( m = 0; m < 12; m++ ) { j = nrand48( xsub ) & 3; . . . etc......... } /*****************************************************************/ /* USING A MICRO-SLEEP AFTER EACH DOZEN WILL ALLOW ANOTHER */ /* CONSUMER TO START RUNNING, PROVIDING DESIRED RANDOM BEHVIOR */ /*****************************************************************/ usleep(1000); /* sleep 1 ms */ } return NULL: } /***********************************************************/ /* PTHREAD ASYNCH SIGNAL HANDLER ROUTINE... */ /***********************************************************/ void *sig_waiter ( void *arg ) { sigset_t sigterm_signal; int signo; sigemptyset ( &sigterm_signal ); sigaddset ( &sigterm_signal, SIGTERM ); sigaddset ( &sigterm_signal, SIGINT ); if sigwait ( &sigterm_signal, & signo) != 0 ) { printf ( "\n sigwait ( ) failed, exiting \n"); exit(2); } printf ( "process going down on SIGNAL (number %d)\n\n", signo ); exit ( 1 ); return NULL; } /**********************************************************/ /* PTHREAD SYNCH SIGNAL HANDLER ROUTINE... */ /**********************************************************/ void sig_handler ( int sig ) { pthread_t signaled_thread_id; int i, thread_index; signaled_thread_id = pthread_self ( ); for ( i = 0; i < (NUMCONSUMERS + 1 ); i++) { if ( signaled_thread_id == thread_id [i] ) { thread_index = i; break; } } printf ( "\nThread %d took signal # %d, PROCESS HALT\n", thread_index, sig ); exit ( 1 ); }