/* Classic producer consumer problem with a buffer.
   Source: Operating Systems: Three Easy Pieces (now version 1.00), Chapter 30, page 12
 */
#include <pthread.h>
#include <stdio.h>

const int MAX = 10; // Number of elements in the buffer
const int loops = 100000; // Number of items to produce/consume
int buffer[MAX];
int fill_ptr = 0;
int use_ptr = 0;
int count = 0;

void put(int value) {
    buffer[fill_ptr] = value;
    fill_ptr = (fill_ptr + 1) % MAX;
    count++;
}

int get() {
    int tmp = buffer[use_ptr];
    use_ptr = (use_ptr + 1) % MAX;
    count--;
    return tmp;
}


pthread_cond_t empty, fill;
pthread_mutex_t mutex;

void* producer(void *arg); // function prototype

void* producer(void *arg) {
    int i;
    for( i = 0; i < loops; i++ ){
        pthread_mutex_lock(&mutex);            // p1
        while (count == MAX){                 // p2
            pthread_cond_wait(&empty, &mutex); // p3
        }
        put(i);                               // p4
        printf("Produced an item (count: %d)\n", count);
        pthread_cond_signal(&fill);           // p5
        pthread_mutex_unlock(&mutex);         // p6
    }
    return NULL;
}

void* consumer(void *arg) {
    int i;
    for (i = 0; i < loops/2; i++){
        pthread_mutex_lock(&mutex); // c1
        while (count == 0){ // c2
            pthread_cond_wait(&fill, &mutex); // c3
        }
        int tmp = get(); // c4
        printf("Consumed an item (count: %d)\n", count);
        pthread_cond_signal(&empty); // c5
        pthread_mutex_unlock(&mutex); // c6
        printf("%d\n", tmp);
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    pthread_t p, c1, c2;
    pthread_create( &p,  NULL, producer, NULL );
    pthread_create( &c1, NULL, consumer, NULL );
    pthread_create( &c2, NULL, consumer, NULL );

    pthread_join(p, NULL);
    pthread_join(c1, NULL);
    pthread_join(c2, NULL);

    return 0;
}