#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <thread.h>
#include <spinlock.h>
#include <mutex.h>
#include <cond.h>
#include <rwlock.h>
#include <misc.h>

#define MAX_THREAD 1000
#define N_THREAD 40

int checkAbort = 0;

#define checkResults(string, val) {             \
 if (val) {                                     \
   printf("%d: Failed with %d at %s", GetCurrentThreadId(), val, string); \
   if ( checkAbort )exit(1);                    \
 }                                              \
}
#define checkResultsL(string, val) {             \
 if (val) {                                     \
   printf("Failed with %d at %s", val, string); \
 }                                              \
}
#define checkResultsAlt(string, val, alt) {					\
 if (val && !(val==alt)) {									\
   printf("Failed with %d (%d) at %s", val, alt, string);	\
   if ( checkAbort )exit(1);								\
 }															\
}

typedef struct {
    int id;
} parm;

struct timespec *starttimer(struct timespec *ts, DWORD ms)
{
    struct timeval    tp;
    /* Convert from timeval to timespec */
    gettimeofday(&tp, NULL);
    ts->tv_sec  = tp.tv_sec;
    ts->tv_nsec = tp.tv_usec * 1000;
    ts->tv_sec += ms  / 1000;
    printf("starttimer, %d s=%ld\n",ms, ts->tv_sec);
    return ts;
}


void *hello(void *arg)
{
    parm *p=(parm *)arg;
    printf("Hello from node %d\n", p->id);
    pthread_exit(NULL);
    printf("Not reached from node %d\n", p->id);
     pthread_exit(NULL);
    return (NULL);
}

pthread_rwlock_t       rwlock;
char testType[100];

/*================================================================================*/
#define SPINLOCK_NTHREADS                50
pthread_spinlock_t      spinlock;
int                     SLsharedData=0;
int                     SLsharedData2=0;

void *spinlock_threadfunc(void *parm)
{
   int   rc, d;
   int tid = pthread_self();
   printf("Thread %d: Entered\n", tid);
   rc = pthread_spin_lock(&spinlock);
   d= SLsharedData;
   checkResults("pthread_spin_lock()\n", rc);
   /********** Critical Section *******************/
   printf("Thread %d: Start critical section, holding lock\n",
          tid);
   /* Access to shared data goes here */
   ++SLsharedData; --SLsharedData2;
   Sleep(50);
   printf("Thread %d: End critical section, release lock\n",
          tid);
   d -= (SLsharedData-1);
   /********** Critical Section *******************/
   rc = pthread_spin_unlock(&spinlock);
   checkResults("pthread_spinlock_unlock()\n", rc);
   if (d) {
        printf("FAILED Thread %d: check SLsharedData=%d instead of 0\n", tid, d);
        exit(1);
   }
   return NULL;
}
 
int spinlock_main(void)
{
    pthread_t             thread[SPINLOCK_NTHREADS];
    int                   rc=0;
    int                   i;

    printf("Enter Testcase - spinlock_main %s\n",testType);
    rc = pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED);
    printf("Spinlock inited\n");
    checkResults("pthread_spin_init()\n", rc);

    printf("Hold Spinlock to prevent access to shared data\n");
    rc = pthread_spin_lock(&spinlock);
    printf("Unlock Spinlock to prevent access to shared data\n");
    checkResults("pthread_spin_lock()\n", rc);
    rc = pthread_spin_unlock(&spinlock);
    checkResults("pthread_spin_unlock()\n",rc);
    printf("Hold Spinlock to prevent access to shared data 2\n");
    rc = pthread_spin_lock(&spinlock);
    checkResults("pthread_spin_lock() 2\n", rc);

    printf("Create/start threads\n");
    for (i=0; i<SPINLOCK_NTHREADS; ++i) {
        rc = pthread_create(&thread[i], NULL, spinlock_threadfunc, NULL);
        checkResults("pthread_create()\n", rc);
    }

    printf("Wait a bit until we are 'done' with the shared data\n");
    Sleep(3000);
    printf("Unlock shared data\n");
    rc = pthread_spin_unlock(&spinlock);
    checkResults("pthread_spin_lock()\n",rc);

    printf("Wait for the threads to complete, and release their resources\n");
    for (i=0; i <SPINLOCK_NTHREADS; ++i) {
        rc = pthread_join(thread[i], NULL);
        checkResults("pthread_join()\n", rc);
    }

    printf("Clean up, data: %d %d\n",SLsharedData,SLsharedData2);
    rc = pthread_spin_destroy(&spinlock);
    printf("Main completed\n");
    return 0;
}
/*================================================================================*/
#define MUTEX_NTHREADS              5
#define MUTEX_LOOPCNT				0
pthread_mutex_t         mutex;
int                     sharedData=0;
int                     sharedData2=0;
int						mwait=30000;
int						init =0, destr=0;

void *mutex_threadfunc(void *parm)
{
   int   rc;
   int d;
   int tid = 0;
   printf("----------------------------\n");
   //Sleep(1000);
    //mutex_print(&mutex,"Lock T A");
    mutex_print(&mutex,"Lock T");
   printf("----------------------------\n");
   //Sleep(1000);
   //printf("Thread %d: Entered\n", tid);

rc = pthread_mutex_lock(&mutex);
   //mutex_print(&mutex,"Locked T");
   d= sharedData;
   checkResults("pthread_mutex_lock()\n", rc);
   /********** Critical Section *******************/
   printf("Thread %d: Start critical section, holding lock\n",
          tid);
   /* Access to shared data goes here */
   ++sharedData;
   Sleep(500);
    --sharedData2;
   printf("Thread %d: End critical section, release lock\n",
          tid);
   d -= (sharedData-1);
   /********** Critical Section *******************/
   //mutex_print(&mutex,"UnLock T");
   rc = pthread_mutex_unlock(&mutex);
   //mutex_print(&mutex,"UnLocked T");
   checkResults("pthread_mutex_unlock()\n", rc);
   if (d) {
        printf("FAILED Thread %d: check sharedData=%d instead of 0\n", tid, d);
        exit(1);
   }



   return NULL;
}
 
