/* * Simulation of bounded-buffer problem. * * Program requires the following command-line arguments: * size of buffer * number of producers * number of consumers * number of items for each producer to produce * maximum time to delay in producers (milliseconds) * maximum time to delay in consumers (milliseconds) */ #define _XOPEN_SOURCE 500 /* may be needed to get usleep() in unistd.h */ #define _SVID_SOURCE || _BSD_SOURCE || _XOPEN_SOURCE /* similarly for *rand48_r() */ #include #include #include #include #include #include "timer.h" #include "getint.h" #include "bbuffer.h" void * producer_fcn(void *thread_arg); void * consumer_fcn(void *thread_arg); /* global variables */ int buffer_size; int num_producers; int num_consumers; int num_items_per_producer; int max_producer_delay; int max_consumer_delay; int seed; bbuffer_t buffer; double start_time; long elapsed_time(void) { return (get_time() - start_time) * 1000; } #include "bounded-buffer-synch.h" /* ---- main program ---- */ int main(int argc, char * argv[]) { char *usage_msg = "parameters:\n" "\tbuffsize\n" "\tproducers\n" "\tconsumers\n" "\titems per producer\n" "\tproducer_delay (msec)\n" "\tconsumer_delay (msec)\n" "\tseed for random delays (optional)\n"; if (argc < 7) { fprintf(stderr, usage_msg); exit(EXIT_FAILURE); } get_positive_int_or_exit(usage_msg, argv[1], &buffer_size); get_positive_int_or_exit(usage_msg, argv[2], &num_producers); get_positive_int_or_exit(usage_msg, argv[3], &num_consumers); get_positive_int_or_exit(usage_msg, argv[4], &num_items_per_producer); get_positive_int_or_exit(usage_msg, argv[5], &max_producer_delay); get_positive_int_or_exit(usage_msg, argv[6], &max_consumer_delay); if (argc > 7) { get_positive_int_or_exit(usage_msg, argv[7], &seed); } /* initialize timer */ start_time = get_time(); /* initialize buffer */ if (!bbuffer_create(&buffer, buffer_size)) { fprintf(stderr, "could not create buffer\n"); return EXIT_FAILURE; } /* initialize for synchronization */ init_synch(); /* set up IDs for threads */ int producerIDs[num_producers]; for (int i = 0; i < num_producers; ++i) producerIDs[i] = i; int consumerIDs[num_consumers]; for (int i = 0; i < num_consumers; ++i) consumerIDs[i] = i; /* start threads for producers and consumers */ pthread_t producers[num_producers]; for (int i = 0; i < num_producers; ++i) pthread_create(&producers[i], NULL, producer_fcn, (void *) &producerIDs[i]); pthread_t consumers[num_consumers]; for (int i = 0; i < num_consumers; ++i) pthread_create(&consumers[i], NULL, consumer_fcn, (void *) &consumerIDs[i]); /* wait for all producer threads to complete */ for (int i = 0; i < num_producers; ++i) pthread_join(producers[i], NULL); /* * wait for user input to exit * ugly hack but keeping track of when all generated items have been consumed adds * significant complexity */ printf("****enter anything other than blank line to exit****\n"); getchar(); /* clean up and exit */ cleanup_synch(); bbuffer_destroy(&buffer); return EXIT_SUCCESS; } /* ---- code to be executed by each producer ---- */ void * producer_fcn(void * thread_arg) { int myID = * (int *) thread_arg; /* use *rand48_r because they're thread-safe and rand() is not */ struct drand48_data rand_local_save; /* unique sequence in each thread */ srand48_r(seed + myID, &rand_local_save); long int rand_out; for (int i = 0; i < num_items_per_producer; ++i) { /* simulate producing item */ lrand48_r(&rand_local_save, &rand_out); int delay = (rand_out % max_producer_delay) + 1; usleep(delay * 1000); bbuffer_item_t item = { myID, i }; printf("at time %ld producer %d produced item (%d %d)\n", elapsed_time(), myID, item.producer, item.number); /* put with synchronization */ put_with_synch(myID, item, &buffer); } pthread_exit((void* ) NULL); } /* ---- code to be executed by each consumer ---- */ void * consumer_fcn(void * thread_arg) { int myID = * (int *) thread_arg; /* use *rand48_r because they're thread-safe and rand() is not */ struct drand48_data rand_local_save; /* unique sequence in each thread */ srand48_r(seed + myID + num_producers, &rand_local_save); long int rand_out; /* delay to make things more interesting -- give producers a head start */ usleep(max_consumer_delay * 1000); /* run until program is stopped -- ugly hack but see comment in main() */ while (true) { /* get with_synchronization */ bbuffer_item_t item = get_with_synch(myID, &buffer); /* simulate consuming item */ lrand48_r(&rand_local_save, &rand_out); int delay = (rand_out % max_consumer_delay) + 1; usleep(delay * 1000); printf("at time %ld consumer %d consumed item (%d %d)\n", elapsed_time(), myID, item.producer, item.number); } pthread_exit((void* ) NULL); }