/* producer-consumer-blocking-macos.c Jeff Ondich, 1 June 2019 This producer/consumer demo is producing 6-sided die rolls and consuming them by recording how many times each sum of two dice occurs. Run the program for a while, then hit Ctrl-C to get the resulting statistical report and terminate the program. */ #include #include #include #include #include #include #include #define BUFFER_CAPACITY 100 int gSharedBuffer[BUFFER_CAPACITY]; int gSharedBufferSize = 0; // We make the statistical report global so it's accessible from // both the consumer (which populates it) and from the interrupt // handler (which prints it). #define DICE_STATS_CAPACITY 13 int gDiceStats[DICE_STATS_CAPACITY]; dispatch_semaphore_t gFullSemaphore; dispatch_semaphore_t gEmptySemaphore; pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER; void *producerMain(void *arg); void *consumerMain(void *arg); void interruptHandler(int sig); int main() { // Seed the random number generator srand(time(0)); // Initialize the Ctrl-C / SIGINT handler if (signal(SIGINT, interruptHandler) != SIG_DFL) { fprintf(stderr, "I'm confused.\n"); } // Initialize the semaphores gEmptySemaphore = dispatch_semaphore_create(BUFFER_CAPACITY); gFullSemaphore = dispatch_semaphore_create(0); // Start the threads pthread_t producerThread, consumerThread; if (pthread_create(&producerThread, NULL, producerMain, 0) != 0) { perror("Can't create producer thread"); exit(1); } if (pthread_create(&consumerThread, NULL, consumerMain, 0) != 0) { perror( "Can't create consumer thread" ); exit( 1 ); } pthread_join(producerThread, NULL); pthread_join(consumerThread, NULL); return 0; } void *producerMain(void *arg) { while(1) { dispatch_semaphore_wait(gEmptySemaphore, DISPATCH_TIME_FOREVER); pthread_mutex_lock(&gLock); gSharedBuffer[gSharedBufferSize] = 1 + rand() % 6; gSharedBufferSize++; pthread_mutex_unlock(&gLock); dispatch_semaphore_signal(gFullSemaphore); } return NULL; } void *consumerMain(void *arg) { // Initialize the statistics int sum; for (sum = 0; sum < DICE_STATS_CAPACITY; sum++) { gDiceStats[sum] = 0; } while(1) { dispatch_semaphore_wait(gFullSemaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(gFullSemaphore, DISPATCH_TIME_FOREVER); pthread_mutex_lock(&gLock); sum = gSharedBuffer[gSharedBufferSize - 1] + gSharedBuffer[gSharedBufferSize - 2]; gSharedBufferSize -= 2; gDiceStats[sum]++; pthread_mutex_unlock(&gLock); dispatch_semaphore_signal(gEmptySemaphore); dispatch_semaphore_signal(gEmptySemaphore); } return NULL; } void interruptHandler(int sig) { int numberOfRolls = 0; for (int k = 0; k < DICE_STATS_CAPACITY; k++) { numberOfRolls += gDiceStats[k]; } fprintf(stderr, "\nSum Frequency\n"); for (int k = 0; k < DICE_STATS_CAPACITY; k++) { fprintf(stderr, "%3d %10d (%.2f%%)\n", k, gDiceStats[k], 100.0 * (float)gDiceStats[k] / (float)numberOfRolls); } fprintf(stderr, "\nTotal number of rolls: %d\n", numberOfRolls); exit(0); }