void *mutex_threadfunc_raced(void *parm)
{
   int   rc, rc1, i,j=0,x=0;
   int d;
   int rn;
   int tid = pthread_self();
   struct timespec   ts;

   //printf("Thread %d: Entered\n", tid);
   while(1) {
       mutex_print(&mutex,"A pthread_mutex_lock");
       //rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 8000 ));
       rc = pthread_mutex_lock(&mutex);
       mutex_print(&mutex,"B pthread_mutex_lock");
       if (rc == ETIMEDOUT) {
           printf("FAILED Thread %d: pthread_mutex_timedlock ETIMEDOUT\n", tid);
           continue;
       }
       checkResults("pthread_mutex_timedlock()\n", rc);
       d= sharedData;
       /********** Critical Section *******************/
       /* Access to shared data goes here */
        ++sharedData;
        Sleep(100);
        --sharedData2;
       d -= (sharedData-1);
       /********** Critical Section *******************/
       mutex_print(&mutex,"A pthread_mutex_unlock 0");
       rc = pthread_mutex_unlock(&mutex);

       mutex_print(&mutex,"B pthread_mutex_unlock 0");
        if(rc) {
            mutex_print(&mutex,"cant unlock 0");
        }
       checkResults("pthread_mutex_unlock()\n", rc);
       if (d) {
            printf("FAILED Thread %d: check sharedData=%d instead of 0\n", tid, d);
            exit(1);
       }
       i = 0;
       mutex_print(&mutex, "A pthread_mutex_destroy 0");
       rc=0;//rc = pthread_mutex_destroy(&mutex);
       mutex_print(&mutex,"B pthread_mutex_destroy 0");
        if(rc) {
            mutex_print(&mutex,"cant destroy 0");
            //break;
        } else {
            destr ++;
            mutex_print(&mutex,"destroyed 0");
        }
        checkResultsAlt("pthread_mutex_destroy()\n", rc, EBUSY);
        do {
            mutex_print(&mutex,"A pthread_mutex_timedlock()");
            //rc1 = pthread_mutex_lock(&mutex);
            rc1 = pthread_mutex_timedlock(&mutex,starttimer(&ts, 4000 ));
            mutex_print(&mutex,"B pthread_mutex_timedlock()");
            //checkResults("pthread_mutex_lock() destroy\n", rc1);
            if(rc1) {
                mutex_print(&mutex,"cant lock");
                break;
            }
            mutex_print(&mutex,"A pthread_mutex_unlock() 1");
            rc1 = pthread_mutex_unlock(&mutex);
            mutex_print(&mutex,"B pthread_mutex_unlock() 1");
            if(rc1) {
                mutex_print(&mutex,"cant unlock");
                break;
            }
            //checkResults("pthread_mutex_unlock() destroy\n", rc1);
            mutex_print(&mutex, "A pthread_mutex_destroy 1");
            rc=0;//rc = pthread_mutex_destroy(&mutex);
            mutex_print(&mutex, "B pthread_mutex_destroy 1");
            if(rc) {
                mutex_print(&mutex,"cant destroy");
                break;
            } else {
                destr ++;
                mutex_print(&mutex,"destroyed");
            }
            checkResultsAlt("pthread_mutex_destroy() 2\n", rc, EBUSY);
            i ++;
       } while(rc == EBUSY);
        if(i)printf("Thread %d: destroy attempts: %d success: %d\n", tid,i,x);
        mutex_print(&mutex, "A pthread_mutex_init");
        rc =0; //rc = pthread_mutex_init(&mutex,NULL);
        mutex_print(&mutex, "B pthread_mutex_init");
        if(rc) {
            mutex_print(&mutex,"cant init");
            break;
        } else {
            init ++;
            mutex_print(&mutex,"re-inited");
        }
        checkResults("pthread_mutex_init()\n", rc);

       j++;
       if (j>MUTEX_LOOPCNT) {
            printf("Thread %d: Leaving\n", tid);
            break;
       }
   }
   return NULL;
}
 
static volatile LONG _tid=0;
void *mutex_threadfunc_timed(void *parm)
{
   int   rc;
   int d,e;
   int tid = pthread_self();
   struct timespec   ts;
   printf("Thread %d: Entered\n", tid);
   mutex_print(&mutex,"Lock T");
   if (rand() & 1) {
        rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 20000 ));
        checkResults("XX pthread_mutex_timedlock()\n", rc);
   } else {
        rc = pthread_mutex_lock(&mutex);
        checkResults("YY pthread_mutex_lock()\n", rc);
   }
   mutex_print(&mutex,"Locked T");
   while (rc) {
        printf("%d: Lock failed with %d\n", tid, rc);
        rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 2000 ));
        mutex_print(&mutex,"Locked TR");
   }
   /********** Critical Section *******************/
   _tid = GetCurrentThreadId();
   d= sharedData;
   printf("Thread %d: Start critical section, holding lock\n",
          tid);
   /* Access to shared data goes here */
   ++sharedData;
   Sleep(100);
   if (_tid != tid) {
        printf("FAILED Thread %d: shared TID = %d\n", tid, _tid);
        mutex_print(&mutex,"Race cond");
   }
   e = sharedData -1;
   --sharedData2;
   printf("Thread %d: End critical section, release lock\n",
          tid);
   d -= (e); 
   if (d) {
        printf("FAILED Thread %d: check sharedData=%d instead of 0\n", tid, d);
        mutex_print(&mutex,"Race cond");
   }
   _tid = 0;
   /********** Critical Section *******************/
   rc = pthread_mutex_unlock(&mutex);
   mutex_print(&mutex,"UnLocked T");
   checkResults("pthread_mutex_unlock()\n", rc);
   return NULL;
}
 
