/** * Brandon Rodriguez * CS 4540 * 02-27-18 * a4 (Assignment 4) */ /** * Description: * * Args: * First arg determines overall sleep time for the program. * Main thread will directly use this arg as the sleep time, as long as it is greater than SLEEP_MIN. * Subthreads' max sleep will be this arg modded by SLEEP_MAX. And no less than SLEEP_MIN. * Second arg is number of producer threads to create. * Third arg is number of consumer threads to create. * * * Size of buffer is defined in buffer.h by BUFFER_SIZE. * Currently set to 5 to (nearly gauranteed) test: * Both multiple insertions and removals. * Buffer case of all indexes full. * Buffer case of all indexes empty. * * * Program first creates consumer and producer threads, based on provided args. * Once all threads are initialized, main sleeps based on args provided. * When main wakes up, it terminates all threads and closes program. * * * While main is sleeping, all threads enter loop of: * * Initially sleep for random period of time between SLEEP_MIN and SLEEP_MAX * (to ensure as much thread unpredictability as possible). * * If producer, generate random number between 0 and 10,000. * * Call (hopefully) thread-safe custom functions to access bounded buffer. * * If producer, put generated number into lowest available buffer index. * * If consumer, grab number from highest buffer index that is populated. * * Return out of thread-safe functions and read out either value that was added or value that was taken * (depending on if producer or consumer). If error occured, will be printed here. * * * To make random numbers less random by using a constant seed on every run, comment out line #122 in main. */ /** * Known Issues: * Ran with: * Varying min and max sleep times. * Up to 100 second total run-time. * Between 1 and 25 different consumers at once. * Between 1 and 25 different producers at once. * 1 consumer and 25 produers. * 25 consumers and 1 producer. * * Seems to be no issues, as far as I can tell. Reads from and writes to buffer (seemingly) as intended. */ // Import headers. #ifndef brodriguez_helper_functions #define brodriguez_helper_functions #include "brodriguez_helper_functions.h" #endif #include <time.h> #include <pthread.h> #include <unistd.h> #include "buffer.h" // Constant Defines. #define SLEEP_MIN 2 #define SLEEP_MAX 10 // Variable Declaration. int wait_on_main = 1; // Method Declaration. void* producer(); void* consumer(); /** * Program's main. * Initializes and runs program. */ int main(int argc, char* argv[]) { int index; int nap_length; int producer_count; int consumer_count; int result_int; int* max_time; pthread_t* producer_array; pthread_t* consumer_array; // Ensure correct number of commands. if (argc < 4) { fprintf(stderr, "Too few arguments. Please provide sleep time, number of producers, and number of consumers.\n"); exit(1); } else if (argc > 4) { fprintf(stderr, "Too many arguments. Please provide sleep time, number of producers, and number of consumers.\n"); exit(1); } // Initialize values. printf("Initializing...\n"); // Validate user input. nap_length = atoi(argv[1]); if (nap_length < SLEEP_MIN) { fprintf(stderr, "Invalid sleep length. Must be a minimum of %d seconds.\n", SLEEP_MIN); exit(1); } producer_count = atoi(argv[2]); if (producer_count <= 0) { fprintf(stderr, "Invalid number of producers. Must be greater than 0.\n"); exit(1); } consumer_count = atoi(argv[3]); if (consumer_count <= 0) { fprintf(stderr, "Invalid number of consumers. Must be greater than 0.\n"); exit(1); } max_time = calloc(1, sizeof(int)); *max_time = nap_length; // Seeding random generator. Uncoment this for consistent seeding every program run. srand(time(NULL)); intialize_item_buffer(); // Create producer thread(s). printf("Creating producers...\n"); producer_array = calloc(producer_count, sizeof(pthread_t)); for (index = 0; index < producer_count; index++) { printf("Main creating producer #%d.\n", index); pthread_create(&producer_array[index], NULL, producer, (void*) max_time); } // Create consumer thread(s). printf("Creating consumers...\n"); consumer_array = calloc(consumer_count, sizeof(pthread_t)); for (index = 0; index < consumer_count; index++) { printf("Main creating consumer #%d.\n", index); pthread_create(&consumer_array[index], NULL, consumer, (void*) max_time); } // Sleep main. sleep(1); printf("\nInitialization complete.\n"); printf("Main will now sleep for %d seconds.\n\n\n", nap_length); wait_on_main = 0; sleep(atoi(argv[1])); printf("\n\n"); // Terminate program. First tell all threads to cancel. for (index = 0; index < producer_count; index++) { printf("Terminating producer #%d.\n", index); result_int = pthread_cancel(producer_array[index]); if (result_int < 0) { fprintf(stderr, "Error terminating producer thread #%d.\n", index); } } for (index = 0; index < consumer_count; index++) { printf("Terminating consumer #%d.\n", index); result_int = pthread_cancel(consumer_array[index]); if (result_int < 0) { fprintf(stderr, "Error terminating consumer thread #%d.\n", index); } } // Then actually wait for threads to cancel. Producers first, then consumers. for (index = 0; index < producer_count; index++) { // printf("Joining producer thread #%d.\n", index); pthread_join(producer_array[index], NULL); printf("Joined producer thread #%d.\n", index); } for (index = 0; index < consumer_count; index++) { // printf("Joining consumer thread #%d.\n", index); pthread_join(consumer_array[index], NULL); printf("Joined consumer thread #%d.\n", index); } free(max_time); free(producer_array); free(consumer_array); exit(0); } /** * Function for producer threads to run. */ void* producer(void* max_time) { int sleep_actual; int thread_sleep_max; buffer_item item; printf("Producer thread created.\n"); while(wait_on_main != 0) { sleep(wait_on_main); } // Get random max for individual thread sleep. Max possible is SLEEP_MAX. Minimum is SLEEP_MIN. thread_sleep_max = *(int*)max_time % SLEEP_MAX; if (thread_sleep_max < SLEEP_MIN) { thread_sleep_max = SLEEP_MAX; } // Loop until thread is terminated. while(1) { // Sleep thread for random time. sleep_actual = rand() % thread_sleep_max; // printf("Sleeping producer for %d seconds.\n", sleep_actual); sleep(sleep_actual); item = rand() % 10000; if (insert_item(item) != 0) { fprintf(stderr, "Producer failed to properly insert item %d.\n", item); } else { printf("Producer inserted item %d.\n", item); } } return NULL; } /** * Function for consumer threads to run. */ void* consumer(void* max_time) { int sleep_actual; int thread_sleep_max; int* item; printf("Consumer thread created.\n"); while(wait_on_main != 0) { sleep(wait_on_main); } // Get random max for individual thread sleep. Max possible is SLEEP_MAX. Minimum is SLEEP_MIN. thread_sleep_max = *(int*)max_time % SLEEP_MAX; if (thread_sleep_max < SLEEP_MIN) { thread_sleep_max = SLEEP_MAX; } // Loop until thread is terminated. while(1) { // Sleep thread for random time. sleep_actual = rand() % thread_sleep_max; // printf("Sleeping consumer for %d seconds.\n", sleep_actual); sleep(sleep_actual); item = calloc(1, sizeof(int)); if ((remove_item(item)) != 0) { fprintf(stderr, "Consumer failed to properly remove item.\n"); } else { printf("Consumer removed item with value %d.\n", *item); } free(item); } return NULL; }