Skip to content
Snippets Groups Projects
Main.c 8.09 KiB
/**
 * 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;
}