/* PTHREAD_NORMAL_MUTEX_INITIALIZER deadlock handling thread 1 */
void *mutex_threadfuncDL1(void *parm) 
{
   int   rc;
   int d;
   int tid = pthread_self();
   printf("Thread %d: Entered, locking n1\n", tid);
   rc = pthread_mutex_lock(&mutex);
   checkResults("pthread_mutex_lock() 1\n", rc);
   printf("Thread %d: locked nr1\n", tid);
   Sleep(1000);
   printf("Thread %d: locking nr2 for a deadlock\n", tid);
   rc = pthread_mutex_lock(&mutex);
   checkResults("pthread_mutex_lock() 2\n", rc);
   printf("Thread %d: locked nr2 and externally unlocked\n", tid);
   /********** Critical Section *******************/
   Sleep(2000);
   printf("Thread %d: Doing job\n", tid);
   Sleep(2000);
   printf("Thread %d: End critical section, release lock\n", tid);
   /********** Critical Section *******************/
   rc = pthread_mutex_unlock(&mutex);
   checkResults("pthread_mutex_unlock()\n", rc);
   printf("Thread %d: leaving\n", tid);
   return NULL;
}
 
/* PTHREAD_NORMAL_MUTEX_INITIALIZER deadlock handling main thread */
int mutex_main_DL(void)
{
   int   rc;
   int d;
   pthread_t thread;
   int tid = pthread_self();
    rc = pthread_create(&thread, NULL, mutex_threadfuncDL1, NULL);
    checkResults("mutex_main_DL: pthread_create()\n", rc);
   printf("mutex_main_DL: wait for thread\n");
   Sleep(3000);
   printf("mutex_main_DL: try ext release deadlock\n");

   rc = pthread_mutex_unlock(&mutex);
   checkResults("pthread_mutex_unlock() ext\n", rc);
   printf("mutex_main_DL: unlocked 1\n");
   Sleep(2000);
   //rc = pthread_mutex_unlock(&mutex);
   checkResults("pthread_mutex_unlock() ext 2\n", rc);
   printf("mutex_main_DL: unlocked 2\n");
   Sleep(10000);
   printf("mutex_main_DL: bye!\n");

   return 0;
}
 
int mutex_main_timed(void)
{
    pthread_t             thread[MUTEX_NTHREADS];
    int                   rc=0;
    int                   i;
    struct timespec   ts;

    printf("Enter Testcase - mutex_main_timed %s\n",testType);
    mutex = PTHREAD_NORMAL_MUTEX_INITIALIZER;
    printf("Mutex inited\n");
    rc = pthread_mutex_init (&mutex, NULL);
    checkResults("pthread_mutex_init()\n", rc);
       mutex_print(&mutex,"Locked 0");

    printf("Hold Mutex to prevent access to shared data\n");
      rc = pthread_mutex_lock(&mutex);
    //rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 30000 ));
       mutex_print(&mutex,"Locked 1");
    printf("Mutex inited type=%d\n", ((mutex_t *)mutex)->type);
    printf("Unlock Mutex to prevent access to shared data\n");
    checkResults("pthread_mutex_lock()\n", rc);
    rc = pthread_mutex_unlock(&mutex);
    checkResults("pthread_mutex_unlock()\n",rc);
    printf("Hold Mutex to prevent access to shared data 2\n");
    mutex_print(&mutex,"Locked 2");
    //rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 60000 ));
    rc = pthread_mutex_lock(&mutex);
    mutex_print(&mutex,"Locked 3");
    checkResults("pthread_mutex_lock() 2\n", rc);

    printf("Create/start threads\n");
    for (i=0; i<MUTEX_NTHREADS; ++i) {
        rc = pthread_create(&thread[i], NULL, mutex_threadfunc_timed, NULL);
        checkResults("pthread_create()\n", rc);
    }

    printf("Wait a bit until we are 'done' with the shared data\n");
    Sleep(5000);
    printf("Unlock shared data\n");
    rc = pthread_mutex_unlock(&mutex);
    checkResults("pthread_mutex_lock()\n",rc);

    printf("Wait for the threads to complete, and release their resources\n");
    for (i=0; i <MUTEX_NTHREADS; ++i) {
        rc = pthread_join(thread[i], NULL);
        checkResults("pthread_join()\n", rc);
    }

    printf("Clean up, data: %d %d\n",sharedData,sharedData2);
    rc = pthread_mutex_destroy(&mutex);
    printf("Main completed\n");
    return 0;
}

