// hilos-sc.cpp - Sección crítica
#include <iostream>
#include <pthread.h>
using namespace std;

const int MAX_MUESTRAS = 10; // elementos de la matriz de datos

// Estructura de datos compartida por los hilos
struct
{
  int muestras[MAX_MUESTRAS]; // matriz de datos
  int ind; // índice del primer elemento vacío
  pthread_mutex_t mutex_adquirir; // exclusión mutua
} st_bufer;

int random(int n = numeric_limits<int>::max())
{
  static bool primera_vez = true;
  if (primera_vez)
  {
    srand(time(0));
    primera_vez = false;
  }
  return (rand() % n)+1; // valor entre 1 y n
}

void error(int codigo, char *str)
{
  cerr << codigo << ": " << str << endl;
  exit(1);
}

// Hilo trabajador
void *AdquirirDatos(void *pid)
{
  int x = 0, cod, id = *((int *)pid);
  do
  {
    cod = pthread_mutex_lock(&st_bufer.mutex_adquirir);
    if (cod) error(cod, "pthread_mutex_lock");
    
    if (st_bufer.ind >= MAX_MUESTRAS) return pid;
    x = random();  // adquirir un dato
    cout << "hilo-" << id << " tomó la muestra " << st_bufer.ind << endl;
    st_bufer.muestras[st_bufer.ind] = x;
    st_bufer.ind++; // incrementar el índice de la matriz
    
    cod = pthread_mutex_unlock(&st_bufer.mutex_adquirir);
    if (cod) error(cod, "pthread_mutex_unlock");
  }
  while (st_bufer.ind < MAX_MUESTRAS);
  return 0;
}

/*
void *TomarMuestra(void *p)
{
  int x = 0, cod;
  cod = pthread_mutex_lock(&st_bufer.mutex_adquirir);
  if (cod) error(cod, "pthread_mutex_lock");
  x = random();  // adquirir un dato
  st_bufer.muestras[st_bufer.ind] = x;
  cod = pthread_mutex_unlock(&st_bufer.mutex_adquirir);
  if (cod) error(cod, "pthread_mutex_unlock");
  return 0;
}
// Hilo trabajador
void *AdquirirDatos(void *pid)
{
  int cod, id = *((int *)pid);
  do
  {
    cod = pthread_mutex_lock(&st_bufer.mutex_adquirir);
    if (cod) error(cod, "pthread_mutex_lock");
    
    if (st_bufer.ind >= MAX_MUESTRAS) return pid;
    TomarMuestra(pid);
    cout << "hilo-" << id << " tomó la muestra " << st_bufer.ind << endl;
    st_bufer.ind++; // incrementar el índice de la matriz
    
    cod = pthread_mutex_unlock(&st_bufer.mutex_adquirir);
    if (cod) error(cod, "pthread_mutex_unlock");
  }
  while (st_bufer.ind < MAX_MUESTRAS);
  return 0;
}
*/

// Hilo principal
int main()
{
  const int NHILOS = 2; // número de hilos
  int i, cod;

  // Iniciar la estructura st_bufer
  st_bufer.ind = 0;
  fill(st_bufer.muestras, st_bufer.muestras + MAX_MUESTRAS, 0);
/*  
  pthread_mutexattr_t mutex_attr;
  pthread_mutexattr_init(&mutex_attr);
  pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);

  cod = pthread_mutex_init(&st_bufer.mutex_adquirir, &mutex_attr);
*/
  cod = pthread_mutex_init(&st_bufer.mutex_adquirir, 0);
  if (cod) error(cod, "pthread_mutex_init");

  // Crear los n hilos e iniciar su ejecución
  pthread_t hilo[NHILOS]; // identificadores de los hilos
  int id[NHILOS]; // mis identificadores: 0, 1, ...
  for (i = 0; i < NHILOS; i++)
  {
    id[i] = i;
    cod = pthread_create(&hilo[i], 0,
                         AdquirirDatos,
                         &id[i]);
    if (cod) error(cod, "pthread_create");
  }
  
  // Esperar a que los hilos secundarios terminen
  for (i = 0; i < NHILOS; i++)
  {
    cod = pthread_join(hilo[i], 0);
    if (cod) error(cod, "pthread_join");
  }
  
  system("pause");
}

