/*
 * Copyright (C) 2019
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdbool.h>

#include <sys/time.h>
#include <pthread.h>

const int NTHREADS = 2;
int I = 0;
typedef struct {
  int thr;
  int i, j;
} ProcessIDs;

int TURN = 0;
bool CS_READY[] = {false, false};
const int MAX_ITER = 1000000;

struct timespec onens = {.tv_sec = 0, .tv_nsec = 1};

void* func (void* p) {
  int i   = (*(ProcessIDs*) p).i;
  int j   = (*(ProcessIDs*) p).j;
  int thr = (*(ProcessIDs*) p).thr;

  printf("Thr %d en marcha.\n", thr);

  for (int l = 0; l < MAX_ITER; l++) {
    CS_READY[i] = true;
    TURN = j;

    //__atomic_thread_fence(__ATOMIC_SEQ_CST); // Full memory barrier
    //
    //nanosleep(&onens, NULL);  // Ok here
    while ((CS_READY[j]) && (TURN == j)) {}

    I += 1;

    //nanosleep(&onens, NULL);  // Also Ok here
    CS_READY[i] = false;

  }

  return NULL;
}

// Main program
int main()
{
  // Initialized the lock then fork 2 threads
  pthread_t p1, p2;
  ProcessIDs datathr0 = {.thr = 0, .i = 0, .j = 1};
  ProcessIDs datathr1 = {.thr = 1, .i = 1, .j = 0};

  // Create two threads (both run func)
  printf ("Threads in C.\n");

  pthread_create(&p1, NULL, func, (void*)&datathr0);
  pthread_create(&p2, NULL, func, (void*)&datathr1);

  // Wait for the threads to end.
  pthread_join(p1, NULL);
  pthread_join(p2, NULL);


  printf("Actual Count: %d | Expected Count: %d\n",
         I, MAX_ITER*2);

  return 0;
}