int mutex_main_raced(void)
{
    //pthread_t             thread[MUTEX_NTHREADS];
    pthread_t             *thread;
    int                   rc=0;
    int                   i, f=0, F=0;
    struct timespec   ts;

    mutex_print_set(1);
    checkAbort = 0;
    thread = (pthread_t *)calloc(MUTEX_NTHREADS, sizeof(pthread_t));
    printf("Enter Testcase - mutex_main_raced %s\n",testType);
    mutex = PTHREAD_NORMAL_MUTEX_INITIALIZER;
    printf("Mutex inited\n");

    printf("Hold Mutex to prevent access to shared data\n");
    //rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 3000000 ));
    rc = pthread_mutex_lock(&mutex);
    printf("Mutex inited type=%d\n", ((mutex_t *)mutex)->type);
    printf("Unlock Mutex to prevent access to shared data\n");
    checkResults("pthread_mutex_timedlock()\n", rc);
    rc = pthread_mutex_unlock(&mutex);
    checkResults("pthread_mutex_unlock() 1\n",rc);
    printf("Hold Mutex to prevent access to shared data 2\n");
    //rc = pthread_mutex_timedlock(&mutex,starttimer(&ts, 3000000 ));
    rc = pthread_mutex_lock(&mutex);
    checkResults("pthread_mutex_timedlock() 2\n", rc);

    rc = pthread_mutex_unlock(&mutex);
    checkResults("pthread_mutex_unlock() 2\n",rc);
    printf("Create/start threads\n");
    Sleep(1000);
    for (i=0; i<MUTEX_NTHREADS; ++i) {
        rc = pthread_create(&thread[i], NULL, mutex_threadfunc_raced, NULL);
        checkResults("pthread_create()\n", rc);
    }

    printf("Wait a bit until we are 'done' with the shared data\n");
    printf("Unlock shared data\n");
    printf("Wait for the threads to complete, and release their resources\n");
    Sleep(40000);
    printf("Done\n");
    mutex_print(&mutex,"Done");
    do {
        f = 0;
        for (i=0; i <MUTEX_NTHREADS; ++i) {
            if( thread[i] ) {
                rc = pthread_join(thread[i], NULL);
                if (rc) {
                    f++;
                    printf("_pthread_tryjoin failed: %d %d %d\n",i, rc,thread[i]);
                } else {
                    thread[i] = 0;
                    printf("_pthread_tryjoin OK: %d\n",i);
                }
            }
        }
        printf("Joins failed: %d destr: %d init: %d\n",f,destr,init);
        F += f;
        Sleep(1000);
    } while (0);

    printf("Joins total failed: %d destr: %d init: %d\n",F,destr,init);
    printf("Clean up, data: %d %d\n",sharedData,sharedData2);
    mutex_print(&mutex, "A Clean up");
    rc = pthread_mutex_destroy(&mutex);
    mutex_print(&mutex, "B Clean up");
    // Removed in r4392: printf("Main completed spincount = %d\n", _spin_lite_getsc(0));
    // Removed in r4392: printf("Main completed spinlocks = %d\n", _spin_lite_getbsc(0));
    // Removed in r4392: printf("Main completed Max spincount = %d\n", _spin_lite_getscMax(0));
    // Removed in r4392: printf("Main completed Avg spincount/thread = %f\n", (float)((float)_spin_lite_getsc(0)/(float)MUTEX_NTHREADS));
    return 0;
}

 
int mutex_main_static(void)
{
    pthread_t             thread[MUTEX_NTHREADS];
    int                   rc=0;
    int                   i;
    struct timespec   ts;

    printf("mutex_main_static: Hold Mutex to prevent access to shared data o=%d\n",0);
    mutex_print(&mutex,"Lock");
    rc = pthread_mutex_lock(&mutex);
    mutex_print(&mutex,"Locked");
    printf("Mutex inited type=%d o=%d\n", ((mutex_t *)mutex)->type,GET_OWNER(((mutex_t *)mutex)));
    printf("Unlock Mutex to prevent access to shared data\n");
    checkResults("pthread_mutex_lock()\n", rc);
    rc = pthread_mutex_unlock(&mutex);
    mutex_print(&mutex,"UnLocked");
    printf("Unlocked Mutex to prevent access to shared data o=%d\n",GET_OWNER(((mutex_t *)mutex)));
    checkResults("pthread_mutex_unlock()\n",rc);
    printf("Hold Mutex to prevent access to shared data 2\n");
    rc = pthread_mutex_lock(&mutex);
    checkResults("pthread_mutex_lock() 2\n", rc);
    mutex_print(&mutex,"Locked 2");
    printf("Create/start threads\n");
    for (i=0; i<MUTEX_NTHREADS; ++i) {
        rc = pthread_create(&thread[i], NULL, mutex_threadfunc, NULL);
        checkResults("pthread_create()\n", rc);
    }

    printf("Wait a bit until we are 'done' with the shared data\n");
    Sleep(3000);
    printf("Unlock shared data\n");
    rc = pthread_mutex_unlock(&mutex);
    mutex_print(&mutex,"UnLocked 2");
    checkResults("pthread_mutex_unlock()\n",rc);
    return 0;
}
int mutex_main(void)
{
    pthread_t             *thread;
    int                   rc=0;
    int                   i;

    int tid = 0;
     mutex_print_set(1);
     thread = (pthread_t *)calloc(MUTEX_NTHREADS, sizeof(pthread_t));
     printf("Main thread %d: Entered\n", tid);
    printf("Enter Testcase - mutex_main %s\n",testType);
    if (strcmp(testType,"static") == 0) {
        mutex = PTHREAD_DEFAULT_MUTEX_INITIALIZER;
        return mutex_main_static();
    } else if (strcmp(testType,"staticN") == 0) {
        mutex = PTHREAD_NORMAL_MUTEX_INITIALIZER;
    } else if (strcmp(testType,"staticND") == 0) {
        mutex = PTHREAD_NORMAL_MUTEX_INITIALIZER;
        return mutex_main_DL();
    } else if (strcmp(testType,"staticE") == 0) {
        mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;
    } else if (strcmp(testType,"staticR") == 0) {
        mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
    } else if (strcmp(testType,"timed") == 0) {
        mutex = PTHREAD_NORMAL_MUTEX_INITIALIZER;
        return mutex_main_timed();
    } else if (strcmp(testType,"raced") == 0) {
        mutex = PTHREAD_NORMAL_MUTEX_INITIALIZER;
        return mutex_main_raced();
    } else {
        rc = pthread_mutex_init(&mutex,NULL);
        printf("Mutex inited\n");
        checkResults("pthread_mutex_init()\n", rc);
    }

    printf("Hold Mutex to prevent access to shared data o=%d\n",0);
    mutex_print(&mutex,"Lock");
    rc = pthread_mutex_lock(&mutex);
    mutex_print(&mutex,"Locked");
    printf("Mutex inited type=%d o=%d\n", ((mutex_t *)mutex)->type,GET_OWNER(((mutex_t *)mutex)));
    printf("Unlock Mutex to prevent access to shared data\n");
    checkResults("pthread_mutex_lock()\n", rc);
    rc = pthread_mutex_unlock(&mutex);
    mutex_print(&mutex,"UnLocked");
    printf("Unlocked Mutex to prevent access to shared data o=%d\n",GET_OWNER(((mutex_t *)mutex)));
    checkResults("pthread_mutex_unlock()\n",rc);
    printf("Hold Mutex to prevent access to shared data 2\n");
    rc = pthread_mutex_lock(&mutex);
    checkResults("pthread_mutex_lock() 2\n", rc);
    mutex_print(&mutex,"Locked 2");
    printf("Create/start threads\n");
    for (i=0; i<MUTEX_NTHREADS; ++i) {
        rc = pthread_create(&thread[i], NULL, mutex_threadfunc, NULL);
        checkResults("pthread_create()\n", rc);
    }

    printf("Wait a bit until we are 'done' with the shared data\n");
    Sleep(3000);
    printf("Unlock shared data\n");
    rc = pthread_mutex_unlock(&mutex);
    mutex_print(&mutex,"UnLocked 2");
    checkResults("pthread_mutex_unlock()\n",rc);

    printf("Wait for the threads to complete, and release their resources\n");
    for (i=0; i <MUTEX_NTHREADS; ++i) {
        rc = pthread_join(thread[i], NULL);
        checkResults("pthread_join()\n", rc);
    }

    printf("Clean up, data: %d %d\n",sharedData,sharedData2);
    rc = pthread_mutex_destroy(&mutex);
    printf("Main completed\n");
    Sleep(6000);
    return 0;
}
#if 1
/*================================================================================*/
#define COND_NTHREADS                3
#define COND_WAIT_TIME_SECONDS       10

