/*
 * Controle da região crítica por alternância simples.
 * Código para N threads.
 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <linux/futex.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <limits.h>

#define N 10

volatile int s = 0; /* Variável compartilhada */
volatile int vez = 0; /* Indica de qual thread é a vez de 
                         entrar na região crítica */

volatile int vvez[N];

/* Retorna -1 se o futex não bloqueou e 
            0 caso contrário */
int futex_wait(void *addr, int val1) {
  return syscall(SYS_futex, addr, FUTEX_WAIT, 
                 val1, NULL, NULL, 0);
}

/* Retorna o número de threads que foram acordadas */
int futex_wake(void *addr, int n) {
    return syscall(SYS_futex, addr, FUTEX_WAKE, 
                   n, NULL, NULL, 0);
}


void* f_thread(void *v) {
  int thr_id = *(int*)v;
  int proximo = (thr_id + 1)%N;
  int i, vez_local;

  for (i = 0; i < 5; i++) {

    while (vvez[thr_id] == 0) 
      futex_wait(&vvez[thr_id], 0); 

    /*  vez_local = vez;
    if (vez_local != thr_id) 
    futex_wait(&vez, vez_local); */

    s = thr_id;
    printf("Thread %d, s = %d.\n", thr_id, s); 
  
    vvez[thr_id] = 0;
    vvez[proximo] = 1;
    futex_wake(&vvez[proximo], 1);

    /* vez = (thr_id + 1)%N; Passa a vez para a outra thread */
  }
  return NULL;
}

int main() {

  pthread_t thr[N];
  int id[N], i;

  vvez[0] = 1;
  for (i = 1; i < N; i++) 
    vvez[i] = 0;

  for (i = 0; i < N; i++) {
    id[i] = i;
    pthread_create(&thr[i], NULL, f_thread, &id[i]);
  }

  for (i = 0; i < N; i++) 
    pthread_join(thr[i], NULL); 

  return 0;
}