// Write a C program to solve the readers and writers Problem.

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

#define NR 3 // NUM_READERS
#define NW 2 // NUM_WRITERS

// shared data structure - simple integer
int sd = 0; // shared_data

// sync variables
pthread_mutex_t m;  // mutex for rc protection
sem_t w;             // semaphore for writer access control
int rc = 0;    // read_count: tracks active readers

void* r(void* arg) { // reader
    int id = *((int*)arg);
    while (1) {
        // read delay sim
        sleep(1);

        // critical section entry
        pthread_mutex_lock(&m);
        rc++;
        if (rc == 1) {
            // first reader blocks writers
            sem_wait(&w);
        }
        pthread_mutex_unlock(&m);

        // critical section
        printf("Reader %d is reading sd = %d\n", id, sd);
        sleep(2);  // read time sim

        // critical section exit
        pthread_mutex_lock(&m);
        rc--;
        if (rc == 0) {
            // last reader unblocks writers
            sem_post(&w);
        }
        pthread_mutex_unlock(&m);
    }
    pthread_exit(NULL);
}

void* w_func(void* arg) { // writer
    int id = *((int*)arg);
    while (1) {
        // write delay sim
        sleep(3);

        // critical section entry
        sem_wait(&w);  // exclusive access control

        // critical section
        sd++; // shared_data modification
        printf("Writer %d is writing: sd becomes %d\n", id, sd);
        sleep(2);  // write time sim

        // critical section exit
        sem_post(&w);
    }
    pthread_exit(NULL);
}

int main(void) {
    pthread_t rts[NR], wts[NW]; // reader/writer threads
    int rids[NR], wids[NW]; // reader/writer ids
    int i;

    // sync init
    pthread_mutex_init(&m, NULL);
    sem_init(&w, 0, 1);  // binary semaphore init

    // reader thread creation
    for (i = 0; i < NR; i++) {
        rids[i] = i + 1;
        pthread_create(&rts[i], NULL, r, &rids[i]);
    }

    // writer thread creation
    for (i = 0; i < NW; i++) {
        wids[i] = i + 1;
        pthread_create(&wts[i], NULL, w_func, &wids[i]);
    }

    // thread joining
    for (i = 0; i < NR; i++) {
        pthread_join(rts[i], NULL);
    }
    for (i = 0; i < NW; i++) {
        pthread_join(wts[i], NULL);
    }

    // cleanup
    pthread_mutex_destroy(&m);
    sem_destroy(&w);

    return 0;
}