int                 workToDo = 0;
int                 workLeave = 0;
pthread_cond_t      cond;
pthread_mutex_t     mutex;

void *condTimed_threadfunc(void *parm)
{
  int               tid;
  int               rc;
  struct timespec   ts;
  struct timeval    tp;

  rc = pthread_mutex_lock(&mutex);
  checkResults("pthread_mutex_lock()\n", rc);
  tid = pthread_self();

  /* Usually worker threads will loop on these operations */
  while (!workLeave) {
    rc =  gettimeofday(&tp, NULL);
    checkResults("gettimeofday()\n", rc);

    /* Convert from timeval to timespec */
    ts.tv_sec  = tp.tv_sec;
    ts.tv_nsec = tp.tv_usec * 1000;
    ts.tv_sec += COND_WAIT_TIME_SECONDS;

    do {
      if (strcmp(testType,"notTimed") == 0) {
        printf("Thread %d blocked, notTimed\n", tid);
        rc = pthread_cond_wait(&cond, &mutex);
      } else {
        printf("Thread %d blocked and waiting\n", tid);
        rc = pthread_cond_timedwait(&cond, &mutex, starttimer(&ts, 6000 ));
      }
      /* If the wait timed out, in this example, the work is complete, and   */
      /* the thread will end.                                                */
      /* In reality, a timeout must be accompanied by some sort of checking  */
      /* to see if the work is REALLY all complete. In the simple example    */
      /* we'll just go belly up when we time out.                            */
      printf("Thread %d unblocked\n", tid);
      if (rc == ETIMEDOUT) {
        printf("Wait %d timed out!\n", tid);
        rc = pthread_mutex_unlock(&mutex);
        checkResults("pthread_mutex_unlock() A\n", rc);
        printf("Exit %d\n", tid);
        pthread_exit(NULL);
      }
      checkResults("pthread_cond_timedwait()\n", rc);
    } while (!workLeave && !workToDo); 
    if (workToDo) {
        printf("Thread %d consumes work here\n", tid);
        Sleep(2000);
        workToDo = 0;
    }
  } 
  printf("Thread %d leaves here\n", tid);
  rc = pthread_mutex_unlock(&mutex);
  checkResults("pthread_mutex_unlock() B\n", rc);
  return NULL;
}

int condTimed_main()
{
  int                   rc=0;
  int                   i;
  pthread_t             threadid[COND_NTHREADS];
  struct timespec   ts;

  printf("Enter Testcase - condTimed_main %s\n",testType);

  rc = pthread_mutex_init (&mutex, NULL);
  checkResults("pthread_mutex_init()\n", rc);
    
  rc = pthread_cond_init (&cond, NULL);
  checkResults("pthread_cond_init()\n", rc);
    
  rc = pthread_mutex_lock(&mutex);
  checkResults("pthread_mutex_lock()\n", rc);

  printf("Try steal a signal 1 (wait on own signal), should timeout\n");
  rc = pthread_cond_broadcast(&cond);
  checkResults("pthread_cond_signal()\n", rc);

  rc = pthread_cond_timedwait(&cond, &mutex, starttimer(&ts, 3000 ));
  printf("rc=%d\n",rc);
  checkResults("pthread_cond_timedwait() Steal 1\n", rc - ETIMEDOUT);
  printf("Done, rc=%d\n",rc);
  rc = pthread_mutex_unlock(&mutex);

  printf("Create %d threads\n", COND_NTHREADS);
  for(i=0; i<COND_NTHREADS; ++i) {
    rc = pthread_create(&threadid[i], NULL, condTimed_threadfunc, NULL);
    checkResults("pthread_create()\n", rc);
  }
  Sleep(2000);
  printf("One work item to give to a thread\n");
  workToDo = 1;
  rc = pthread_mutex_lock(&mutex);
  rc = pthread_cond_signal(&cond);
  checkResults("pthread_cond_signal()\n", rc);
  rc = pthread_mutex_unlock(&mutex);
  checkResults("pthread_mutex_unlock()\n", rc);

  Sleep(3001);
  rc = pthread_mutex_lock(&mutex);
  checkResults("pthread_mutex_lock() 2\n", rc);
  printf("One another work item to give to a thread\n");
  workToDo = 1;
  rc = pthread_cond_signal(&cond);
  checkResults("pthread_cond_signal()\n", rc);

  rc = pthread_mutex_unlock(&mutex);
  checkResults("pthread_mutex_unlock()\n", rc);

  //Sleep(10000);
  printf("Broadcast leave to all threads, waiters=%d\n",((cond_t *)cond)->waiters_count_);
  workLeave = 1;
  rc = pthread_cond_broadcast(&cond);
   printf("Broadcast done, waiters=%d\n",((cond_t *)cond)->waiters_count_);
  checkResults("pthread_cond_broadcast()\n", rc);
  printf("Try steal a signal 2, should timeout\n");
  rc = pthread_mutex_lock(&mutex);
  printf("pthread_mutex_lock, waiters=%d\n",((cond_t *)cond)->waiters_count_);
  rc = pthread_cond_timedwait(&cond, &mutex, starttimer(&ts, 3000 ));
  printf("pthread_cond_timedwait, waiters=%d\n",((cond_t *)cond)->waiters_count_);
  checkResults("pthread_cond_timedwait() Steal 2\n", rc - ETIMEDOUT);
  printf("Done, rc=%d\n",rc);
  rc = pthread_mutex_unlock(&mutex);

  printf("Wait for threads and cleanup\n");
  for (i=0; i<COND_NTHREADS; ++i) {
    rc = pthread_join(threadid[i], NULL);
    checkResults("pthread_join()\n", rc);
  }

  printf("Exit, waiters=%d\n",((cond_t *)cond)->waiters_count_);
  pthread_cond_destroy(&cond);
  pthread_mutex_destroy(&mutex);
  printf("Main completed\n");
  return 0;
}
#else
/*================================================================================*/

