#include <pthread.h>
#include <stdio.h>
#include <limits.h>
#define size 10

pthread_mutex_t  mutex;
pthread_cond_t   cond_push;
pthread_cond_t   cond_pop;

int count;
int data[size];
int COMPLEX_PUSH = (int) INT_MAX / 5;
int COMPLEX_POP  = (int) INT_MAX / 3;

void lifo_init(void) {
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond_push, NULL);
	pthread_cond_init(&cond_pop, NULL);
	count = 0;
}

int lifo_pop(void) {
	pthread_mutex_lock(&mutex);
	while (count == 0) {
        printf("LIFO is empty\n"); 
		pthread_cond_wait(&cond_pop, &mutex);
    }
	int item = data[--count];
	pthread_cond_signal(&cond_push);
	pthread_mutex_unlock(&mutex);
	return item;
}

void lifo_push(int item) {
	pthread_mutex_lock(&mutex);
	while (count == size) {
        printf("LIFO is full\n");
		pthread_cond_wait(&cond_push, &mutex);
    }
	data[count++] = item;
	pthread_cond_signal(&cond_pop);
	pthread_mutex_unlock(&mutex);
}

void hardcalcul(int limit) {
    int i;
    for (i=0; i<limit; i++) {}
}

void *push(void *arg) {
    int i, n = size * 2;

    for (i=0; i<n; i++) {
        hardcalcul(COMPLEX_PUSH);
        lifo_push(i);
        printf("Item written: %d, Count: %d\n", i, count);
    }
    printf("# End of writing\n");
}

void *pop(void *arg) {
    int i, n = 4;

    // Will wait for push, because LIFO is empty. (cf. hardcalcul)
    printf("# Starting reading LIFO\n");
    for (i=0; i<n; i++) {
        printf("Item read: %d, Count: %d\n", lifo_pop(), count);
        hardcalcul(COMPLEX_POP);
    }
    printf("# Pop is waiting (doing something else)...\n");
    sleep(7);
    while (count) { 
        printf("Item read: %d\n", lifo_pop());
        hardcalcul(COMPLEX_POP);
    }
    printf("# End of reading\n");
}

int main(void) {
    pthread_t thPush, thPop;

    lifo_init();
    pthread_create(&thPush, NULL, &push, NULL);
    pthread_create(&thPop, NULL, &pop, NULL);
    pthread_join(thPush, NULL);
    pthread_join(thPop, NULL);    
}

