/**
 * A single run
 */
void singleTest(int numThreads, char * title, int lock_type, pthread_t * pthread_list) {
    int i;
    int threadid[NUM_THREADS];
    printf(title);
    g_which_lock = lock_type;
    clearOperCounters();
    // Start the threads
    for(i = 0; i < numThreads; i++ ) {
        threadid[i] = i;
        pthread_create(&pthread_list[i], NULL, (void *(*)(void *))worker_thread, (void *)&threadid[i]);
    }
    sleep(10);
    atomic_store(&g_quit, 1);
    for (i = 0; i < numThreads; i++) {
        pthread_join(pthread_list[i], NULL);
    }
    atomic_store(&g_quit, 0);
    printOperationsPerSecond();
}
/**
 * Starts 4 pthreads and uses either a pthread_rwlock_t or a di_rwlock_t
 * to protect access to an array.
 *
 */
int main(void) {
    int i;
    pthread_t *pthread_list;
    int threadid[NUM_THREADS];

    /* Allocate memory for the two instance arrays */
    array1 = (int *)malloc(ARRAY_SIZE*sizeof(int));
    if (array1 == NULL) {
        printf("Not enough memory to allocate array\n");
        return -1;
    }
    for (i = 0; i < ARRAY_SIZE; i++) {
        array1[i] = 0;
    }

    /* Initialize locks */
    pthread_mutex_init(&pmutex, NULL);
    pthread_spin_init(&pspin, PTHREAD_PROCESS_PRIVATE);
    mpsc_mutex_init(&mpscmutex);
    ticket_mutex_init(&ticketmutex);
    clh_mutex_init(&clhmutex);

    printf("Starting benchmark with %d threads\n", NUM_THREADS);
    printf("Array has size of %d\n", ARRAY_SIZE);

    // Create the threads
    pthread_list = (pthread_t *)calloc(sizeof(pthread_t), NUM_THREADS);

    printf("Doing test for pthread_mutex_t, sleeping for 10 seconds...\n");
    g_which_lock = TYPE_PTHREAD_MUTEX;
    clearOperCounters();
    // Start the threads
    for(i = 0; i < NUM_THREADS; i++ ) {
        threadid[i] = i;
        pthread_create(&pthread_list[i], NULL, (void *(*)(void *))worker_thread, (void *)&threadid[i]);
    }
    sleep(10);
    g_quit = 1;

    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(pthread_list[i], NULL);
    }
    g_quit = 0;
    printOperationsPerSecond();

    printf("Doing test for pthread_spin_t, sleeping for 10 seconds\n");
    g_which_lock = TYPE_PTHREAD_SPIN;
    clearOperCounters();
    for(i = 0; i < NUM_THREADS; i++ ) {
        pthread_create(&pthread_list[i], NULL, (void *(*)(void *))worker_thread, (void *)&threadid[i]);
    }
    sleep(10);
    g_quit = 1;
    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(pthread_list[i], NULL);
    }
    g_quit = 0;
    printOperationsPerSecond();

    printf("Doing test for mpsc_mutex_t, sleeping for 10 seconds\n");
    g_which_lock = TYPE_MPSC_MUTEX;
    clearOperCounters();
    for(i = 0; i < NUM_THREADS; i++ ) {
        pthread_create(&pthread_list[i], NULL, (void *(*)(void *))worker_thread, (void *)&threadid[i]);
    }
    sleep(10);
    g_quit = 1;
    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(pthread_list[i], NULL);
    }
    g_quit = 0;
    printOperationsPerSecond();

    printf("Doing test for ticket_mutex_t, sleeping for 10 seconds...\n");
    g_which_lock = TYPE_TICKET_MUTEX;
    clearOperCounters();
    // Start the threads
    for(i = 0; i < NUM_THREADS; i++ ) {
        threadid[i] = i;
        pthread_create(&pthread_list[i], NULL, (void *(*)(void *))worker_thread, (void *)&threadid[i]);
    }
    sleep(10);
    g_quit = 1;
    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(pthread_list[i], NULL);
    }
    g_quit = 0;
    printOperationsPerSecond();

    printf("Doing test for exchg_mutex_t, sleeping for 10 seconds...\n");
    g_which_lock = TYPE_EXCHG_MUTEX;
    clearOperCounters();
    // Start the threads
    for(i = 0; i < NUM_THREADS; i++ ) {
        threadid[i] = i;
        pthread_create(&pthread_list[i], NULL, (void *(*)(void *))worker_thread, (void *)&threadid[i]);
    }
    sleep(10);
    g_quit = 1;
    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(pthread_list[i], NULL);
    }
    g_quit = 0;
    printOperationsPerSecond();

    /* Destroy locks */
    pthread_mutex_destroy(&pmutex);
    pthread_spin_destroy(&pspin);
    mpsc_mutex_destroy(&mpscmutex);
    ticket_mutex_destroy(&ticketmutex);
    clh_mutex_destroy(&clhmutex);

    /* Release memory for the array instances and threads */
    free(array1);
    free(pthread_list);
    return 0;
}