/* * Simulation of bounded-buffer problem. * (Doesn't actually maintain a buffer, just the count of items in it.) * * Program requires the following command-line arguments: * size of buffer * number of producers * number of consumers * number of items to produce/consume * maximum time to delay in producers (milliseconds) * maximum time to delay in consumers (milliseconds) * * Code is designed to allow different implementations of parts involving * synchronization. * * To allow simulation to end gracefully, program keeps count of total * number of gets and puts and ends when both equal the number of items * to produce and consume. This requires some additional synchronization. */ #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 void * producer_fcn(void *thread_arg); void * consumer_fcn(void *thread_arg); /* global variables for runtime parameters */ int buffer_size; int num_producers; int num_consumers; int num_items; int max_producer_delay; int max_consumer_delay; int seed; /* global variable to simulate buffer */ int in_buffer = 0; /* global variables to allow simulation to end gracefully */ int requests_to_put = 0; int requests_to_get = 0; #include "getint.h" #include "timer.h" #include "test-bounded-buffer.h" #include "bounded-buffer.h" /* ---- main program ---- */ int main(int argc, char * argv[]) { char *usage_msg = "parameters:\n" "\tbuffsize\n" "\tproducers\n" "\tconsumers\n" "\titems\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); 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 */ init_start_time(); /* initialize for synchronization */ init_synch(buffer_size); /* 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 threads to complete */ for (int i = 0; i < num_producers; ++i) pthread_join(producers[i], NULL); for (int i = 0; i < num_consumers; ++i) pthread_join(consumers[i], NULL); /* clean up and exit */ end_synch(); 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; while (atomic_increment(&requests_to_put, num_items)) { lrand48_r(&rand_local_save, &rand_out); int delay = (rand_out % max_producer_delay) + 1; usleep(delay * 1000); printf("at time %ld producer %d produced item\n", elapsed_time(), myID); simulate_put_with_synch(myID); } 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); while (atomic_increment(&requests_to_get, num_items)) { simulate_get_with_synch(myID); 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\n", elapsed_time(), myID); } pthread_exit((void* ) NULL); } /* ---- simulated put, get ---- */ /* * simulate put -- just increment count of items in buffer and print */ void simulate_put(const int producerID) { in_buffer += 1; printf("at time %ld producer %d adding item, count now %d\n", elapsed_time(), producerID, in_buffer); } /* * simulate get -- just decrement count of items in buffer and print */ void simulate_get(const int consumerID) { in_buffer -= 1; printf("at time %ld consumer %d removing item, count now %d\n", elapsed_time(), consumerID, in_buffer); }