// prod-cons.cpp - Productor-consumidor con variables de condición
#include <iostream>
#include <iomanip>
#include <pthread.h>
using namespace std;

const int ELEMENTOS_BUFER = 16; 
const int TOTAL_DATOS = 512;

typedef struct
{
  int bufer[ELEMENTOS_BUFER]; // matriz de datos
  int ocupados;          // elementos de la matriz ocupados
  int ind_p, ind_c;      // índices del productor y del consumidor
  pthread_mutex_t exmut; // exclusión mutua
  pthread_cond_t vacios; // var. de condición de elementos vacíos
  pthread_cond_t llenos; // var. de condición de elementos llenos
} st_bufer_t;

void productor(st_bufer_t *); 
void consumidor(st_bufer_t *); 
typedef void *(*ptrfnthr)(void *);

int main()
{
  pthread_t hilo_p, hilo_c;

  // Estructura de datos compartida por los hilos
  st_bufer_t st_bufer;
  // Iniciar la estructura st_bufer
  st_bufer.ind_p = st_bufer.ind_c = st_bufer.ocupados = 0;
  pthread_mutex_init(&st_bufer.exmut, 0);
  pthread_cond_init(&st_bufer.vacios, 0);
  pthread_cond_init(&st_bufer.llenos, 0);
  // Crear los hilos
  pthread_create(&hilo_p, 0, (ptrfnthr)productor, &st_bufer);
  pthread_create(&hilo_c, 0, (ptrfnthr)consumidor, &st_bufer);
  // Esperar a que los hilos trabajadores terminen
  pthread_join(hilo_p, 0);
  pthread_join(hilo_c, 0);
  
  system("pause");
}

// Productor
void productor(st_bufer_t *st_b)
{
  int i, a;
  for(i = 0; i < TOTAL_DATOS; i++)
  {
    pthread_mutex_lock(&st_b->exmut);
    while (st_b->ocupados == ELEMENTOS_BUFER)
      pthread_cond_wait(&st_b->vacios, &st_b->exmut);
    assert(st_b->ocupados < ELEMENTOS_BUFER);
    a =  rand();
    st_b->bufer[st_b->ind_p] = a;
    st_b->ind_p = (st_b->ind_p + 1) % ELEMENTOS_BUFER;
    st_b->ocupados++;
    /* ahora, o st_b->ocupados < ELEMENTOS_BUFER y st_b->ind_p
       es el índice del siguiente elemento vacío en el bufer, o
       st_b->ocupados == ELEMENTOS_BUFER y st_b->ind_p es el índice
       del siguiente elemento lleno que será vaciado por un
       consumidor (st_b->ind_p == st_b->ind_c) */
    cout << "bufer  <-- " << setw(6) << a << "  hilo productor\n";
    pthread_cond_signal(&st_b->llenos);
    pthread_mutex_unlock(&st_b->exmut);
  }
}

// Consumidor
void consumidor(st_bufer_t *st_b)
{
  int i, a;
  for(i = 0; i < TOTAL_DATOS; i++)
  {
    pthread_mutex_lock(&st_b->exmut);
    while(st_b->ocupados == 0)
      pthread_cond_wait(&st_b->llenos, &st_b->exmut);
    assert(st_b->ocupados > 0);
    a = st_b->bufer[st_b->ind_c];
    st_b->ind_c = (st_b->ind_c + 1) % ELEMENTOS_BUFER;
    st_b->ocupados--;
    /* ahora, o st_b->ocupados > 0 y st_b->ind_c es el índice
       del siguiente elemento ocupado en el bufer, o
       st_b->ocupados == 0 y st_b->ind_c es el índice del siguiente
       elemento vacío que será llenado por un productor
       (st_b->ind_c == st_b->ind_p) */
    cout << "bufer  --> " << setw(6) << a << "  hilo consumidor\n";
    pthread_cond_signal(&st_b->vacios);
    pthread_mutex_unlock(&st_b->exmut);
  }
}

