#include <stdio.h>   /* printf                  */
#include <stdlib.h>  /* atoi                    */
#include <pthread.h> /* thread create/join/exit */

int Loops;
int Count;
int turn;
int ready[2] = {0, 0};
int lock;

typedef void (*LockFn)(int);

LockFn LOCK;
LockFn UNLOCK;

void Lock0(int me)
{
}

void Unlock0(int me)
{
}

void Lock1(int me)
{
  while (lock == 1)
    continue;
    
  lock = 1;
}

void Unlock1(int me)
{
  lock = 0;
}

void Lock2(int me)
{
  while (turn != me)
    continue;
}

void Unlock2(int me)
{
  turn = 1 - me;
}

void Lock3(int me)
{
  ready[me] = 1;
  while (ready[1 - me])
    continue;
}

void Unlock3(int me)
{
  ready[me] = 0;
}

void Lock4(int me)
{
  int you = 1 - me;
  turn = you;
  ready[me] = 1;
  while (ready[you] && (turn == (you)))
    continue;
}

void Unlock4(int me)
{
  ready[me] = 0;
}

void Lock5(int me)
{
  int you = 1 - me;
  turn = you;
  ready[me] = 1;
  __sync_synchronize();
  while (ready[you] && (turn == you))
    continue;
}

void Unlock5(int me)
{
  ready[me] = 0;
}

void *Increment(void *p)
{
  int i, id = *(int *)p;
  
  for (i = 0; i < Loops; i++)
  {
    LOCK(id);
      Count++;
    UNLOCK(id);
  }
  return 0;
}

int main(int argc, char **argv)
{
  pthread_t thread_id0, thread_id1;
  int id0 = 0, id1 = 1;
  
  LockFn locks[] = {Lock0, Lock1, Lock2, Lock3, Lock4, Lock5};
  LockFn unlocks[] = {Unlock0, Unlock1, Unlock2, Unlock3, Unlock4, Unlock5};
  
  LOCK = locks[0];
  UNLOCK = unlocks[0];
  
  if (argc < 3)
  {
    printf("Usage: %s <LOCK_FN> <ITERATIONS>\n", argv[0]);
    printf("\n");
    printf("where: LOCK_FN is the lock function to test:\n");
    printf("         0 - No locking at all\n");
    printf("         1 - Uses a single global lock variable, no mutual exclusion.\n");
    printf("         2 - Taking turns, no shared memory, mutual exclusion but non-bounded waiting.\n");
    printf("         3 - Yielding to other thread. Causes a race condition and will likely hang.\n");
    printf("         4 - Peterson's Solution. Fails on modern architectures.\n");
    printf("         5 - Peterson's Solution with a memory barrier. Works but the barrier isn't implemented in all systems.\n");
    printf("\n");
    printf("       ITERATIONS is the number of times each thread will increment the shared variable x 1,000,000\n");
    printf("\n");
    printf("Example:  lock_test 5 10 \n");
    printf("          runs Lock5/Unlock5 test with 10,000,000 iterations.\n");
    return -1;
  }
  
  LOCK = locks[atoi(argv[1])];
  UNLOCK = unlocks[atoi(argv[1])];
  Loops = 1000 * 1000 * atoi(argv[2]);
  
  pthread_create(&thread_id0, 0, Increment, &id0);
  pthread_create(&thread_id1, 0, Increment, &id1);
    
  pthread_join(thread_id0, 0);
  pthread_join(thread_id1, 0);
    
  printf("------------------\n");
  printf("Count = %i\n", Count);
  
  return 0;
}