You are on page 1of 2

// Name: Benjamin Shafer

// Date: 2023-08-01
// Title: Lab6 - Step 2
// Description: : This program recreates the producer-consumer problem where
// the alphabet is produced and consumed. It creates 2 threads, 1 for the producer
// and 1 for the consumer. It uses 3 semaphores to synchronize a buffer that is
// shared by the producer and consumer threads. One semaphore is used control
// filling the buffer, one is used to control emptying the buffer, and one is used
// as a mutex lock for the buffer. The threads will print out what character is
being
// produced or consumed. Once threads are finished with the alphabet, the main
thread exits.

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include <semaphore.h>

//Shared data: semaphore full, empty, mutex;


//pool of n buffers, each can hold one item
//mutex provides mutual exclusion to the buffer pool
//empty and full count the number of empty and full buffers
//Initially: full = 0, empty = MAX_BUFFER, mutex = 1

#define MAX_BUFFER 5 // Size of the buffer


sem_t mutex; // Semaphore used as a mutex lock for the buffer's critical sections
sem_t empty; // Semaphore used to track the empty spots in the buffer
sem_t full; // Semaphore used to track the full spots in the buffer

int buffer[MAX_BUFFER]; // Buffer data shared between threads


int next_in = 0; // Index to insert data into buffer
int next_out = 0; // Index to pull data from buffer

char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Alphabet to produce/consume

// Producer method to add values to the buffer.


void put(char value) {
buffer[next_in] = value;
next_in = (next_in + 1) % MAX_BUFFER;
printf("Producer: %c\n", value);
}

// Consumer method to pull values from the buffer.


char get() {
char tmp = buffer[next_out];
next_out = (next_out + 1) % MAX_BUFFER;
printf("Consumer: %c\n", tmp);
return tmp;
}

// Method used to start a producer thread.


void *produce(void *arg) {
int i;
for (int i = 0; i < sizeof(alphabet) - 1; i++) {
sem_wait(&empty); // Wait for empty buffer
sem_wait(&mutex); // Wait for critical section
put(alphabet[i]);
sem_post(&mutex); // Release the critical section
sem_post(&full); // Signal the buffer is full.
}
}

// Method used to start a consumer thread.


void *consume(void *arg) {
int i;
for (i=0; i < sizeof(alphabet) - 1; i++) {
sem_wait(&full); // Wait for full buffer
sem_wait(&mutex); // Wait for critical section
int tmp = get();
sem_post(&mutex); // Release the critical section
sem_post(&empty); // Signal the buffer is empty.
}
}

int main(int argc, char *argv[]) {

// Initialize each semaphore and verify success.


int rc;
rc = sem_init(&empty, 0, MAX_BUFFER); // Initialize the empty semaphore with
MAX_BUFFERS
assert(rc == 0);
rc = sem_init(&full, 0, 0); // Initialize the full semaphore with 0
assert(rc == 0);
rc = sem_init(&mutex, 0, 1); // Initialize the mutex semaphore with 1
assert(rc == 0);

// Create and Start the producer/consumer threads.


pthread_t producer, consumer;
pthread_create(&producer, NULL, produce, NULL);
pthread_create(&consumer, NULL, consume, NULL);

// Have the main thread wait for the producer/consumer threads to finish.
pthread_join(producer, NULL);
pthread_join(consumer, NULL);

printf("Main thread done.\n");

// Destroy the semaphores


sem_destroy(&mutex);
sem_destroy(&empty);
sem_destroy(&full);

return 0;
}

You might also like