#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <semaphore.h>
#define THREADNUM 5
#define BUFF_SIZE 10

sem_t semaphore1; // keeps track of how many empty slots are there?
sem_t semaphore2; // keeps track of how many are full?
int s = 5;
int counter = 0;
pthread_mutex_t mutexbuff;
int buff[BUFF_SIZE];
int in = 0; // producer index
int out = 0; // consumer index

void* producer(void* args) {
    while (1) {
        int x = rand() % 100;
        printf("produced %d\n", x);
        sleep(1);
        sem_wait(&semaphore1);
        pthread_mutex_lock(&mutexbuff);
        buff[in] = x;
        in = (in + 1) % BUFF_SIZE;
        counter++;
        pthread_mutex_unlock(&mutexbuff);
        sem_post(&semaphore2);

        if (counter >= s) {
            break;
            exit(0);
        }
    }
    return NULL;
}

void* consumer(void* args) {
    while (1) {
        sem_wait(&semaphore2);
        pthread_mutex_lock(&mutexbuff);

        if (counter >= s && in == out) {
            pthread_mutex_unlock(&mutexbuff);
            sem_post(&semaphore1);
            break;
        }

        int x = buff[out];
        out = (out + 1) % BUFF_SIZE;
        printf("consumed %d\n", x);

        pthread_mutex_unlock(&mutexbuff);
        sem_post(&semaphore1);
        sleep(1);
    }
    return NULL;
}

int main() {
    sem_init(&semaphore1, 0, BUFF_SIZE);
    sem_init(&semaphore2, 0, 0);
    pthread_mutex_init(&mutexbuff, NULL);

    pthread_t threads[THREADNUM];

    for (int i = 0; i < THREADNUM; i++) {
        if (i < 3) {
            pthread_create(&threads[i], NULL, producer, NULL);
        } else {
            pthread_create(&threads[i], NULL, consumer, NULL);
        }
    }

    for (int i = 0; i < THREADNUM; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            perror("Failed to join thread");
        }
    }

    sem_destroy(&semaphore1);
    sem_destroy(&semaphore2);
    pthread_mutex_destroy(&mutexbuff);
    return 0;
}
Q1: Write C++/C code to implement a system with three producer threads and two
consumer threads. Each producer generates random integers and writes them into a
shared bounded buffer that can hold up to 10 integers. The consumers read from this
buffer and print each integer, ensuring the buffer does not exceed capacity and that items
are consumed in the order they were produced.
Each producer should stop after collectively generating 50 integers. Once all items are
consumed, the program should free any resources and exit.
Constraints:
• Use semaphores exclusively for synchronization.
• Ensure no thread reads or writes outside buffer bounds.
Sample Workflow:
1. Producer Threads:
o Producer 1 generates an integer (e.g., 15) and writes it into the buffer.
o Producer 2 generates another integer (e.g., 42) and adds it next.
o Producer 3 generates an integer (e.g., 9) and adds it next.
2. Consumer Threads:
o Consumer 1 retrieves and prints the first integer (15), removing it from the
buffer.
o Consumer 2 retrieves and prints the next integer (42).
3. Process Continues:
o Producers and consumers operate concurrently, each waiting as necessary to
avoid buffer overflows or reading from an empty buffer.
o Once 50 integers are generated and consumed, the program exits. TODO: order and exiting isnt write