/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Université Bordeaux 1, Télécom SudParis
 * See COPYING in top-level directory.
 */

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdint.h>
#include <stdarg.h>

// Debugging part, print out only if debugging level of the system is verbose or more
int _debug = -77;

void debug(const char *fmt, ...) {
  if (_debug == -77) {
    char *buf = getenv("EZTRACE_DEBUG");
    if (buf == NULL)
      _debug = 0;
    else
      _debug = atoi(buf);
  }
  if (_debug >= 0) { // debug verbose mode
    va_list va;
    va_start(va, fmt);
    vfprintf(stdout, fmt, va);
    va_end(va);
  }
}
// end of debugging part

// Number of threads
#define NBTHREAD 2

// Number of iterations
#define NBITER 10000

/*
 * Only used during thread creating to make sure that the thread
 * got the correct args.
 */
sem_t thread_ready;

pthread_barrier_t barrier;

volatile int sum = 0;

void compute() __attribute__((optimize("-O0")));

void compute() {
  int i;
  for (i = 0; i < NBITER; i++) {
    void *ptr = malloc(1);
    free(ptr);
    sum += 1;
  }
}

void* do_work(void* arg) {
  uint8_t my_id = *(uint8_t*) arg;

  // Notify the main thread that we got the args
  sem_post(&thread_ready);

  debug("Running thread #%d\n", my_id);

  pthread_barrier_wait(&barrier);
  compute();
  pthread_barrier_wait(&barrier);

  debug("End of thread #%d\n", my_id);
  return NULL;
}

int main() {
  pthread_t tid[NBTHREAD];
  int i;

  pthread_barrier_init(&barrier, NULL, NBTHREAD);
  sem_init(&thread_ready, 0, 0);

  for (i = 0; i < NBTHREAD; i++) {
    pthread_create(&tid[i], NULL, do_work, &i);
    sem_wait(&thread_ready);
  }

  for (i = 0; i < NBTHREAD; i++)
    pthread_join(tid[i], NULL);

  printf("Thread sum: %d\n", sum);
  sum = NBTHREAD * NBITER;
  printf("Check sum:  %d\n", sum);

  pthread_barrier_destroy(&barrier);
  return 0;
}