#define COND_NTHREADS                3
#define COND_WAIT_TIME_SECONDS       10

int                 workToDo = 0;
int                 workLeave = 0;
pthread_cond_t      cond=PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex=PTHREAD_MUTEX_INITIALIZER;

void *condTimed_threadfunc(void *parm)
{
  int               tid;
  int               rc;
  struct timespec   ts;
  struct timeval    tp;

  rc = pthread_mutex_lock(&mutex);
  checkResults("pthread_mutex_lock()\n", rc);
  tid = pthread_self();

  /* Usually worker threads will loop on these operations */
  printf("condTimed_threadfunc, wait %d secs\n", COND_WAIT_TIME_SECONDS);
  while (!workLeave) {
    rc =  gettimeofday(&tp, NULL);
    checkResults("gettimeofday()\n", rc);

    /* Convert from timeval to timespec */
    ts.tv_sec  = tp.tv_sec;
    ts.tv_nsec = tp.tv_usec * 1000;
    ts.tv_sec += COND_WAIT_TIME_SECONDS;

    do {
      if (strcmp(testType,"notTimed") == 0) {
        printf("Thread %d blocked, notTimed\n", tid);
        rc = pthread_cond_wait(&cond, &mutex);
      } else {
        printf("Thread %d blocked\n", tid);
        rc = pthread_cond_timedwait(&cond, &mutex, starttimer(&ts, 3000 ));
      }
      /* If the wait timed out, in this example, the work is complete, and   */
      /* the thread will end.                                                */
      /* In reality, a timeout must be accompanied by some sort of checking  */
      /* to see if the work is REALLY all complete. In the simple example    */
      /* we'll just go belly up when we time out.                            */
      printf("Thread %d unblocked\n", tid);
      if (rc == ETIMEDOUT) {
        printf("Wait %d timed out!\n", tid);
        rc = pthread_mutex_unlock(&mutex);
        checkResults("pthread_mutex_unlock() A\n", rc);
        printf("Exit %d\n", tid);
        pthread_exit(NULL);
      }
      checkResults("pthread_cond_timedwait()\n", rc);
    } while (!workLeave && !workToDo); 
    if (workToDo) {
        printf("Thread %d consumes work here\n", tid);
        Sleep(2000);
        workToDo = 0;
    }
  } 
  printf("Thread %d leaves here\n", tid);
  rc = pthread_mutex_unlock(&mutex);
  checkResults("pthread_mutex_unlock() B\n", rc);
  return NULL;
}

int condTimed_main()
{
  int                   rc=0;
  int                   i;
  pthread_t             threadid[COND_NTHREADS];
  struct timespec   ts;

    printf("Enter Testcase - condTimed_main %s\n",testType);

    if (strcmp(testType,"static") == 0) {
        printf("cond + mutex static initialized\n");
        strcpy(testType, "notTimed");
    } else {
      rc = pthread_mutex_init (&mutex, NULL);
      checkResults("pthread_mutex_init()\n", rc);
    
      rc = pthread_cond_init (&cond, NULL);
      checkResults("pthread_cond_init()\n", rc);
        printf("cond + mutex normal initialized\n");
    }

    
  rc = pthread_mutex_lock(&mutex);
  checkResults("pthread_mutex_lock()\n", rc);
    printf("Mutex locked \n");

  printf("Try steal a signal 1, should timeout\n");
  
  printf("Timed wait 1, waiters=%d\n",cond->waiters_count_);
  rc = pthread_cond_timedwait(&cond, &mutex, starttimer(&ts, 3000 ));
  printf("rc=%d\n",rc);
  checkResults("pthread_cond_timedwait() Steal 1\n", rc - ETIMEDOUT);
  printf("Done, rc=%d\n",rc);
  rc = pthread_mutex_unlock(&mutex);

  printf("Create %d threads\n", COND_NTHREADS);
  for(i=0; i<COND_NTHREADS; ++i) {
    rc = pthread_create(&threadid[i], NULL, condTimed_threadfunc, NULL);
    checkResults("pthread_create()\n", rc);
  }
  Sleep(2000);
  printf("One work item to give to a thread\n");
  workToDo = 1;
  rc = pthread_mutex_lock(&mutex);
  printf("Mutex locked 2\n");
  rc = pthread_cond_signal(&cond);
  checkResults("pthread_cond_signal()\n", rc);
  rc = pthread_mutex_unlock(&mutex);
  checkResults("pthread_mutex_unlock()\n", rc);

  //Sleep(1000);
  rc = pthread_mutex_lock(&mutex);
  checkResults("pthread_mutex_lock() 2\n", rc);
  printf("One another work item to give to a thread\n");
  workToDo = 1;
  rc = pthread_cond_signal(&cond);
  checkResults("pthread_cond_signal()\n", rc);

  rc = pthread_mutex_unlock(&mutex);
  checkResults("pthread_mutex_unlock()\n", rc);

  //Sleep(1000);
  printf("Broadcast leave to all threads, waiters=%d\n",cond->waiters_count_);
  workLeave = 1;
  rc = pthread_cond_broadcast(&cond);
  //Sleep(1000);
  printf("Broadcast done, waiters=%d\n",cond->waiters_count_);
  checkResults("pthread_cond_broadcast()\n", rc);
  printf("Try steal a signal 2, should timeout\n");
  rc = pthread_mutex_lock(&mutex);
  printf("Timed wait 2, waiters=%d\n",cond->waiters_count_);
  rc = pthread_cond_timedwait(&cond, &mutex, starttimer(&ts, 2000 ));
  checkResults("pthread_cond_timedwait() Steal 2\n", rc - ETIMEDOUT);
  printf("Done, rc=%d\n",rc);
  rc = pthread_mutex_unlock(&mutex);

  printf("Wait for threads and cleanup\n");
  for (i=0; i<COND_NTHREADS; ++i) {
    rc = pthread_join(threadid[i], NULL);
    checkResults("pthread_join()\n", rc);
  }

  printf("Exit, waiters=%d\n",cond->waiters_count_);
  pthread_cond_destroy(&cond);
  pthread_mutex_destroy(&mutex);
  printf("Main completed\n");
  return 0;
}

int cond_main()
{
  strcpy(testType, "notTimed");
  return condTimed_main();
}

int condStatic_main()
{
  strcpy(testType, "static");
  return condTimed_main();
}
#endif
/*================================================================================*/
void *rwlockTimed_rdlockThread(void *arg)
{
  int             rc;
  int             count=0;
  struct timespec ts;
 unsigned long long ct1 = _pthread_time_in_ms();

  printf("Entered thread, getting read lock with timeout\n");
  Retry:
  rc = pthread_rwlock_timedrdlock(&rwlock, starttimer(&ts, 5000 ));
  if (rc != 0) {
    if (count >= 10) {
      printf("Retried too many times, failure!\n");
      exit(EXIT_FAILURE);
    }
    ++count;
    printf("RETRY...rc=%d\n",rc);
    goto Retry;
  }
  checkResults("pthread_rwlock_timedrdlock() 1\n", rc);

  Sleep(2000);

  printf("unlock the read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  checkResults("pthread_rwlock_unlock()\n", rc);
 unsigned long long ct2 = _pthread_time_in_ms();

  printf("Secondary thread complete, waited total: %ld\n",ct2-ct1);
  return NULL;
}

int rwlockTimed_main(void)
{
  int                   rc=0;
  pthread_t             thread;

  printf("Enter Testcase rwlockTimed_main\n");

  printf("Main, initialize the read write lock\n");
  //rc = pthread_rwlock_init(&rwlock, NULL);
  rwlock = PTHREAD_RWLOCK_INITIALIZER;
  printf("RWL %p\n",rwlock);
  checkResults("pthread_rwlock_init()\n", rc);

  printf("Main, get the write lock\n");
  rc = pthread_rwlock_wrlock(&rwlock);
  checkResults("pthread_rwlock_wrlock()\n", rc);

  printf("Main, create the timed rd lock thread\n");
  rc = pthread_create(&thread, NULL, rwlockTimed_rdlockThread, NULL);
  checkResults("pthread_create\n", rc);

  printf("Main, wait a bit holding the write lock\n");
  Sleep(18000);

  printf("Main, Now unlock the write lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  checkResults("pthread_rwlock_unlock()\n", rc);

  printf("Main, wait for the thread to end\n");
  rc = pthread_join(thread, NULL);
  checkResults("pthread_join\n", rc);

  rc = pthread_rwlock_destroy(&rwlock);
  rwl_print(&rwlock,"destroyed?");
  checkResults("pthread_rwlock_destroy()\n", rc);
  printf("Main completed\n");
  return 0;
}


/*================================================================================*/
volatile static int rwdata=0;

void *rwlock_rdlockThread(void *arg)
{
  int rc,i;
  struct timespec ts;

  printf("Entered thread, getting read lock\n");
  //rc = pthread_rwlock_rdlock(&rwlock);
  rc = pthread_rwlock_timedrdlock(&rwlock, starttimer(&ts, 30000 ));
  checkResults("pthread_rwlock_rdlock()\n", rc);
  printf("got the rwlock read lock\n");

  for (i=0; i<=200; i++) {
    Sleep(10);
    if (rwdata) {
        printf("RACE cond read %d\n", rwdata);
    }
  }

  printf("unlock the read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  checkResults("pthread_rwlock_unlock()\n", rc);
  printf("Secondary thread unlocked\n");
  return NULL;
}

void *rwlock_wrlockThread(void *arg)
{
  int rc;

  printf("Entered thread, getting write lock\n");
  rc = pthread_rwlock_wrlock(&rwlock);
  checkResults("pthread_rwlock_wrlock()\n", rc);

  rwdata ++;
  Sleep(5000);
  rwdata --;

  printf("Got the rwlock write lock, now unlock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  checkResults("pthread_rwlock_unlock()\n", rc);
  printf("Secondary thread unlocked\n");
  return NULL;
}

int rwlock_main(void)
{
  int                   rc=0;
  pthread_t             thread, thread1;

  printf("Enter Testcase rwlock_main\n");

  printf("Main, initialize the read write lock\n");
  rc = pthread_rwlock_init(&rwlock, NULL);
  checkResults("pthread_rwlock_init()\n", rc);

  rwl_print(&rwlock, "Main, grab a read lock");
  rc = pthread_rwlock_rdlock(&rwlock);
  rwl_print(&rwlock, "Main, grabbbed a read lock");
  checkResults("pthread_rwlock_rdlock()\n",rc);

/*
  printf("Main, grab the same read lock again\n");
  rc = pthread_rwlock_rdlock(&rwlock);
  checkResults("pthread_rwlock_rdlock() second\n", rc);
*/

  printf("Main, create the read lock thread\n");
  rc = pthread_create(&thread, NULL, rwlock_rdlockThread, NULL);
  checkResults("pthread_create\n", rc);

/*
  printf("Main - unlock the first read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  checkResults("pthread_rwlock_unlock()\n", rc);
*/

  printf("Main, create the write lock thread\n");
  rc = pthread_create(&thread1, NULL, rwlock_wrlockThread, NULL);
  checkResults("pthread_create\n", rc);

  printf("Main - unlock the read lock\n");
  rc = pthread_rwlock_unlock(&rwlock);
  checkResults("pthread_rwlock_unlock()\n", rc);

  printf("Main, wait for the threads\n");
  rc = pthread_join(thread, NULL);
  checkResults("pthread_join\n", rc);

  rc = pthread_join(thread1, NULL);
  checkResults("pthread_join\n", rc);

  rc = pthread_rwlock_destroy(&rwlock);
  checkResults("pthread_rwlock_destroy()\n", rc);

  printf("Main completed\n");
  rwl_print(&rwlock, "Main completed");
  return 0;
}

/*================================================================================*/
#define BARRIER_NTHREADS 50

#define BARRIER_ROUNDS 50

static pthread_barrier_t barriers[BARRIER_NTHREADS];

static pthread_mutex_t lock;
static int counters[BARRIER_NTHREADS];
static int serial[BARRIER_NTHREADS];



void *barrier_Thread(void *arg)
{
    void *result = NULL;
    int nr = (int)(uintptr_t)arg;
    int i;

    for (i = 0; i < BARRIER_ROUNDS; ++i)
    {
        int j;
        int retval;

        if (nr == 0)
        {
            memset (counters, '\0', sizeof (counters));
            memset (serial, '\0', sizeof (serial));
        }

        retval = pthread_barrier_wait (&barriers[BARRIER_NTHREADS - 1]);
        if (retval != 0 && retval != PTHREAD_BARRIER_SERIAL_THREAD)
        {
            printf ("thread %d failed to wait for all the others\n", nr);
            result = (void *) 1;
        }

        for (j = nr; j < BARRIER_NTHREADS; ++j)
        {
            /* Increment the counter for this round.  */
            //printf ("Increment the counter for this round %d\n", j);
            pthread_mutex_lock (&lock);
            ++counters[j];
            pthread_mutex_unlock (&lock);
            //printf ("Incremented the counter for this round %d\n", j);

            /* Wait for the rest.  */
            retval = pthread_barrier_wait (&barriers[j]);

            /* Test the result.  */
            if (nr == 0 && counters[j] != j + 1)
            {
                printf ("barrier in round %d released but count is %d\n",
                        j, counters[j]);
                result = (void *) 1;
            }

            if (retval != 0)
            {
                if (retval != PTHREAD_BARRIER_SERIAL_THREAD)
                {
                    printf ("thread %d in round %d has nonzero return value != PTHREAD_BARRIER_SERIAL_THREAD\n",
                            nr, j);
                    result = (void *) 1;
                }
                else
                {
                    //printf ("pthread_mutex_lock %d\n", nr);
                    pthread_mutex_lock (&lock);
                    ++serial[j];
                    pthread_mutex_unlock (&lock);
                }
            }

            /* Wait for the rest again.  */
            printf ("Wait for the rest again %d\n",j);
            retval = pthread_barrier_wait (&barriers[j]);
            /* the following printf can make bugs go away - timing dependend */
            /* without this printf the test hangs here */
            /* try USE_MUTEX_CriticalSection + USE_COND_Semaphore */
            //printf ("Wait for the rest again continue %d\n",j);
    
            /* Now we can check whether exactly one thread was serializing.  */
            if (nr == 0 && serial[j] != 1)
            {
                printf ("not exactly one serial thread in round %d\n", j);
                result = (void *) 1;
                exit(1);
            }
        }
    }

    return result;
}

int barrier_main(void)
{
    int                   rc=0;
    pthread_t threads[BARRIER_NTHREADS];
    int i;
    void *res;
    int result = 0;


    printf("Enter Testcase - barrier_main %s\n",testType);

    rc = pthread_mutex_init (&lock, NULL);
    checkResults("pthread_mutex_init()\n", rc);

    /* Initialized the barrier variables.  */
    for (i = 0; i < BARRIER_NTHREADS; ++i) {
        if (pthread_barrier_init (&barriers[i], NULL, i + 1) != 0)
        {
            printf ("Failed to initialize barrier %d\n", i);
            exit (1);
        }
        printf ("initialized barrier %d\n", i);
    }

    /* Start the threads.  */
    for (i = 0; i < BARRIER_NTHREADS; ++i) {
        if (pthread_create (&threads[i], NULL, barrier_Thread, (void *)(uintptr_t)i) != 0)
        {
            printf ("Failed to start thread %d\n", i);
            exit (1);
        }
        printf ("started thread %d\n", i);
    }

    /* And wait for them.  */
    printf ("Wait for %d threads\n", i);
    for (i = 0; i < BARRIER_NTHREADS; ++i) {
        if (pthread_join (threads[i], &res) != 0 || res != NULL)
        {
            printf ("thread %d returned a failure\n", i);
            result = 1;
        }
    }

    printf ("Result: %d\n", result);
    if (result == 0)
        puts ("all OK");

    return result;

}
/*================================================================================*/
void thread(void)
{
    int i;
    parm *p;
    pthread_t *threads;
    pthread_attr_t pthread_custom_attr;
    int n = N_THREAD;
    n = N_THREAD;
    if ((n < 1) || (n > MAX_THREAD))
    {
        printf ("The no of thread should between 1 and %d.\n",MAX_THREAD);
        exit(1);
    }

    threads=(pthread_t *)malloc(n*sizeof(*threads));
    pthread_attr_init(&pthread_custom_attr);

    p=(parm *)malloc(sizeof(parm)*n);
    /* Start up thread */

    for (i=0; i<n; i++)
    {
        p[i].id=i;
        pthread_create(&threads[i], &pthread_custom_attr, hello, (void *)(p+i));
    }

    /* Synchronize the completion of each thread. */

    for (i=0; i<n; i++)
    {
        pthread_join(threads[i],NULL);
    }
    free(p);
     pthread_exit(NULL);
}

int main(int argc, char * argv[]) {
    char name[100];

    if (argc < 2)
    {
        printf ("Usage: %s <name> [type]\nwhere <name> is test name\n",argv[0]);
        printf ("test names are: thread, rwlock, rwlockTimed, cond, condTimed, condStatic, spinlock, barrier, mutex [static[N|R|E|ND]].\n");
        exit(1);
    }
    strcpy(testType, "default");
    if (argc == 3)
    {
        strcpy(testType, argv[2]);
    }

    strcpy(name, argv[1]); 
    printf ("Threads test: %s\n",name);
    printf ("P size: %d %d\n",SIZE_MAX>UINT_MAX,sizeof(struct timespec ));
    if (strcmp(name, "thread") == 0) thread();
    else if (strcmp(name, "rwlock") == 0) rwlock_main();
    else if (strcmp(name, "rwlockTimed") == 0) rwlockTimed_main();
    //else if (strcmp(name, "cond") == 0) cond_main();
    else if (strcmp(name, "condTimed") == 0) condTimed_main();
    //else if (strcmp(name, "condStatic") == 0) condStatic_main();
    else if (strcmp(name, "barrier") == 0) barrier_main();
    else if (strcmp(name, "mutex") == 0) mutex_main();
    else if (strcmp(name, "spinlock") == 0) spinlock_main();
    else printf ("Unknown test name '%s'\n",name);
 
        return 0;
}
