/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
 *  (C) 2008 by Argonne National Laboratory.
 *      See COPYRIGHT in top-level directory.
 */

/*
 * Uncomment this definition to disable the OPA library and instead use naive
 * (non-atomic) operations.  This should cause failures.
 */
/*
#define OPA_TEST_NAIVE
*/

#include "opa_test.h"

/* Definitions for test_threaded_loadstore_int */
/* LOADSTORE_INT_DIFF is the difference between the unique values of successive
 * threads.  This value contains 0's in the top half of the int and 1's in the
 * bottom half.  Therefore, for 4 byte ints the sequence of unique values is:
 * 0x00000000, 0x0000FFFF, 0x0001FFFE, 0x0002FFFD, etc. */
#define LOADSTORE_INT_DIFF ((1 << (sizeof(int) * CHAR_BIT / 2)) - 1)
#define LOADSTORE_INT_NITER (4000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being read/written by all threads */
    int unique_val;             /* This thread's unique value to store in shared_val */
    int nerrors;                /* Number of errors */
    int master_thread;          /* Whether this is the master thread */
} loadstore_int_t;

/* Definitions for test_threaded_loadstore_ptr */
#define LOADSTORE_PTR_DIFF (((unsigned long) 1 << (sizeof(void *) * CHAR_BIT / 2)) - 1)
#define LOADSTORE_PTR_NITER (4000000 / iter_reduction[curr_test])
typedef struct {
    OPA_ptr_t *shared_val;      /* Shared int being read/written by all threads */
    void *unique_val;           /* This thread's unique value to store in shared_val */
    int nerrors;                /* Number of errors */
    int master_thread;          /* Whether this is the master thread */
} loadstore_ptr_t;

/* Definitions for test_threaded_add */
#define ADD_EXPECTED ADD_NITER
#define ADD_NITER (2000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being added to by all threads */
    int unique_val;             /* This thread's unique value to add to shared_val */
    int master_thread;          /* Whether this is the master thread */
} add_t;

/* Definitions for test_threaded_incr_decr */
#define INCR_DECR_EXPECTED INCR_DECR_NITER
#define INCR_DECR_NITER (2000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being manipulated by all threads */
    int master_thread;          /* Whether this is the master thread */
} incr_decr_t;

/* Definitions for test_threaded_decr_and_test */
#define DECR_AND_TEST_NITER_INNER 20    /* *Must* be even */
#define DECR_AND_TEST_NITER_OUTER (10000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being decr and tested by all threads */
    unsigned ntrue;             /* # of times decr_and_test returned true */
    int master_thread;          /* Whether this is the master thread */
} decr_test_t;

/* Definitions for test_threaded_faa */
/* Uses definitions from test_threaded_add */

/* Definitions for test_threaded_faa_ret */
#define FAA_RET_EXPECTED (-((nthreads - 1) * FAA_RET_NITER))
#define FAA_RET_NITER (2000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being added to by all threads */
    int nerrors;                /* Number of errors */
    int n1;                     /* # of times faa returned 1 */
    int master_thread;          /* Whether this is the master thread */
} faa_ret_t;

/* Definitions for test_threaded_fai_fad */
/* Uses definitions from test_threaded_incr_decr */

/* Definitions for test_threaded_fai_ret */
#define FAI_RET_EXPECTED ((nthreads - 1) * FAA_RET_NITER)
#define FAI_RET_NITER (2000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being added to by all threads */
    int nerrors;                /* Number of errors */
    int nm1;                    /* # of times faa returned -1 */
    int master_thread;          /* Whether this is the master thread */
} fai_ret_t;

/* Definitions for test_threaded_fad_red */
/* Uses definitions from test_threaded_faa_ret */

/* Definitions for test_threaded_cas_int */
#define CAS_INT_NITER (5000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being manipulated by all threads */
    int threadno;               /* Unique thread number */
    int nsuccess;               /* # of times cas succeeded */
    int master_thread;          /* Whether this is the master thread */
} cas_int_t;

/* Definitions for test_threaded_cas_ptr */
#define CAS_PTR_NITER (5000000 / iter_reduction[curr_test])
typedef struct {
    OPA_ptr_t *shared_val;      /* Shared ptr being manipulated by all threads */
    int *threadno;              /* Unique thread number */
    int *max_threadno;          /* Maximum unique thread number */
    int nsuccess;               /* # of times cas succeeded */
    int master_thread;          /* Whether this is the master thread */
} cas_ptr_t;

/* Definitions for test_grouped_cas_int */
#define GROUPED_CAS_INT_NITER (5000000 / iter_reduction[curr_test])
#define GROUPED_CAS_INT_TPG 4
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being manipulated by all threads */
    int groupno;                /* Unique group number */
    int ngroups;                /* Number of groups */
    int nsuccess;               /* # of times cas succeeded */
    int master_thread;          /* Whether this is the master thread */
} grouped_cas_int_t;

/* Definitions for test_grouped_cas_ptr */
#define GROUPED_CAS_PTR_NITER (5000000 / iter_reduction[curr_test])
#define GROUPED_CAS_PTR_TPG 4
typedef struct {
    OPA_ptr_t *shared_val;      /* Shared ptr being manipulated by all threads */
    int *groupno;               /* Unique group number */
    int *max_groupno;           /* Maximum unique group number */
    int nsuccess;               /* # of times cas succeeded */
    int master_thread;          /* Whether this is the master thread */
} grouped_cas_ptr_t;

/* Definitions for test_threaded_cas_int_fairness */
#define CAS_INT_FAIRNESS_MIN_SUCCESS (100 / iter_reduction[curr_test])
#define CAS_INT_FAIRNESS_MAX_ITER (10000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being manipulated by all threads */
    int nerrors;                /* Number of errors */
    int threadno;               /* Unique thread number */
    int nthreads;               /* Number of threads */
    OPA_int_t *successful_threads;      /* # of threads that have completed */
    int terminated;             /* Whether this thread was terminated by MAX_ITER */
    int master_thread;          /* Whether this is the master thread */
} cas_int_fairness_t;

/* Definitions for test_threaded_cas_ptr_fairness */
#define CAS_PTR_FAIRNESS_MIN_SUCCESS (100 / iter_reduction[curr_test])
#define CAS_PTR_FAIRNESS_MAX_ITER (10000000 / iter_reduction[curr_test])
typedef struct {
    OPA_ptr_t *shared_val;      /* Shared ptr being manipulated by all threads */
    int nerrors;                /* Number of errors */
    int *threadno;              /* Unique thread number */
    int nthreads;               /* Number of threads */
    OPA_int_t *successful_threads;      /* # of threads that have completed */
    int terminated;             /* Whether this thread was terminated by MAX_ITER */
    int master_thread;          /* Whether this is the master thread */
} cas_ptr_fairness_t;

/* Definitions for test_threaded_swap_int */
#define SWAP_INT_NITER (2000000 / iter_reduction[curr_test])
typedef struct {
    OPA_int_t *shared_val;      /* Shared int being manipulated by all threads */
    int local_val;              /* Local int value for this thread */
    int master_thread;          /* Whether this is the master thread */
} swap_int_t;

/* Definitions for test_threaded_swap_ptr */
#define SWAP_PTR_NITER (2000000 / iter_reduction[curr_test])
typedef struct {
    OPA_ptr_t *shared_val;      /* Shared ptr being manipulated by all threads */
    void *local_val;            /* Local ptr value for this thread */
    unsigned threadno;          /* Thread number */
    int master_thread;          /* Whether this is the master thread */
} swap_ptr_t;

/* Definitions for test_threaded_llsc_int_aba */
#define LLSC_INT_ABA_NITER 2000000
typedef struct {
    char padding1[OPA_TEST_CACHELINE_PADDING];
    OPA_int_t shared_val;       /* Shared value being manipulated by both threads */
    char padding2[OPA_TEST_CACHELINE_PADDING - sizeof(OPA_int_t)];
    OPA_int_t pass_point_0;     /* Whether we have passed point 0 */
    OPA_int_t pass_point_1;     /* Whether we have passed point 1 */
    OPA_int_t pass_point_2;     /* Whether we have passed point 2 */
    OPA_int_t change_val;       /* Whether we will change/have changed shared_val */
    int false_positives;        /* Number of times SC failed unnecessarily */
    int nunchanged_val;         /* Number of times change_val was false */
} llsc_int_aba_t;

/* Definitions for test_threaded_llsc_ptr_aba */
#define LLSC_PTR_ABA_NITER 2000000
typedef struct {
    char padding1[OPA_TEST_CACHELINE_PADDING];
    OPA_ptr_t shared_val;       /* Shared value being manipulated by both threads */
    char padding2[OPA_TEST_CACHELINE_PADDING - sizeof(OPA_ptr_t)];
    OPA_int_t pass_point_0;     /* Whether we have passed point 0 */
    OPA_int_t pass_point_1;     /* Whether we have passed point 1 */
    OPA_int_t pass_point_2;     /* Whether we have passed point 2 */
    OPA_int_t change_val;       /* Whether we will change/have changed shared_val */
    int false_positives;        /* Number of times SC failed unnecessarily */
    int nunchanged_val;         /* Number of times change_val was false */
} llsc_ptr_aba_t;

/* Definitions for test_threaded_llsc_int_stack */
#define LLSC_INT_STACK_NITER (100000 / iter_reduction[curr_test])
#define LLSC_INT_STACK_NLOOP 100000
#define LLSC_INT_STACK_TPG 3
typedef struct {
    OPA_int_t on_stack;         /* Whether this object is on the stack */
    OPA_int_t next;             /* Index of next object in stack */
} llsc_int_stack_obj_t;
typedef struct {
    OPA_int_t *head;            /* Head of stack (index into objs) */
    llsc_int_stack_obj_t **objs;        /* Array of pointers to objects */
    int obj;                    /* Object for currect pusher thread (index into objs) */
    OPA_int_t *npushers;        /* Number of pusher threads running */
    int nsuccess;               /* Number of successful pops or pushes */
    int nerrors;                /* Number of errors */
    OPA_int_t *failed;          /* Whether the test has failed and should be terminated */
    int master_thread;          /* Whether this is the master thread */
} llsc_int_stack_t;

/* Definitions for test_threaded_llsc_ptr_stack */
#define LLSC_PTR_STACK_NITER (100000 / iter_reduction[curr_test])
#define LLSC_PTR_STACK_NLOOP 100000
#define LLSC_PTR_STACK_TPG 3
typedef struct {
    OPA_int_t on_stack;         /* Whether this object is on the stack */
    OPA_ptr_t next;             /* Next object in stack */
} llsc_ptr_stack_obj_t;
typedef struct {
    OPA_ptr_t *head;            /* Head of stack (index into objs) */
    llsc_ptr_stack_obj_t *obj;  /* Object for currect pusher thread */
    OPA_int_t *npushers;        /* Number of pusher threads running */
    int nsuccess;               /* Number of successful pops or pushes */
    int nerrors;                /* Number of errors */
    OPA_int_t *failed;          /* Whether the test has failed and should be terminated */
    int master_thread;          /* Whether this is the master thread */
} llsc_ptr_stack_t;


/*-------------------------------------------------------------------------
 * Function: test_simple_loadstore_int
 *
 * Purpose: Tests basic functionality of OPA_load_int and OPA_store_int with a
 *          single thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, March 19, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_loadstore_int(void)
{
    OPA_int_t a, b;

    TESTING("simple integer load/store functionality", 0);

    /* Store 0 in a, -1 in b.  Verify that these values are returned by
     * OPA_load_int. */
    OPA_store_int(&a, 0);
    if (0 != OPA_load_int(&a))
        TEST_ERROR;
    OPA_store_int(&b, -1);
    if (-1 != OPA_load_int(&b))
        TEST_ERROR;
    if (0 != OPA_load_int(&a))
        TEST_ERROR;
    if (-1 != OPA_load_int(&b))
        TEST_ERROR;

    /* Store INT_MIN in a and INT_MAX in b.  Verify that these values are
     * returned by OPA_load_int. */
    OPA_store_int(&a, INT_MIN);
    if (INT_MIN != OPA_load_int(&a))
        TEST_ERROR;
    OPA_store_int(&b, INT_MAX);
    if (INT_MAX != OPA_load_int(&b))
        TEST_ERROR;
    if (INT_MIN != OPA_load_int(&a))
        TEST_ERROR;
    if (INT_MAX != OPA_load_int(&b))
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_loadstore_int() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_loadstore_int_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_loadstore_int
 *
 * Return: Success: NULL
 *         Failure: non-NULL
 *
 * Programmer: Neil Fortner
 *             Thursday, March 19, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_loadstore_int_helper(void *_udata)
{
    loadstore_int_t *udata = (loadstore_int_t *) _udata;
    int loaded_val;
    unsigned niter = LOADSTORE_INT_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Store the unique value into the shared value */
        OPA_store_int(udata->shared_val, udata->unique_val);

        /* Load the shared_value, and check if it is valid */
        if ((loaded_val = OPA_load_int(udata->shared_val)) % LOADSTORE_INT_DIFF) {
            printf("    Unexpected load: %d is not a multiple of %d\n",
                   loaded_val, LOADSTORE_INT_DIFF);
            udata->nerrors++;
        }       /* end if */
    }   /* end for */

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_loadstore_int_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_loadstore_int
 *
 * Purpose: Tests atomicity of OPA_load_int and OPA_store_int.  Launches nthreads
 *          threads, each of which repeatedly loads and stores a shared
 *          variable.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, March 19, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_loadstore_int(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    loadstore_int_t *thread_data = NULL;        /* User data structs for each thread */
    OPA_int_t shared_int;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned nerrors = 0;       /* number of errors */
    unsigned i;

    TESTING("integer load/store", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (loadstore_int_t *) calloc(nthreads, sizeof(loadstore_int_t))))
        TEST_ERROR;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Initialize thread data structs */
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_int;
        thread_data[i].unique_val = i * LOADSTORE_INT_DIFF;;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_loadstore_int_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_loadstore_int_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Check for errors */
    for (i = 0; i < nthreads; i++)
        nerrors += thread_data[i].nerrors;
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_load_int\n", nerrors,
                             nerrors == 1 ? "" : "s"));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("integer load/store", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_loadstore_int() */


/*-------------------------------------------------------------------------
 * Function: test_simple_loadstore_ptr
 *
 * Purpose: Tests basic functionality of OPA_load_int and OPA_store_int with a
 *          single thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, March 20, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_loadstore_ptr(void)
{
    OPA_ptr_t a, b;

    TESTING("simple pointer load/store functionality", 0);

    /* Store 0 in a, 1 in b.  Verify that these values are returned by
     * OPA_load_int. */
    OPA_store_ptr(&a, (void *) 0);
    if ((void *) 0 != OPA_load_ptr(&a))
        TEST_ERROR;
    OPA_store_ptr(&b, (void *) 1);
    if ((void *) 1 != OPA_load_ptr(&b))
        TEST_ERROR;
    if ((void *) 0 != OPA_load_ptr(&a))
        TEST_ERROR;
    if ((void *) 1 != OPA_load_ptr(&b))
        TEST_ERROR;

    /* Store -1 in a and -2 in b.  Verify that these values are returned by
     * OPA_load_int. */
    OPA_store_ptr(&a, (void *) -2);
    if ((void *) -2 != OPA_load_ptr(&a))
        TEST_ERROR;
    OPA_store_ptr(&b, (void *) -1);
    if ((void *) -1 != OPA_load_ptr(&b))
        TEST_ERROR;
    if ((void *) -2 != OPA_load_ptr(&a))
        TEST_ERROR;
    if ((void *) -1 != OPA_load_ptr(&b))
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_loadstore_ptr() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_loadstore_ptr_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_loadstore_ptr
 *
 * Return: Success: NULL
 *         Failure: non-NULL
 *
 * Programmer: Neil Fortner
 *             Thursday, March 20, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_loadstore_ptr_helper(void *_udata)
{
    loadstore_ptr_t *udata = (loadstore_ptr_t *) _udata;
    unsigned long loaded_val;
    int niter = LOADSTORE_PTR_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Store the unique value into the shared value */
        OPA_store_ptr(udata->shared_val, udata->unique_val);

        /* Load the shared_value, and check if it is valid */
        if ((loaded_val = (unsigned long) OPA_load_ptr(udata->shared_val)) % LOADSTORE_PTR_DIFF) {
            printf("    Unexpected load: %lu is not a multiple of %lu\n",
                   loaded_val, LOADSTORE_PTR_DIFF);
            udata->nerrors++;
        }       /* end if */
    }   /* end for */

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_loadstore_ptr_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_loadstore_ptr
 *
 * Purpose: Tests atomicity of OPA_load_int and OPA_store_int.  Launches nthreads
 *          threads, each of which repeatedly loads and stores a shared
 *          variable.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, March 20, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_loadstore_ptr(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    loadstore_ptr_t *thread_data = NULL;        /* User data structs for each thread */
    OPA_ptr_t shared_ptr;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned nerrors = 0;       /* number of errors */
    unsigned i;

    TESTING("pointer load/store", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (loadstore_ptr_t *) calloc(nthreads, sizeof(loadstore_ptr_t))))
        TEST_ERROR;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Initialize thread data structs */
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_ptr;
        thread_data[i].unique_val = (void *) ((unsigned long) i * LOADSTORE_PTR_DIFF);
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_loadstore_ptr_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_loadstore_ptr_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;


    /* Check for errors */
    for (i = 0; i < nthreads; i++)
        nerrors += thread_data[i].nerrors;
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_load_ptr\n", nerrors,
                             nerrors == 1 ? "" : "s"));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("pointer load/store", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_loadstore_ptr() */


/*-------------------------------------------------------------------------
 * Function: test_simple_add_incr_decr
 *
 * Purpose: Tests basic functionality of OPA_add_int, OPA_incr_int and
 *          OPA_decr_int with a single thread.  Does not test atomicity
 *          of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, March 20, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_add_incr_decr(void)
{
    OPA_int_t a;

    TESTING("simple add/incr/decr functionality", 0);

    /* Store 0 in a */
    OPA_store_int(&a, 0);

    /* Add INT_MIN */
    OPA_add_int(&a, INT_MIN);

    /* Increment */
    OPA_incr_int(&a);

    /* Add INT_MAX */
    OPA_add_int(&a, INT_MAX);

    /* Decrement */
    OPA_decr_int(&a);

    /* Load the result, verify it is correct */
    if (OPA_load_int(&a) != INT_MIN + 1 + INT_MAX - 1)
        TEST_ERROR;

    /* Store 0 in a */
    OPA_store_int(&a, 0);

    /* Add INT_MAX */
    OPA_add_int(&a, INT_MAX);

    /* Decrement */
    OPA_decr_int(&a);

    /* Add INT_MIN */
    OPA_add_int(&a, INT_MIN);

    /* Increment */
    OPA_incr_int(&a);

    /* Load the result, verify it is correct */
    if (OPA_load_int(&a) != INT_MAX - 1 + INT_MIN + 1)
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_add_incr_decr() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_add_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_add
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Thursday, March 20, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_add_helper(void *_udata)
{
    add_t *udata = (add_t *) _udata;
    unsigned niter = ADD_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        /* Add the unique value to the shared value */
        OPA_add_int(udata->shared_val, udata->unique_val);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_add_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_add
 *
 * Purpose: Tests atomicity of OPA_add_int.  Launches nthreads threads
 *          each of which repeatedly adds a unique number to a shared
 *          variable.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, March 20, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_add(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    add_t *thread_data = NULL;  /* User data structs for each thread */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("add", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (add_t *) calloc(nthreads, sizeof(add_t))))
        TEST_ERROR;

    /* Initialize thread data structs.  All the unique values must add up to
     * 0. */
    OPA_store_int(&shared_val, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].unique_val = i - (nthreads - 1) / 2
            - (!(nthreads % 2) && (i >= nthreads / 2))
            + (i == nthreads - 1);
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_add_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_add_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != ADD_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), ADD_EXPECTED));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("add", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_add() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_incr_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_incr_decr
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, April 21, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_incr_helper(void *_udata)
{
    incr_decr_t *udata = (incr_decr_t *) _udata;
    unsigned niter = INCR_DECR_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        /* Increment the shared value */
        OPA_incr_int(udata->shared_val);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_incr_helper() */


/*-------------------------------------------------------------------------
 * Function: threaded_decr_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_incr_decr
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, April 21, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_decr_helper(void *_udata)
{
    incr_decr_t *udata = (incr_decr_t *) _udata;
    unsigned niter = INCR_DECR_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        /* Decrement the shared value */
        OPA_decr_int(udata->shared_val);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_decr_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_incr_decr
 *
 * Purpose: Tests atomicity of OPA_incr_int and OPA_decr_int.  Launches
 *          nthreads threads, each of which repeatedly either increments
 *          or decrements a shared variable.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, April 21, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_incr_decr(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    incr_decr_t *thread_data = NULL;    /* User data structs for each thread */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    /* Must use an odd number of threads */
    if (!(nthreads & 1))
        nthreads--;

    TESTING("incr and decr", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc(nthreads * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (incr_decr_t *) calloc(nthreads, sizeof(incr_decr_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, 0);
    for (i = 0; i < nthreads; i++)
        thread_data[i].shared_val = &shared_val;
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Set the initial state of the shared value (0) */
    OPA_store_int(&shared_val, 0);

    /* Create the threads.  All the unique values must add up to 0. */
    for (i = 0; i < (nthreads - 1); i++) {
        if (i & 1) {
            if (pthread_create(&threads[i], &ptattr, threaded_decr_helper, &thread_data[i]))
                TEST_ERROR;
        } else if (pthread_create(&threads[i], &ptattr, threaded_incr_helper, &thread_data[i]))
            TEST_ERROR;
    }   /* end for */
    if (i & 1)
        (void) threaded_decr_helper(&thread_data[i]);
    else
        (void) threaded_incr_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != INCR_DECR_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), INCR_DECR_EXPECTED));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("incr and decr", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_incr_decr() */


/*-------------------------------------------------------------------------
 * Function: test_simple_decr_and_test
 *
 * Purpose: Tests basic functionality of OPA_decr_and_test_int with a
 *          single thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Wednesday, April 22, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_decr_and_test(void)
{
    OPA_int_t a;
    int i;

    TESTING("simple decr and test functionality", 0);

    /* Store 10 in a */
    OPA_store_int(&a, 10);

    for (i = 0; i < 20; i++)
        if (OPA_decr_and_test_int(&a))
            if (i != 9)
                TEST_ERROR;

    if (OPA_load_int(&a) != -10)
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_decr_and_test() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_decr_and_test_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_decr_and_test
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Wednesday, April 22, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_decr_and_test_helper(void *_udata)
{
    decr_test_t *udata = (decr_test_t *) _udata;
    unsigned i;

    /* Main loop */
    for (i = 0; i < DECR_AND_TEST_NITER_INNER; i++)
        /* Add the unique value to the shared value */
        if (OPA_decr_and_test_int(udata->shared_val))
            udata->ntrue++;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_decr_and_test_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_decr_and_test
 *
 * Purpose: Tests atomicity of OPA_decr_and_test_int.  Launches nthreads
 *          threads, each of which repeatedly adds a unique number to a
 *          shared variable.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, April 21, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_decr_and_test(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    decr_test_t *thread_data = NULL;    /* User data structs for each thread */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned ntrue_total;
    int starting_val;
    unsigned niter = DECR_AND_TEST_NITER_OUTER;
    unsigned nthreads = num_threads[curr_test];
    unsigned nerrors = 0;
    unsigned i, j;

    TESTING("decr and test", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (decr_test_t *) calloc(nthreads, sizeof(decr_test_t))))
        TEST_ERROR;

    /* Initialize the "shared_val" and "master thread" fields (ntrue will be
     * initialized in the loop) */
    for (i = 0; i < nthreads; i++)
        thread_data[i].shared_val = &shared_val;
    thread_data[nthreads - 1].master_thread = 1;

    /* Calculate the starting value for the shared int */
    starting_val = DECR_AND_TEST_NITER_INNER * nthreads / 2;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Outer loop (perform the threaded test multiple times */
    for (i = 0; i < niter; i++) {
        /* Initialize thread data structs */
        OPA_store_int(&shared_val, starting_val);
        for (j = 0; j < nthreads; j++)
            thread_data[j].ntrue = 0;

        /* Create the threads */
        for (j = 0; j < (nthreads - 1); j++)
            if (pthread_create(&threads[j], &ptattr, threaded_decr_and_test_helper,
                               &thread_data[j]))
                TEST_ERROR;
        (void) threaded_decr_and_test_helper(&thread_data[j]);

        /* Join the threads */
        for (j = 0; j < (nthreads - 1); j++)
            if (pthread_join(threads[j], NULL))
                TEST_ERROR;

        /* Verify the results */
        ntrue_total = 0;
        for (j = 0; j < nthreads; j++) {
            /* Verify that OPA_decr_and_test_int returned true at most once */
            if (thread_data[j].ntrue > 1) {
                printf
                    ("\n    Unexpected return from thread %u: OPA_decr_and_test_int returned true %u times",
                     j, thread_data[j].ntrue);
                nerrors++;
            }

            /* end if */
            /* Update ntrue_total */
            ntrue_total += thread_data[j].ntrue;
        }       /* end for */

        /* Verify that OPA_decr_and_test_int returned true exactly once over all
         * the threads. */
        if (ntrue_total != 1) {
            printf("\n    Unexpected result: OPA_decr_and_test_int returned true %u times total",
                   ntrue_total);
            nerrors++;
        }

        /* end if */
        /* Verify that the shared value contains the expected result */
        if (OPA_load_int(&shared_val) != -starting_val) {
            printf("\n    Unexpected result: %d expected: %d",
                   OPA_load_int(&shared_val), -starting_val);
            nerrors++;
        }       /* end if */
    }   /* end for */

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    if (nerrors) {
        puts("");
        TEST_ERROR;
    }

    /* end if */
    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("decr and test", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_decr_and_test() */


/*-------------------------------------------------------------------------
 * Function: test_simple_faa_fai_fad
 *
 * Purpose: Tests basic functionality of OPA_fetch_and_add_int,
 *          OPA_fetch_and_incr_int and OPA_fetch_and_decr_int with a
 *          single thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, April 23, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_faa_fai_fad(void)
{
    OPA_int_t a;
    int result, expected;

    TESTING("simple fetch and add/incr/decr functionality", 0);

    /* Store 0 in a */
    OPA_store_int(&a, 0);
    expected = 0;

    /* Add INT_MIN */
    result = OPA_fetch_and_add_int(&a, INT_MIN);
    if (result != expected)
        TEST_ERROR;
    expected += INT_MIN;

    /* Increment */
    result = OPA_fetch_and_incr_int(&a);
    if (result != expected)
        TEST_ERROR;
    expected++;

    /* Add INT_MAX */
    result = OPA_fetch_and_add_int(&a, INT_MAX);
    if (result != expected)
        TEST_ERROR;
    expected += INT_MAX;

    /* Decrement */
    result = OPA_fetch_and_decr_int(&a);
    if (result != expected)
        TEST_ERROR;
    expected--;

    /* Load the result, verify it is correct */
    if (OPA_load_int(&a) != INT_MIN + 1 + INT_MAX - 1)
        TEST_ERROR;

    /* Store 0 in a */
    OPA_store_int(&a, 0);
    expected = 0;

    /* Add INT_MAX */
    result = OPA_fetch_and_add_int(&a, INT_MAX);
    if (result != expected)
        TEST_ERROR;
    expected += INT_MAX;

    /* Decrement */
    result = OPA_fetch_and_decr_int(&a);
    if (result != expected)
        TEST_ERROR;
    expected--;

    /* Add INT_MIN */
    result = OPA_fetch_and_add_int(&a, INT_MIN);
    if (result != expected)
        TEST_ERROR;
    expected += INT_MIN;

    /* Increment */
    result = OPA_fetch_and_incr_int(&a);
    if (result != expected)
        TEST_ERROR;
    expected++;

    /* Load the result, verify it is correct */
    if (OPA_load_int(&a) != INT_MAX - 1 + INT_MIN + 1)
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_faa_fai_fad() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_faa_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_faa
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Thursday, April 23, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_faa_helper(void *_udata)
{
    add_t *udata = (add_t *) _udata;
    unsigned niter = ADD_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        /* Add the unique value to the shared value */
        (void) OPA_fetch_and_add_int(udata->shared_val, udata->unique_val);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_add_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_faa
 *
 * Purpose: Tests atomicity of OPA_fetch_and_add_int.  Launches nthreads
 *          threads, each of which repeatedly adds a unique number to a
 *          shared variable.  Does not test return value of
 *          OPA_fetch_and_add_int.  This is basically a copy of
 *          test_threaded_add.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, April 23, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_faa(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    add_t *thread_data = NULL;  /* User data structs for each thread */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("fetch and add", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (add_t *) calloc(nthreads, sizeof(add_t))))
        TEST_ERROR;

    /* Initialize thread data structs.  All the unique values must add up to
     * 0. */
    OPA_store_int(&shared_val, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].unique_val = i - (nthreads - 1) / 2
            - (!(nthreads % 2) && (i >= nthreads / 2))
            + (i == nthreads - 1);
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_faa_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_faa_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != ADD_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), ADD_EXPECTED));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("fetch and add", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_faa() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_faa_ret_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_faa_ret
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Thursday, April 23, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_faa_ret_helper(void *_udata)
{
    faa_ret_t *udata = (faa_ret_t *) _udata;
    int ret, prev = INT_MAX;
    unsigned niter = FAA_RET_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Add -1 to the shared value */
        ret = OPA_fetch_and_add_int(udata->shared_val, -1);

        /* Verify that the value returned is less than the previous return */
        if (ret >= prev) {
            printf("\n    Unexpected return: %d is not less than %d  ", ret, prev);
            (udata->nerrors)++;
        }

        /* end if */
        /* Check if the return value is 1 */
        if (ret == 1)
            (udata->n1)++;

        /* update prev */
        prev = ret;
    }   /* end for */

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_faa_ret_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_faa_ret
 *
 * Purpose: Tests atomicity of OPA_fetch_and_add_int.  Launches nthreads
 *          threads, each of which repeatedly adds -1 to a shared
 *          variable.  Verifies that the value returned is always
 *          decreasing, and that it returns 1 exactly once.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Thursday, April 23, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_faa_ret(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    faa_ret_t *thread_data = NULL;      /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    int nerrors = 0;            /* Number of errors */
    int n1 = 0;                 /* # of times faa returned 1 */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("fetch and add return values", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (faa_ret_t *) calloc(nthreads, sizeof(faa_ret_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, FAA_RET_NITER);
    for (i = 0; i < nthreads; i++)
        thread_data[i].shared_val = &shared_val;
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_faa_ret_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_faa_ret_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Count number of errors and number of times 1 was returned */
    for (i = 0; i < nthreads; i++) {
        nerrors += thread_data[i].nerrors;
        n1 += thread_data[i].n1;
    }   /* end for */

    /* Verify that no errors were reported */
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_fetch_and_add_int\n",
                             nerrors, nerrors == 1 ? "" : "s"));

    /* Verify that OPA_fetch_and_add_int returned 1 expactly once */
    if (n1 != 1)
        FAIL_OP_ERROR(printf("    OPA_fetch_and_add_int returned 1 %d times.  Expected: 1\n", n1));

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != FAA_RET_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), FAA_RET_EXPECTED));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("fetch and add return values", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_faa_ret() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_fai_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_fai_fad
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Friday, May 8, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_fai_helper(void *_shared_val)
{
    OPA_int_t *shared_val = (OPA_int_t *) _shared_val;
    unsigned niter = INCR_DECR_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        /* Add the unique value to the shared value */
        (void) OPA_fetch_and_incr_int(shared_val);

    /* Exit */
    pthread_exit(NULL);
}       /* end threaded_fai_helper() */


/*-------------------------------------------------------------------------
 * Function: threaded_fad_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_fai_fad
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Friday, May 8, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_fad_helper(void *_shared_val)
{
    OPA_int_t *shared_val = (OPA_int_t *) _shared_val;
    unsigned niter = INCR_DECR_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        /* Add the unique value to the shared value */
        (void) OPA_fetch_and_decr_int(shared_val);

    /* Exit */
    pthread_exit(NULL);
}       /* end threaded_fad_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_fai_fad
 *
 * Purpose: Tests atomicity of OPA_fetch_and_incr_int and
 *          OPA_fetch_and_decr_int.  Launches nthreads threads, each of
 *          which repeatedly either increments or decrements a shared
 *          variable.  Does not test return values.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Friday, May 8, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_fai_fad(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    /* Must use an odd number of threads */
    if (!(nthreads & 1))
        nthreads--;

    TESTING("fetch and incr/decr", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc(nthreads * sizeof(pthread_t))))
        TEST_ERROR;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Set the initial state of the shared value (0) */
    OPA_store_int(&shared_val, 0);

    /* Create the threads.  All the unique values must add up to 0. */
    for (i = 0; i < nthreads; i++) {
        if (i & 1) {
            if (pthread_create(&threads[i], &ptattr, threaded_fad_helper, (void *) &shared_val))
                TEST_ERROR;
        } else if (pthread_create(&threads[i], &ptattr, threaded_fai_helper, (void *) &shared_val))
            TEST_ERROR;
    }   /* end for */

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < nthreads; i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != INCR_DECR_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), INCR_DECR_EXPECTED));

    /* Free memory */
    free(threads);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("fetch and incr/decr", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_fai_fad() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_fai_ret_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_fai_ret
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Friday, June 5, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_fai_ret_helper(void *_udata)
{
    fai_ret_t *udata = (fai_ret_t *) _udata;
    int ret, prev = INT_MIN;
    unsigned niter = FAI_RET_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Add -1 to the shared value */
        ret = OPA_fetch_and_incr_int(udata->shared_val);

        /* Verify that the value returned is greater than the previous return */
        if (ret <= prev) {
            printf("\n    Unexpected return: %d is not greater than %d  ", ret, prev);
            (udata->nerrors)++;
        }

        /* end if */
        /* Check if the return value is -1 */
        if (ret == -1)
            (udata->nm1)++;

        /* update prev */
        prev = ret;
    }   /* end for */

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_fai_ret_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_fai_ret
 *
 * Purpose: Tests atomicity of OPA_fetch_and_incr_int.  Launches nthreads
 *          threads, each of which repeatedly adds -1 to a shared
 *          variable.  Verifies that the value returned is always
 *          decreasing, and that it returns 1 exactly once.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Friday, June 5, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_fai_ret(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    fai_ret_t *thread_data = NULL;      /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    int nerrors = 0;            /* Number of errors */
    int n1 = 0;                 /* # of times fai returned 1 */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("fetch and incr return values", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (fai_ret_t *) calloc(nthreads, sizeof(fai_ret_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, -FAI_RET_NITER);
    for (i = 0; i < nthreads; i++)
        thread_data[i].shared_val = &shared_val;
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_fai_ret_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_fai_ret_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Count number of errors and number of times 1 was returned */
    for (i = 0; i < nthreads; i++) {
        nerrors += thread_data[i].nerrors;
        n1 += thread_data[i].nm1;
    }   /* end for */

    /* Verify that no errors were reported */
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_fetch_and_incr_int\n",
                             nerrors, nerrors == 1 ? "" : "s"));

    /* Verify that OPA_fetch_and_add_int returned 1 expactly once */
    if (n1 != 1)
        FAIL_OP_ERROR(printf("    OPA_fetch_and_add_int returned -1 %d times.  Expected: 1\n", n1));

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != FAI_RET_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), FAI_RET_EXPECTED));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("fetch and incr return values", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_fai_ret() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_fad_ret_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_fad_ret
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Friday, June 5, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_fad_ret_helper(void *_udata)
{
    faa_ret_t *udata = (faa_ret_t *) _udata;
    int ret, prev = INT_MAX;
    unsigned niter = FAA_RET_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Add -1 to the shared value */
        ret = OPA_fetch_and_decr_int(udata->shared_val);

        /* Verify that the value returned is less than the previous return */
        if (ret >= prev) {
            printf("\n    Unexpected return: %d is not less than %d  ", ret, prev);
            (udata->nerrors)++;
        }

        /* end if */
        /* Check if the return value is 1 */
        if (ret == 1)
            (udata->n1)++;

        /* update prev */
        prev = ret;
    }   /* end for */

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_fad_ret_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_fad_ret
 *
 * Purpose: Tests atomicity of OPA_fetch_and_decr_int.  Launches nthreads
 *          threads, each of which repeatedly adds -1 to a shared
 *          variable.  Verifies that the value returned is always
 *          decreasing, and that it returns 1 exactly once.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Friday, June 5, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_fad_ret(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    faa_ret_t *thread_data = NULL;      /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    int nerrors = 0;            /* Number of errors */
    int n1 = 0;                 /* # of times faa returned 1 */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("fetch and decr return values", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (faa_ret_t *) calloc(nthreads, sizeof(faa_ret_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, FAA_RET_NITER);
    for (i = 0; i < nthreads; i++)
        thread_data[i].shared_val = &shared_val;
    thread_data[nthreads - 1].master_thread = 1;


    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_fad_ret_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_fad_ret_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Count number of errors and number of times 1 was returned */
    for (i = 0; i < nthreads; i++) {
        nerrors += thread_data[i].nerrors;
        n1 += thread_data[i].n1;
    }   /* end for */

    /* Verify that no errors were reported */
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_fetch_and_decr_int\n",
                             nerrors, nerrors == 1 ? "" : "s"));

    /* Verify that OPA_fetch_and_add_int returned 1 expactly once */
    if (n1 != 1)
        FAIL_OP_ERROR(printf("    OPA_fetch_and_add_int returned 1 %d times.  Expected: 1\n", n1));

    /* Verify that the shared value contains the expected result (0) */
    if (OPA_load_int(&shared_val) != FAA_RET_EXPECTED)
        FAIL_OP_ERROR(printf("    Unexpected result: %d expected: %d\n",
                             OPA_load_int(&shared_val), FAA_RET_EXPECTED));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("fetch and decr return values", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_fad_ret() */


/*-------------------------------------------------------------------------
 * Function: test_simple_cas_int
 *
 * Purpose: Tests basic functionality of OPA_cas_int with a single thread.
 *          Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_cas_int(void)
{
    OPA_int_t a;

    TESTING("simple integer compare-and-swap functionality", 0);

    /* Store 0 in a */
    OPA_store_int(&a, 0);

    /* Compare and swap multiple times, verify return value and final result */
    if (0 != OPA_cas_int(&a, 1, INT_MAX))
        TEST_ERROR;
    if (0 != OPA_cas_int(&a, 0, INT_MAX))
        TEST_ERROR;
    if (INT_MAX != OPA_cas_int(&a, INT_MAX, INT_MIN))
        TEST_ERROR;
    if (INT_MIN != OPA_cas_int(&a, INT_MAX, 1))
        TEST_ERROR;
    if (INT_MIN != OPA_cas_int(&a, INT_MIN, 1))
        TEST_ERROR;
    if (1 != OPA_load_int(&a))
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_cas_int() */


/*-------------------------------------------------------------------------
 * Function: test_simple_cas_ptr
 *
 * Purpose: Tests basic functionality of OPA_cas_ptr with a single thread.
 *          Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_cas_ptr(void)
{
    OPA_ptr_t a;
    void *ptr1 = malloc(1);     /* Pointers to assign to a */
    void *ptr2 = malloc(1);
    void *ptr3 = malloc(1);
    void *ptr4 = malloc(1);

    TESTING("simple pointer compare-and-swap functionality", 0);

    /* Store ptr1 in a */
    OPA_store_ptr(&a, ptr1);

    /* Compare and swap multiple times, verify return value and final result */
    if (ptr1 != OPA_cas_ptr(&a, ptr2, ptr3))
        TEST_ERROR;
    if (ptr1 != OPA_cas_ptr(&a, ptr1, ptr3))
        TEST_ERROR;
    if (ptr3 != OPA_cas_ptr(&a, ptr3, ptr4))
        TEST_ERROR;
    if (ptr4 != OPA_cas_ptr(&a, ptr3, ptr2))
        TEST_ERROR;
    if (ptr4 != OPA_cas_ptr(&a, ptr4, ptr2))
        TEST_ERROR;
    if (ptr2 != OPA_load_ptr(&a))
        TEST_ERROR;

    if (ptr1)
        free(ptr1);
    if (ptr2)
        free(ptr2);
    if (ptr3)
        free(ptr3);
    if (ptr4)
        free(ptr4);

    PASSED();
    return 0;

  error:
    if (ptr1)
        free(ptr1);
    if (ptr2)
        free(ptr2);
    if (ptr3)
        free(ptr3);
    if (ptr4)
        free(ptr4);

    return 1;
}       /* end test_simple_cas_ptr() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_cas_int_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_cas_int
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_cas_int_helper(void *_udata)
{
    cas_int_t *udata = (cas_int_t *) _udata;
    int thread_id = udata->threadno;
    int next_id = (thread_id + 1) % num_threads[curr_test];
    unsigned niter = CAS_INT_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        if (OPA_cas_int(udata->shared_val, thread_id, next_id) == thread_id)
            udata->nsuccess++;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_cas_int_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_cas_int
 *
 * Purpose: Tests atomicity of OPA_cas_int.  Launches nthreads threads,
 *          each of which continually tries to compare-and-swap a shared
 *          value with its thread id (oldv) and thread id + 1 % n (newv).
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_cas_int(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    cas_int_t *thread_data = NULL;      /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("integer compare-and-swap", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (cas_int_t *) calloc(nthreads, sizeof(cas_int_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].threadno = i;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_cas_int_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_cas_int_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Verify that cas succeeded at least once */
    if (thread_data[0].nsuccess == 0)
        FAIL_OP_ERROR(printf("    Compare-and-swap never succeeded\n"));

    /* Verify that the number of successes does not increase with increasing
     * thread number, and also that the shared value's final value is consistent
     * with the numbers of successes */
    if (nthreads > 1)
        for (i = 1; i < nthreads; i++) {
            if (thread_data[i].nsuccess > thread_data[i - 1].nsuccess)
                FAIL_OP_ERROR(printf("    Thread %d succeeded more times than thread %d\n",
                                     i, i - 1));

            if ((thread_data[i].nsuccess < thread_data[i - 1].nsuccess)
                && (OPA_load_int(&shared_val) != i))
                FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));
        }       /* end for */
    if ((thread_data[0].nsuccess == thread_data[nthreads - 1].nsuccess)
        && (OPA_load_int(&shared_val) != 0))
        FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));

    /* Verify that the number of successes for the first threads is equal to or
     * one greater than the number of successes for the last thread.  Have
     * already determined that it is not less in the previous test. */
    if (thread_data[0].nsuccess > (thread_data[nthreads - 1].nsuccess + 1))
        FAIL_OP_ERROR(printf("    Thread 0 succeeded %d times more than thread %d\n",
                             thread_data[0].nsuccess - thread_data[nthreads - 1].nsuccess,
                             nthreads - 1));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("integer compare-and-swap", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_cas_int() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_cas_ptr_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_cas_ptr
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Monday, August 3, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_cas_ptr_helper(void *_udata)
{
    cas_ptr_t *udata = (cas_ptr_t *) _udata;
    int *thread_id = udata->threadno;
    int *next_id = thread_id + 1;
    unsigned niter = CAS_PTR_NITER;
    unsigned i;

    /* This is the equivalent of the "modulus" operation, but for pointers */
    if (next_id > udata->max_threadno)
        next_id = (int *) 0;

    /* Main loop */
    for (i = 0; i < niter; i++)
        if (OPA_cas_ptr(udata->shared_val, (void *) thread_id, (void *) next_id) ==
            (void *) thread_id)
            udata->nsuccess++;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_cas_ptr_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_cas_ptr
 *
 * Purpose: Tests atomicity of OPA_cas_ptr.  Launches nthreads threads,
 *          each of which continually tries to compare-and-swap a shared
 *          value with its thread id (oldv) and thread id + 1 % n (newv).
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Monday, August 3, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_cas_ptr(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    cas_ptr_t *thread_data = NULL;      /* User data structs for threads */
    OPA_ptr_t shared_val;       /* Integer shared between threads */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("pointer compare-and-swap", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (cas_ptr_t *) calloc(nthreads, sizeof(cas_ptr_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_ptr(&shared_val, (void *) 0);
    thread_data[0].shared_val = &shared_val;
    thread_data[0].threadno = (int *) 0;
    for (i = 1; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].threadno = (int *) 0 + i;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;
    for (i = 0; i < nthreads; i++)
        thread_data[i].max_threadno = thread_data[nthreads - 1].threadno;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_cas_ptr_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_cas_ptr_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Verify that cas succeeded at least once */
    if (thread_data[0].nsuccess == 0)
        FAIL_OP_ERROR(printf("    Compare-and-swap never succeeded\n"));

    /* Verify that the number of successes does not increase with increasing
     * thread number, and also that the shared value's final value is consistent
     * with the numbers of successes */
    if (nthreads > 1)
        for (i = 1; i < nthreads; i++) {
            if (thread_data[i].nsuccess > thread_data[i - 1].nsuccess)
                FAIL_OP_ERROR(printf("    Thread %d succeeded more times than thread %d\n",
                                     i, i - 1));

            if ((thread_data[i].nsuccess < thread_data[i - 1].nsuccess)
                && (OPA_load_ptr(&shared_val) != thread_data[i].threadno))
                FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));
        }       /* end for */
    if ((thread_data[0].nsuccess == thread_data[nthreads - 1].nsuccess)
        && (OPA_load_ptr(&shared_val) != (void *) 0))
        FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));

    /* Verify that the number of successes for the first threads is equal to or
     * one greater than the number of successes for the last thread.  Have
     * already determined that it is not less in the previous test. */
    if (thread_data[0].nsuccess > (thread_data[nthreads - 1].nsuccess + 1))
        FAIL_OP_ERROR(printf("    Thread 0 succeeded %d times more than thread %d\n",
                             thread_data[0].nsuccess - thread_data[nthreads - 1].nsuccess,
                             nthreads - 1));

    /* Free memory */
    free(threads);
    free(thread_data);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("pointer compare-and-swap", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_cas_ptr() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: grouped_cas_int_helper
 *
 * Purpose: Helper (thread) routine for test_grouped_cas_int
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, August 4, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *grouped_cas_int_helper(void *_udata)
{
    grouped_cas_int_t *udata = (grouped_cas_int_t *) _udata;
    int group_id = udata->groupno;
    int next_id = (group_id + 1) % udata->ngroups;
    unsigned niter = GROUPED_CAS_INT_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        if (OPA_cas_int(udata->shared_val, group_id, next_id) == group_id)
            udata->nsuccess++;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end grouped_cas_int_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_grouped_cas_int
 *
 * Purpose: Tests atomicity of OPA_cas_int.  Launches nthreads threads,
 *          subdivided into nthreads/4 or 2 groups, whichever is greater.
 *          Each thread continually tries to compare-and-swap a shared
 *          value with its group id (oldv) and group id + 1 % ngroups
 *          (newv).
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, August 4, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_grouped_cas_int(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    grouped_cas_int_t *thread_data = NULL;      /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    int ngroups;                /* Number of groups of threads */
    int threads_per_group;      /* Threads per group */
    int *group_success = NULL;  /* Number of successes for each group */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("grouped integer compare-and-swap", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (grouped_cas_int_t *) calloc(nthreads, sizeof(grouped_cas_int_t))))
        TEST_ERROR;

    /* Calculate number of groups and threads per group */
    if ((nthreads / GROUPED_CAS_INT_TPG) >= 2)
        threads_per_group = GROUPED_CAS_INT_TPG;
    else
        threads_per_group = (nthreads + 1) / 2;
    ngroups = (nthreads + threads_per_group - 1) / threads_per_group;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].groupno = i / threads_per_group;
        thread_data[i].ngroups = ngroups;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, grouped_cas_int_helper, &thread_data[i]))
            TEST_ERROR;
    (void) grouped_cas_int_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Calculate the total number of successes for each group */
    if (NULL == (group_success = (int *) calloc(ngroups, sizeof(int))))
        TEST_ERROR;
    for (i = 0; i < nthreads; i++)
        group_success[thread_data[i].groupno] += thread_data[i].nsuccess;

    /* Verify that cas succeeded at least once */
    if (group_success[0] == 0)
        FAIL_OP_ERROR(printf("    Compare-and-swap never succeeded\n"));

    /* Verify that the number of successes does not increase with increasing
     * group number, and also that the shared value's final value is consistent
     * with the numbers of successes */
    if (nthreads > 1)
        for (i = 1; i < ngroups; i++) {
            if (group_success[i] > group_success[i - 1])
                FAIL_OP_ERROR(printf("    Group %d succeeded more times than group %d\n",
                                     i, i - 1));

            if ((group_success[i] < group_success[i - 1])
                && (OPA_load_int(&shared_val) != i))
                FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));
        }       /* end for */
    if ((group_success[0] == group_success[ngroups - 1])
        && (OPA_load_int(&shared_val) != 0))
        FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));

    /* Verify that the number of successes for the first threads is equal to or
     * one greater than the number of successes for the last thread.  Have
     * already determined that it is not less in the previous test. */
    if (group_success[0] > (group_success[ngroups - 1] + 1))
        FAIL_OP_ERROR(printf("    Group 0 succeeded %d times more than group %d\n",
                             thread_data[0].nsuccess - thread_data[nthreads - 1].nsuccess,
                             nthreads - 1));

    /* Free memory */
    free(threads);
    free(thread_data);
    free(group_success);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("grouped integer compare-and-swap", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    if (group_success)
        free(group_success);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_grouped_cas_int() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: grouped_cas_ptr_helper
 *
 * Purpose: Helper (thread) routine for test_grouped_cas_ptr
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Monday, August 10, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *grouped_cas_ptr_helper(void *_udata)
{
    grouped_cas_ptr_t *udata = (grouped_cas_ptr_t *) _udata;
    int *group_id = udata->groupno;
    int *next_id = group_id + 1;
    unsigned niter = GROUPED_CAS_PTR_NITER;
    unsigned i;

    /* This is the equivalent of the "modulus" operation, but for pointers */
    if (next_id > udata->max_groupno)
        next_id = (int *) 0;

    /* Main loop */
    for (i = 0; i < niter; i++)
        if (OPA_cas_ptr(udata->shared_val, (void *) group_id, (void *) next_id) ==
            (void *) group_id)
            udata->nsuccess++;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end grouped_cas_ptr_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_grouped_cas_ptr
 *
 * Purpose: Tests atomicity of OPA_cas_ptr.  Launches nthreads threads,
 *          subdivided into nthreads/4 or 2 groups, whichever is greater.
 *          Each thread continually tries to compare-and-swap a shared
 *          value with its group id (oldv) and group id + 1 % ngroups
 *          (newv).
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Monday, August 10, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_grouped_cas_ptr(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    grouped_cas_ptr_t *thread_data = NULL;      /* User data structs for threads */
    OPA_ptr_t shared_val;       /* Integer shared between threads */
    int ngroups;                /* Number of groups of threads */
    int threads_per_group;      /* Threads per group */
    int *group_success = NULL;  /* Number of successes for each group */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("grouped pointer compare-and-swap", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (grouped_cas_ptr_t *) calloc(nthreads, sizeof(grouped_cas_ptr_t))))
        TEST_ERROR;

    /* Calculate number of groups and threads per group */
    if ((nthreads / GROUPED_CAS_PTR_TPG) >= 2)
        threads_per_group = GROUPED_CAS_PTR_TPG;
    else
        threads_per_group = (nthreads + 1) / 2;
    ngroups = (nthreads + threads_per_group - 1) / threads_per_group;

    /* Initialize thread data structs */
    OPA_store_ptr(&shared_val, (void *) 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].groupno = (int *) 0 + (i / threads_per_group);
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;
    for (i = 0; i < nthreads; i++)
        thread_data[i].max_groupno = thread_data[nthreads - 1].groupno;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, grouped_cas_ptr_helper, &thread_data[i]))
            TEST_ERROR;
    (void) grouped_cas_ptr_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Calculate the total number of successes for each group */
    if (NULL == (group_success = (int *) calloc(ngroups, sizeof(int))))
        TEST_ERROR;
    for (i = 0; i < nthreads; i++)
        group_success[(int) (thread_data[i].groupno - (int *) 0)] += thread_data[i].nsuccess;

    /* Verify that cas succeeded at least once */
    if (group_success[0] == 0)
        FAIL_OP_ERROR(printf("    Compare-and-swap never succeeded\n"));

    /* Verify that the number of successes does not increase with increasing
     * group number, and also that the shared value's final value is consistent
     * with the numbers of successes */
    if (nthreads > 1)
        for (i = 1; i < ngroups; i++) {
            if (group_success[i] > group_success[i - 1])
                FAIL_OP_ERROR(printf("    Group %d succeeded more times than group %d\n",
                                     i, i - 1));

            if ((group_success[i] < group_success[i - 1])
                && (OPA_load_ptr(&shared_val) != ((int *) 0) + i))
                FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));
        }       /* end for */
    if ((group_success[0] == group_success[ngroups - 1])
        && (OPA_load_ptr(&shared_val) != (int *) 0))
        FAIL_OP_ERROR(printf("    Number of successes is inconsistent\n"));

    /* Verify that the number of successes for the first threads is equal to or
     * one greater than the number of successes for the last thread.  Have
     * already determined that it is not less in the previous test. */
    if (group_success[0] > (group_success[ngroups - 1] + 1))
        FAIL_OP_ERROR(printf("    Group 0 succeeded %d times more than group %d\n",
                             thread_data[0].nsuccess - thread_data[nthreads - 1].nsuccess,
                             nthreads - 1));

    /* Free memory */
    free(threads);
    free(thread_data);
    free(group_success);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("grouped pointer compare-and-swap", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    if (group_success)
        free(group_success);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_grouped_cas_ptr() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_cas_int_fairness_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_cas_int_fairness
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_cas_int_fairness_helper(void *_udata)
{
    cas_int_fairness_t *udata = (cas_int_fairness_t *) _udata;
    int thread_id = udata->threadno;
    unsigned nsuccess = 0;
    unsigned min_success = CAS_INT_FAIRNESS_MIN_SUCCESS;
    unsigned max_iter = CAS_INT_FAIRNESS_MAX_ITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < max_iter; i++) {
        /* Attempt to set the shared value to the thread id */
        if (OPA_cas_int(udata->shared_val, -1, thread_id) == -1) {

            /* Increment success counter */
            nsuccess++;

            /* Check for completion */
            if (nsuccess == min_success)
                OPA_incr_int(udata->successful_threads);

            /* Reset shared value */
            if (OPA_cas_int(udata->shared_val, thread_id, -1) != thread_id)
                udata->nerrors++;
        }

        /* end if */
        /* Check for global completion */
        if (udata->nthreads == OPA_load_int(udata->successful_threads))
            break;

        /* Yield if we are not performing strict fairness checks.  This gives
         * other threads a chance and reduces the probability of a thread being
         * stopped for a long period of time after succeeding but before
         * resetting the shared value, preventing forward progress. */
#ifndef OPA_HAVE_STRICT_FAIRNESS_CHECKS
        OPA_TEST_YIELD();
#endif /* OPA_HAVE_STRICT_FAIRNESS_CHECKS */
    }   /* end for */

    /* Check if the loop was terminated due to hitting the MAX_ITER barrier,
     * throw a warning if it was */
    if (i == max_iter)
        udata->terminated = 1;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_cas_int_fairness_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_cas_int_fairness
 *
 * Purpose: Tests fairness of OPA_cas_int.  Launches nthreads threads,
 *          each of which continually tries to compare-and-swap a shared
 *          value with -1 (oldv) and thread id(newv), then sets it back to
 *          -1.  Each thread continues until all threads have succeeded
 *          CAS_INT_FAIRNESS_MIN_SUCCESS times.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, August 11, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_cas_int_fairness(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    cas_int_fairness_t *thread_data = NULL;     /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    OPA_int_t successful_threads;       /* Number of threads that have succeeded */
    int succeeded = 0;          /* Whether all threads succeeded */
    int terminated = 0;         /* Number of threads that were terminated */
    int nerrors = 0;            /* Number of errors */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("integer compare-and-swap fairness", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (cas_int_fairness_t *) calloc(nthreads, sizeof(cas_int_fairness_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, -1);
    OPA_store_int(&successful_threads, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].threadno = i;
        thread_data[i].nthreads = (int) nthreads;
        thread_data[i].successful_threads = &successful_threads;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_cas_int_fairness_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_cas_int_fairness_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Check if any errors were reported by the threads */
    for (i = 0; i < nthreads; i++)
        nerrors += thread_data[i].nerrors;
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_cas_int\n",
                             nerrors, nerrors == 1 ? "" : "s"));

    /* Verify that all threads succeeded */
    if (OPA_load_int(&successful_threads) == (int) nthreads)
        succeeded = 1;

    /* Check if any threads were terminated before succeeding */
    for (i = 0; i < nthreads; i++)
        terminated += thread_data[i].terminated;

    /* If no threads were terminated, but not all succeeded, something is very
     * wrong... */
    if (!succeeded && !terminated)
        FAIL_OP_ERROR(printf("    Not all threads succeeded, but none were terminated\n"));

    /* Otherwise, throw a warning if any threads were terminated */
    if (terminated) {
        WARNING();
        if (succeeded)
            printf
                ("    %d thread%s were terminated before succeeding: there may be an issue with\n    fairness\n",
                 terminated, terminated == 1 ? "" : "s");
        else {
            i = nthreads - OPA_load_int(&successful_threads);
            printf("    %d thread%s did not succeed: there may be an issue with fairness\n",
                   i, i == 1 ? "" : "s");
        }       /* end else */
    }

    /* end if */
    /* Free memory */
    free(threads);
    free(thread_data);

    if (!terminated)
        PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("integer compare-and-swap fairness", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_cas_int_fairness() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_cas_ptr_fairness_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_cas_ptr_fairness
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_cas_ptr_fairness_helper(void *_udata)
{
    cas_ptr_fairness_t *udata = (cas_ptr_fairness_t *) _udata;
    int *thread_id = udata->threadno;
    unsigned nsuccess = 0;
    unsigned min_success = CAS_PTR_FAIRNESS_MIN_SUCCESS;
    unsigned max_iter = CAS_PTR_FAIRNESS_MAX_ITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < max_iter; i++) {
        /* Attempt to set the shared value to the thread id */
        if (OPA_cas_ptr(udata->shared_val, (void *) 0, thread_id) == (void *) 0) {

            /* Increment success counter */
            nsuccess++;

            /* Check for completion */
            if (nsuccess == min_success)
                OPA_incr_int(udata->successful_threads);

            /* Reset shared value */
            if (OPA_cas_ptr(udata->shared_val, thread_id, (void *) 0) != (void *) thread_id)
                udata->nerrors++;
        }

        /* end if */
        /* Check for global completion */
        if (udata->nthreads == OPA_load_int(udata->successful_threads))
            break;

        /* Yield if we are not performing strict fairness checks.  This gives
         * other threads a chance and reduces the probability of a thread being
         * stopped for a long period of time after succeeding but before
         * resetting the shared value, preventing forward progress. */
#ifndef OPA_HAVE_STRICT_FAIRNESS_CHECKS
        OPA_TEST_YIELD();
#endif /* OPA_HAVE_STRICT_FAIRNESS_CHECKS */
    }   /* end for */

    /* Check if the loop was terminated due to hitting the MAX_ITER barrier,
     * throw a warning if it was */
    if (i == max_iter)
        udata->terminated = 1;

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_cas_ptr_fairness_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_cas_ptr_fairness
 *
 * Purpose: Tests fairness of OPA_cas_int.  Launches nthreads threads,
 *          each of which continually tries to compare-and-swap a shared
 *          value with -1 (oldv) and thread id(newv), then sets it back to
 *          -1.  Each thread continues until all threads have succeeded
 *          CAS_INT_FAIRNESS_MIN_SUCCESS times.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, August 11, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_cas_ptr_fairness(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    cas_ptr_fairness_t *thread_data = NULL;     /* User data structs for threads */
    OPA_ptr_t shared_val;       /* Integer shared between threads */
    OPA_int_t successful_threads;       /* Number of threads that have succeeded */
    int succeeded = 0;          /* Whether all threads succeeded */
    int terminated = 0;         /* Number of threads that were terminated */
    int nerrors = 0;            /* Number of errors */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("pointer compare-and-swap fairness", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (cas_ptr_fairness_t *) calloc(nthreads, sizeof(cas_ptr_fairness_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_ptr(&shared_val, (void *) 0);
    OPA_store_int(&successful_threads, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].threadno = (void *) 0 + i + 1;
        thread_data[i].nthreads = (int) nthreads;
        thread_data[i].successful_threads = &successful_threads;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_cas_ptr_fairness_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_cas_ptr_fairness_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Check if any errors were reported by the threads */
    for (i = 0; i < nthreads; i++)
        nerrors += thread_data[i].nerrors;
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d unexpected return%s from OPA_cas_ptr\n",
                             nerrors, nerrors == 1 ? "" : "s"));

    /* Verify that all threads succeeded */
    if (OPA_load_int(&successful_threads) == (int) nthreads)
        succeeded = 1;

    /* Check if any threads were terminated before succeeding */
    for (i = 0; i < nthreads; i++)
        terminated += thread_data[i].terminated;

    /* If no threads were terminated, but not all succeeded, something is very
     * wrong... */
    if (!succeeded && !terminated)
        FAIL_OP_ERROR(printf("    Not all threads succeeded, but none were terminated\n"));

    /* Otherwise, throw a warning if any threads were terminated */
    if (terminated) {
        WARNING();
        if (succeeded)
            printf
                ("    %d thread%s were terminated before succeeding: there may be an issue with\n    fairness\n",
                 terminated, terminated == 1 ? "" : "s");
        else {
            i = nthreads - OPA_load_int(&successful_threads);
            printf("    %d thread%s did not succeed: there may be an issue with fairness\n",
                   i, i == 1 ? "" : "s");
        }       /* end else */
    }

    /* end if */
    /* Free memory */
    free(threads);
    free(thread_data);

    if (!terminated)
        PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("pointer compare-and-swap fairness", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_cas_ptr_fairness() */


/*-------------------------------------------------------------------------
 * Function: test_simple_swap_int
 *
 * Purpose: Tests basic functionality of OPA_swap_int with a single
 *          thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Wednesday, March 24, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_swap_int(void)
{
    OPA_int_t a;

    TESTING("simple integer swap functionality", 0);

    /* Store 0 in a */
    OPA_store_int(&a, 0);

    /* Compare and swap multiple times, verify return value and final result */
    if (0 != OPA_swap_int(&a, INT_MAX))
        TEST_ERROR;
    if (INT_MAX != OPA_swap_int(&a, INT_MIN))
        TEST_ERROR;
    if (INT_MIN != OPA_swap_int(&a, 1))
        TEST_ERROR;
    if (1 != OPA_load_int(&a))
        TEST_ERROR;

    PASSED();
    return 0;

  error:
    return 1;
}       /* end test_simple_swap_int() */


/*-------------------------------------------------------------------------
 * Function: test_simple_swap_ptr
 *
 * Purpose: Tests basic functionality of OPA_swap_ptr with a single
 *          thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Wednesday, March 24, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_swap_ptr(void)
{
    OPA_ptr_t a;
    void *ptr1 = malloc(1);     /* Pointers to assign to a */
    void *ptr2 = malloc(1);
    void *ptr3 = malloc(1);
    void *ptr4 = malloc(1);

    TESTING("simple pointer swap functionality", 0);

    /* Store ptr1 in a */
    OPA_store_ptr(&a, ptr1);

    /* Compare and swap multiple times, verify return value and final result */
    if (ptr1 != OPA_swap_ptr(&a, ptr3))
        TEST_ERROR;
    if (ptr3 != OPA_swap_ptr(&a, ptr4))
        TEST_ERROR;
    if (ptr4 != OPA_swap_ptr(&a, ptr2))
        TEST_ERROR;
    if (ptr2 != OPA_load_ptr(&a))
        TEST_ERROR;

    if (ptr1)
        free(ptr1);
    if (ptr2)
        free(ptr2);
    if (ptr3)
        free(ptr3);
    if (ptr4)
        free(ptr4);

    PASSED();
    return 0;

  error:
    if (ptr1)
        free(ptr1);
    if (ptr2)
        free(ptr2);
    if (ptr3)
        free(ptr3);
    if (ptr4)
        free(ptr4);

    return 1;
}       /* end test_simple_swap_ptr() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_swap_int_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_swap_int
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Wednesday, March 24, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_swap_int_helper(void *_udata)
{
    swap_int_t *udata = (swap_int_t *) _udata;
    unsigned niter = SWAP_INT_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        udata->local_val = OPA_swap_int(udata->shared_val, udata->local_val);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_swap_int_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_swap_int
 *
 * Purpose: Tests atomicity of OPA_swap_int.  Launches nthreads threads,
 *          each of which continually swaps a shared value with a local
 *          variable whose initial value is the thread id.  Afterwards,
 *          Verifies that each thread id is present exactly once in all of
 *          the thread local values, and the shared value.  The initial
 *          state of the shared value must also be present exactly once in
 *          these places.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_swap_int(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    swap_int_t *thread_data = NULL;     /* User data structs for threads */
    OPA_int_t shared_val;       /* Integer shared between threads */
    unsigned *vals = NULL;      /* Local values found */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("integer swap", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (swap_int_t *) calloc(nthreads, sizeof(swap_int_t))))
        TEST_ERROR;

    /* Initialize thread data structs */
    OPA_store_int(&shared_val, nthreads);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].local_val = i;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_swap_int_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_swap_int_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Allocate the array of local values found.  These number 0 to nthreads
     * (1 for each thread id, and nthreads was the original value) */
    if (NULL == (vals = (unsigned *) calloc(nthreads + 1, sizeof(unsigned))))
        TEST_ERROR;

    /* Loop over all local values and the shared value, and total up the number
     * of times each value was found */
    for (i = 0; i < nthreads; i++) {
        /* Verify that the value is in range */
        if ((thread_data[i].local_val < 0)
            || (thread_data[i].local_val > nthreads))
            FAIL_OP_ERROR(printf("    Local value for thread %u is out of range: %d\n",
                                 i, thread_data[i].local_val));

        /* Increment the number of times local_val was encountered */
        vals[thread_data[i].local_val]++;
    }   /* end for */
    if ((OPA_load_int(&shared_val) < 0)
        || (OPA_load_int(&shared_val) > nthreads))
        FAIL_OP_ERROR(printf("    Shared value is out of range: %d\n", OPA_load_int(&shared_val)));
    vals[OPA_load_int(&shared_val)]++;

    /* Verify that each possible value was encountered exactly once */
    for (i = 0; i < nthreads; i++)
        if (vals[i] != 1)
            FAIL_OP_ERROR(printf("    Value %u was encountered %u times.  Expected: 1\n",
                                 i, vals[i]));

    /* Free memory */
    free(threads);
    free(thread_data);
    free(vals);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("integer swap", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    if (vals)
        free(vals);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_swap_int() */


#if defined(OPA_HAVE_PTHREAD_H)
/*-------------------------------------------------------------------------
 * Function: threaded_swap_ptr_helper
 *
 * Purpose: Helper (thread) routine for test_threaded_swap_ptr
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Wednesday, March 24, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_swap_ptr_helper(void *_udata)
{
    swap_ptr_t *udata = (swap_ptr_t *) _udata;
    unsigned niter = SWAP_PTR_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++)
        udata->local_val = OPA_swap_ptr(udata->shared_val, udata->local_val);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_swap_ptr_helper() */
#endif /* OPA_HAVE_PTHREAD_H */


/*-------------------------------------------------------------------------
 * Function: test_threaded_swap_ptr
 *
 * Purpose: Tests atomicity of OPA_swap_ptr.  Launches nthreads threads,
 *          each of which continually swaps a shared value with a local
 *          variable whose initial value is the thread id.  Afterwards,
 *          Verifies that each thread id is present exactly once in all of
 *          the thread local values, and the shared value.  The initial
 *          state of the shared value must also be present exactly once in
 *          these places.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 9, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_swap_ptr(void)
{
#if defined(OPA_HAVE_PTHREAD_H)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    swap_ptr_t *thread_data = NULL;     /* User data structs for threads */
    OPA_ptr_t shared_val;       /* Pointer shared between threads */
    unsigned *vals = NULL;      /* Local values found */
    unsigned nthreads = num_threads[curr_test];
    unsigned i;

    TESTING("pointer swap", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (swap_ptr_t *) calloc(nthreads, sizeof(swap_ptr_t))))
        TEST_ERROR;

    /* Initialize thread data structs.  Each local_val points to an element in
     * thread_data, or NULL (original value) */
    OPA_store_ptr(&shared_val, NULL);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].shared_val = &shared_val;
        thread_data[i].local_val = &(thread_data[i]);
        thread_data[i].threadno = i;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_create(&threads[i], &ptattr, threaded_swap_ptr_helper, &thread_data[i]))
            TEST_ERROR;
    (void) threaded_swap_ptr_helper(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Allocate the array of local values found.  These number 0 to nthreads
     * (1 for each thread id, and nthreads was the original value) */
    if (NULL == (vals = (unsigned *) calloc(nthreads + 1, sizeof(unsigned))))
        TEST_ERROR;

    /* Loop over all local values and the shared value, and total up the number
     * of times each value was found */
    for (i = 0; i < nthreads; i++) {
        /* Increment the number of times local_val was encountered.  Use the
         * threadno of the target thread_data element as the index into vals.
         * If local_val is NULL, use nthreads as the index. */
        if (thread_data[i].local_val)
            vals[((swap_ptr_t *) thread_data[i].local_val)->threadno]++;
        else
            vals[nthreads]++;
    }   /* end for */
    if (OPA_load_ptr(&shared_val))
        vals[((swap_ptr_t *) OPA_load_ptr(&shared_val))->threadno]++;
    else
        vals[nthreads]++;

    /* Verify that each possible value was encountered exactly once */
    for (i = 0; i < nthreads; i++)
        if (vals[i] != 1)
            FAIL_OP_ERROR(printf("    Value %d was encountered %d times.  Expected: 1\n",
                                 i, vals[i]));

    /* Free memory */
    free(threads);
    free(thread_data);
    free(vals);

    PASSED();

#else /* OPA_HAVE_PTHREAD_H */
    TESTING("pointer swap", 0);
    SKIPPED();
    puts("    pthread.h not available");
#endif /* OPA_HAVE_PTHREAD_H */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H)
  error:
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    if (vals)
        free(vals);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H */
}       /* end test_threaded_swap_ptr() */


/*-------------------------------------------------------------------------
 * Function: test_simple_llsc_int
 *
 * Purpose: Tests basic functionality of OPA_LL_int and OPA_SC_int with a
 *          single thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 15, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_llsc_int(void)
{
#if defined(OPA_LL_SC_SUPPORTED)
    OPA_int_t a;
    int warned = 0;             /* Whether a warning message has been issued */

    TESTING("simple integer load-linked/store-conditional functionality", 0);

    /* Store 0 in a */
    OPA_store_int(&a, 0);

    /* Load linked a */
    if (0 != OPA_LL_int(&a))
        TEST_ERROR;

    /* Store conditional INT_MAX in a - should succeed */
    if (!OPA_SC_int(&a, INT_MAX) && !warned) {
        WARNING();
        printf("    Unexpected failure of OPA_SC_int.  LL/SC may be weak\n");
        warned = 1;
        OPA_store_int(&a, INT_MAX);
    }

    /* end if */
    /* Load linked a */
    if (INT_MAX != OPA_LL_int(&a))
        TEST_ERROR;

    /* Store conditional INT_MIN in a - should succeed */
    if (!OPA_SC_int(&a, INT_MIN) && !warned) {
        WARNING();
        printf("    Unexpected failure of OPA_SC_int.  LL/SC may be weak\n");
        warned = 1;
        OPA_store_int(&a, INT_MIN);
    }

    /* end if */
    /* Load linked a */
    if (INT_MIN != OPA_LL_int(&a))
        TEST_ERROR;

    /* Store conditional 0 in a - should succeed */
    if (!OPA_SC_int(&a, 0) && !warned) {
        WARNING();
        printf("    Unexpected failure of OPA_SC_int.  LL/SC may be weak\n");
        warned = 1;
        OPA_store_int(&a, 0);
    }

    /* end if */
    /* Make sure a contains 0 */
    if (0 != OPA_load_int(&a))
        TEST_ERROR;

    if (!warned)
        PASSED();

#else /* OPA_LL_SC_SUPPORTED */
    TESTING("simple integer load-linked/store-conditional functionality", 0);
    SKIPPED();
    puts("    LL/SC not available");
#endif /* OPA_LL_SC_SUPPORTED */

    return 0;

#if defined(OPA_LL_SC_SUPPORTED)
  error:
    return 1;
#endif /* OPA_LL_SC_SUPPORTED */
}       /* end test_simple_llsc_int() */


/*-------------------------------------------------------------------------
 * Function: test_simple_llsc_ptr
 *
 * Purpose: Tests basic functionality of OPA_LL_ptr and OPA_SC_ptr with a
 *          single thread.  Does not test atomicity of operations.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 15, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_simple_llsc_ptr(void)
{
#if defined(OPA_LL_SC_SUPPORTED)
    OPA_ptr_t a;
    void *ptr1 = malloc(1);     /* Pointers to assign to a */
    void *ptr2 = malloc(1);
    void *ptr3 = malloc(1);
    int warned = 0;             /* Whether a warning message has been issued */

    TESTING("simple pointer load-linked/store-conditional functionality", 0);

    /* Store ptr1 in a */
    OPA_store_ptr(&a, ptr1);

    /* Load linked a */
    if (ptr1 != OPA_LL_ptr(&a))
        TEST_ERROR;

    /* Store conditional ptr2 in a - should succeed */
    if (!OPA_SC_ptr(&a, ptr2) && !warned) {
        WARNING();
        printf("    Unexpected failure of OPA_SC_ptr.  LL/SC may be weak\n");
        warned = 1;
        OPA_store_ptr(&a, ptr2);
    }

    /* end if */
    /* Load linked a */
    if (ptr2 != OPA_LL_ptr(&a))
        TEST_ERROR;

    /* Store conditional ptr3 in a - should succeed */
    if (!OPA_SC_ptr(&a, ptr3) && !warned) {
        WARNING();
        printf("    Unexpected failure of OPA_SC_ptr.  LL/SC may be weak\n");
        warned = 1;
        OPA_store_ptr(&a, ptr3);
    }

    /* end if */
    /* Load linked a */
    if (ptr3 != OPA_LL_ptr(&a))
        TEST_ERROR;

    /* Store conditional ptr1 in a - should succeed */
    if (!OPA_SC_ptr(&a, ptr1) && !warned) {
        WARNING();
        printf("    Unexpected failure of OPA_SC_ptr.  LL/SC may be weak\n");
        warned = 1;
        OPA_store_ptr(&a, ptr1);
    }

    /* end if */
    /* Make sure a contains ptr1 */
    if (ptr1 != OPA_load_ptr(&a))
        TEST_ERROR;

    if (ptr1)
        free(ptr1);
    if (ptr2)
        free(ptr2);
    if (ptr3)
        free(ptr3);

    if (!warned)
        PASSED();

#else /* OPA_LL_SC_SUPPORTED */
    TESTING("simple pointer load-linked/store-conditional functionality", 0);
    SKIPPED();
    puts("    LL/SC not available");
#endif /* OPA_LL_SC_SUPPORTED */

    return 0;

#if defined(OPA_LL_SC_SUPPORTED)
  error:
    if (ptr1)
        free(ptr1);
    if (ptr2)
        free(ptr2);
    if (ptr3)
        free(ptr3);

    return 1;
#endif /* OPA_LL_SC_SUPPORTED */
}       /* end test_simple_llsc_ptr() */


#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
/*-------------------------------------------------------------------------
 * Function: threaded_llsc_int_aba_helper_0
 *
 * Purpose: Helper (thread) routine 0 for test_threaded_llsc_int_aba
 *
 * Return: Number of errors
 *
 * Programmer: Neil Fortner
 *             Monday, June 21, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int threaded_llsc_int_aba_helper_0(llsc_int_aba_t * udata)
{
    unsigned niter = LLSC_PTR_ABA_NITER;
    int nerrors = 0;            /* Number of errors */
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Initialize the shared value to 0 */
        OPA_store_int(&udata->shared_val, 0);

        /* Load linked the shared value, and verify that it returns 0 */
        if (0 != OPA_LL_int(&udata->shared_val)) {
            OP_SUPPRESS(printf("    Unexpected return from OPA_LL_int\n"), nerrors, 20);
            nerrors++;
        }

        /* end if */
        /* Point 0 */
        OPA_store_int(&udata->pass_point_0, 1);

        /* Wait until thread 1 passes point 1 */
        while (!OPA_load_int(&udata->pass_point_1));

        /* Store conditional 1 to the shared value */
        if (OPA_SC_int(&udata->shared_val, 1)) {
            /* SC succeeded, make sure that the shared value was not changed by
             * thread 1 */
            if (OPA_load_int(&udata->change_val)) {
                OP_SUPPRESS(printf("    Unexpected success of OPA_SC_int\n"), nerrors, 20);
                nerrors++;
            } else
                udata->nunchanged_val++;

            /* Verify that the shared value contains 1 */
            if (1 != OPA_load_int(&udata->shared_val)) {
                OP_SUPPRESS(printf
                            ("    Unexpected return from OPA_load_int after OPA_SC_int success\n"),
                            nerrors, 20);
                nerrors++;
            }   /* end if */
        } else {
            /* SC failed, check if it was a false positive */
            if (!OPA_load_int(&udata->change_val)) {
                udata->false_positives++;
                udata->nunchanged_val++;
            }

            /* end if */
            /* Verify that the shared value contains 0 */
            if (0 != OPA_load_int(&udata->shared_val)) {
                OP_SUPPRESS(printf
                            ("    Unexpected return from OPA_load_int after OPA_SC_int failure\n"),
                            nerrors, 20);
                nerrors++;
            }   /* end if */
        }       /* end else */

        /* Reset pass_point_1 */
        OPA_store_int(&udata->pass_point_1, 0);

        /* Read/write barrier to make sure we are done before we allow thread 1
         * to continue */
        OPA_read_write_barrier();

        /* Point 2 */
        OPA_store_int(&udata->pass_point_2, 1);
    }   /* end for */

    /* Exit */
    return (nerrors);
}       /* end threaded_llsc_int_aba_helper_0() */


/*-------------------------------------------------------------------------
 * Function: threaded_llsc_int_aba_helper_1
 *
 * Purpose: Helper (thread) routine 1 for test_threaded_llsc_int_aba
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 22, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_llsc_int_aba_helper_1(void *_udata)
{
    llsc_int_aba_t *udata = (llsc_int_aba_t *) _udata;
    unsigned niter = LLSC_PTR_ABA_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Decide if we are changing the shared value in this iteration */
        OPA_store_int(&udata->change_val, rand() % 2);

        /* Wait until thread 0 passes point 0 */
        while (!OPA_load_int(&udata->pass_point_0));

        /* Reset pass_point_0 */
        OPA_store_int(&udata->pass_point_0, 0);

        if (OPA_load_int(&udata->change_val)) {
            /* Set the shared value to 1 then back to 0.  This is the "ABA" part
             * of this test */
            OPA_store_int(&udata->shared_val, 1);
            OPA_store_int(&udata->shared_val, 0);

            /* Write barrier to make sure the shared value was actually updated
             * before we mark point 1 as passed */
            OPA_write_barrier();
        }

        /* end if */
        /* Point 1 */
        OPA_store_int(&udata->pass_point_1, 1);

        /* Wait until thread 0 passes point 2 */
        while (!OPA_load_int(&udata->pass_point_2));

        /* Reset pass_point_2 */
        OPA_store_int(&udata->pass_point_2, 0);
    }   /* end for */

    /* Exit */
    pthread_exit(NULL);
}       /* end threaded_llsc_int_aba_helper_1() */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */


/*-------------------------------------------------------------------------
 * Function: test_threaded_llsc_int_aba
 *
 * Purpose: Tests functionality of OPA_LL_int and OPA_SC_int.  Launches 2
 *          threads, which manipulate a shared value in such a way that it
 *          is changed to a new value then back to the original value
 *          before the other thread can call OPA_SC_int.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 22, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_llsc_int_aba(void)
{
#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
    pthread_t thread;           /* Thread */
    pthread_attr_t ptattr;      /* Thread attributes */
    llsc_int_aba_t thread_data; /* User data struct for threads */
    int nerrors = 0;            /* Number of errors */
    unsigned i;

    TESTING("integer LL/SC ABA", 2);

    /* Initialize thread data struct */
    OPA_store_int(&thread_data.shared_val, 0);
    OPA_store_int(&thread_data.pass_point_0, 0);
    OPA_store_int(&thread_data.pass_point_1, 0);
    OPA_store_int(&thread_data.pass_point_2, 0);
    OPA_store_int(&thread_data.change_val, 0);
    thread_data.false_positives = 0;
    thread_data.nunchanged_val = 0;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    if (pthread_create(&thread, &ptattr, threaded_llsc_int_aba_helper_1, &thread_data))
        TEST_ERROR;
    nerrors = threaded_llsc_int_aba_helper_0(&thread_data);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join thread 1 */
    if (pthread_join(thread, NULL))
        TEST_ERROR;

    /* Check if thread 0 returned any errors */
    if (nerrors)
        TEST_ERROR;

    /* Print a warning if OPA_SC_int never succeeded, otherwise pass */
    if (thread_data.false_positives == thread_data.nunchanged_val) {
        WARNING();
        printf("    OPA_SC_int never succeeded.  LL/SC appears to be weak.\n");
    } else
        PASSED();

    /* Report the number of false positives */
    printf("    False positives: %d / %d\n", thread_data.false_positives,
           thread_data.nunchanged_val);

#else /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
    TESTING("integer LL/SC ABA", 0);
    SKIPPED();
#if !defined(OPA_HAVE_PTHREAD_H)
    puts("    pthread.h not available");
#else /* OPA_HAVE_PTHREAD_H */
    puts("    LL/SC not available");
#endif /* OPA_HAVE_PTHREAD_H */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
  error:
    printf("    False positives: %d / %d\n", thread_data.false_positives,
           thread_data.nunchanged_val);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
}       /* end test_threaded_llsc_int_aba() */


#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
/*-------------------------------------------------------------------------
 * Function: threaded_llsc_ptr_aba_helper_0
 *
 * Purpose: Helper (thread) routine 0 for test_threaded_llsc_ptr_aba
 *
 * Return: Number of errors
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 22, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int threaded_llsc_ptr_aba_helper_0(llsc_ptr_aba_t * udata)
{
    unsigned niter = LLSC_INT_ABA_NITER;
    int nerrors = 0;            /* Number of errors */
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Initialize the shared value to 0 */
        OPA_store_ptr(&udata->shared_val, (void *) 0);

        /* Load linked the shared value, and verify that it returns 0 */
        if ((void *) 0 != OPA_LL_ptr(&udata->shared_val)) {
            OP_SUPPRESS(printf("    Unexpected return from OPA_LL_ptr\n"), nerrors, 20);
            nerrors++;
        }

        /* end if */
        /* Point 0 */
        OPA_store_int(&udata->pass_point_0, 1);

        /* Wait until thread 1 passes point 1 */
        while (!OPA_load_int(&udata->pass_point_1));

        /* Store conditional 1 to the shared value */
        if (OPA_SC_ptr(&udata->shared_val, (void *) ((int *) 0 + 1))) {
            /* SC succeeded, make sure that the shared value was not changed by
             * thread 1 */
            if (OPA_load_int(&udata->change_val)) {
                OP_SUPPRESS(printf("    Unexpected success of OPA_SC_ptr\n"), nerrors, 20);
                nerrors++;
            } else
                udata->nunchanged_val++;

            /* Verify that the shared value contains 1 */
            if ((void *) ((int *) 0 + 1) != OPA_load_ptr(&udata->shared_val)) {
                OP_SUPPRESS(printf
                            ("    Unexpected return from OPA_load_ptr after OPA_SC_int success\n"),
                            nerrors, 20);
                nerrors++;
            }   /* end if */
        } else {
            /* SC failed, check if it was a false positive */
            if (!OPA_load_int(&udata->change_val)) {
                udata->false_positives++;
                udata->nunchanged_val++;
            }

            /* end if */
            /* Verify that the shared value contains 0 */
            if ((void *) 0 != OPA_load_ptr(&udata->shared_val)) {
                OP_SUPPRESS(printf
                            ("    Unexpected return from OPA_load_ptr after OPA_SC_int failure\n"),
                            nerrors, 20);
                nerrors++;
            }   /* end if */
        }       /* end else */

        /* Reset pass_point_1 */
        OPA_store_int(&udata->pass_point_1, 0);

        /* Read/write barrier to make sure we are done before we allow thread 1
         * to continue */
        OPA_read_write_barrier();

        /* Point 2 */
        OPA_store_int(&udata->pass_point_2, 1);
    }   /* end for */

    /* Exit */
    return (nerrors);
}       /* end threaded_llsc_ptr_aba_helper_0() */


/*-------------------------------------------------------------------------
 * Function: threaded_llsc_ptr_aba_helper_1
 *
 * Purpose: Helper (thread) routine 1 for test_threaded_llsc_ptr_aba
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 22, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_llsc_ptr_aba_helper_1(void *_udata)
{
    llsc_ptr_aba_t *udata = (llsc_ptr_aba_t *) _udata;
    unsigned niter = LLSC_INT_ABA_NITER;
    unsigned i;

    /* Main loop */
    for (i = 0; i < niter; i++) {
        /* Decide if we are changing the shared value in this iteration */
        OPA_store_int(&udata->change_val, rand() % 2);

        /* Wait until thread 0 passes point 0 */
        while (!OPA_load_int(&udata->pass_point_0));

        /* Reset pass_point_0 */
        OPA_store_int(&udata->pass_point_0, 0);

        if (OPA_load_int(&udata->change_val)) {
            /* Set the shared value to 1 then back to 0.  This is the "ABA" part
             * of this test */
            OPA_store_ptr(&udata->shared_val, (void *) ((int *) 0 + 1));
            OPA_store_ptr(&udata->shared_val, (void *) 0);

            /* Write barrier to make sure the shared value was actually updated
             * before we mark point 1 as passed */
            OPA_write_barrier();
        }

        /* end if */
        /* Point 1 */
        OPA_store_int(&udata->pass_point_1, 1);

        /* Wait until thread 0 passes point 2 */
        while (!OPA_load_int(&udata->pass_point_2));

        /* Reset pass_point_2 */
        OPA_store_int(&udata->pass_point_2, 0);
    }   /* end for */

    /* Exit */
    pthread_exit(NULL);
}       /* end threaded_llsc_ptr_aba_helper_1() */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */


/*-------------------------------------------------------------------------
 * Function: test_threaded_llsc_ptr_aba
 *
 * Purpose: Tests functionality of OPA_LL_ptr and OPA_SC_ptr.  Launches 2
 *          threads, which manipulate a shared value in such a way that it
 *          is changed to a new value then back to the original value
 *          before the other thread can call OPA_SC_ptr.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Tuesday, June 22, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_llsc_ptr_aba(void)
{
#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
    pthread_t thread;           /* Thread */
    pthread_attr_t ptattr;      /* Thread attributes */
    llsc_ptr_aba_t thread_data; /* User data struct for threads */
    int nerrors = 0;            /* Number of errors */
    unsigned i;

    TESTING("pointer LL/SC ABA", 2);

    /* Initialize thread data struct */
    OPA_store_ptr(&thread_data.shared_val, (void *) 0);
    OPA_store_int(&thread_data.pass_point_0, 0);
    OPA_store_int(&thread_data.pass_point_1, 0);
    OPA_store_int(&thread_data.pass_point_2, 0);
    OPA_store_int(&thread_data.change_val, 0);
    thread_data.false_positives = 0;
    thread_data.nunchanged_val = 0;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    if (pthread_create(&thread, &ptattr, threaded_llsc_ptr_aba_helper_1, &thread_data))
        TEST_ERROR;
    nerrors = threaded_llsc_ptr_aba_helper_0(&thread_data);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join thread 1 */
    if (pthread_join(thread, NULL))
        TEST_ERROR;

    /* Check if thread 0 returned any errors */
    if (nerrors)
        TEST_ERROR;

    /* Print a warning if OPA_SC_int never succeeded, otherwise pass */
    if (thread_data.false_positives == thread_data.nunchanged_val) {
        WARNING();
        printf("    OPA_SC_ptr never succeeded.  LL/SC appears to be weak.\n");
    } else
        PASSED();

    /* Report the number of false positives */
    printf("    False positives: %d / %d\n", thread_data.false_positives,
           thread_data.nunchanged_val);

#else /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
    TESTING("pointer LL/SC ABA", 0);
    SKIPPED();
#if !defined(OPA_HAVE_PTHREAD_H)
    puts("    pthread.h not available");
#else /* OPA_HAVE_PTHREAD_H */
    puts("    LL/SC not available");
#endif /* OPA_HAVE_PTHREAD_H */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
  error:
    printf("    False positives: %d / %d\n", thread_data.false_positives,
           thread_data.nunchanged_val);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
}       /* end test_threaded_llsc_ptr_aba() */


#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
/*-------------------------------------------------------------------------
 * Function: threaded_llsc_int_stack_push
 *
 * Purpose: "Push" routine for test_threaded_llsc_int_stack
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Wednesday, June 23, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_llsc_int_stack_push(void *_udata)
{
    llsc_int_stack_t *udata = (llsc_int_stack_t *) _udata;
    llsc_int_stack_obj_t *obj = udata->objs[udata->obj];
    int tmp_head;
    unsigned niter = LLSC_INT_STACK_NITER;
    unsigned i, j;

    /* Main loop */
    for (i = 0; i < niter && !OPA_load_int(udata->failed); i++) {
        /* Acquire object */
        j = 0;
        while (OPA_cas_int(&obj->on_stack, 0, 1)) {
            OPA_TEST_YIELD();
            if (++j == LLSC_INT_STACK_NLOOP)
                break;
        }       /* end while */

        /* Check if we failed to acquire the object */
        if (j == LLSC_INT_STACK_NLOOP)
            continue;

        /* Push object onto stack */
        do {
            tmp_head = OPA_load_int(udata->head);
            OPA_store_int(&obj->next, tmp_head);
            /* don't let the "on_stack" CAS or the "next" store pass the "head" CAS */
            OPA_write_barrier();
        } while (tmp_head != OPA_cas_int(udata->head, tmp_head, udata->obj));

        /* Increment the number of successful pushes */
        udata->nsuccess++;
    }   /* end for */

    /* don't allow npusher's decr to be perceived as happening before the pushes
     * are visible */
    OPA_write_barrier();

    /* Mark this thread as having completed */
    OPA_decr_int(udata->npushers);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_llsc_int_stack_push() */

/*-------------------------------------------------------------------------
 * Function: threaded_llsc_int_stack_pop
 *
 * Purpose: "Pop" routine for test_threaded_llsc_int_stack
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Wednesday, June 23, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_llsc_int_stack_pop(void *_udata)
{
    llsc_int_stack_t *udata = (llsc_int_stack_t *) _udata;
    llsc_int_stack_obj_t *obj = NULL;
    int tmp_obj;
    unsigned i;

    /* Main loop */
    do {
        /* Load-linked the object */
        if (-1 == (tmp_obj = OPA_LL_int(udata->head))) {
            OPA_TEST_YIELD();
            continue;
        }       /* end if */
        obj = udata->objs[tmp_obj];

        /* Get the next object */
        tmp_obj = OPA_load_int(&obj->next);

        /* Store conditional the "next" object as the new head */
        if (OPA_SC_int(udata->head, tmp_obj)) {
            /* ensure that the head data is perceived as stored before reading
             * the on_stack variable */
            OPA_read_write_barrier();

            /* Make sure this object was marked as on the stack */
            if (!OPA_load_int(&obj->on_stack)) {
                OP_SUPPRESS(printf("    Popped object was not on the stack\n"), udata->nerrors, 20);
                udata->nerrors++;

                if (udata->nerrors >= 1000000) {
                    /* Cause all threads to break out so the test fails in a
                     * reasonable amount of time */
                    OPA_store_int(udata->failed, 1);
                    break;
                }       /* end if */
            }

            /* end if */
            /* Mark the object as off the stack, release for pushing */
            OPA_store_int(&obj->on_stack, 0);

            /* Increment the number of successful pops */
            udata->nsuccess++;
        }

        /* end if */
        /* We could include a read_write_barrier here to enforce ordering
         * between pop attempt above and the npushers load below, but leaving it
         * out is safe because npushers starts at max and monotonically
         * decreases.  Worst case is a few extra iterations. */
    } while (OPA_load_int(udata->npushers) && !OPA_load_int(udata->failed));

    pthread_exit(NULL);
}       /* end threaded_llsc_int_stack_pop() */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */


/*-------------------------------------------------------------------------
 * Function: test_threaded_llsc_int_stack
 *
 * Purpose: Tests atomicity of OPA_LL_int and OPA_SC_int.  Launches
 *          nthreads threads, subdivided into nthreads/3 or 2 groups,
 *          whichever is greater.  One group of threads continually tries
 *          to pop objects off a shared stack using LL/SC, while the
 *          other groups all try to push objects onto the stack.  The
 *          algorithm of the pop routine is such that LL/SC must be able
 *          to detect ABA conditions, or the wrong object might be marked
 *          as the head of the stack.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Wednesday, June 23, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_llsc_int_stack(void)
{
#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    llsc_int_stack_t *thread_data = NULL;       /* User data structs for threads */
    llsc_int_stack_obj_t **objs = NULL; /* Array of pointers to objects to put on stack */
    int nobjs;                  /* Number of objects */
    OPA_int_t head;             /* The head of the stack */
    OPA_int_t npushers;         /* Number of "push" threads running */
    OPA_int_t failed;           /* Whether the test has failed and must be terminated */
    int threads_per_group;      /* Threads per group */
    int npops = 0;              /* Total number of times an object was popped */
    int npushes = 0;            /* Total number of times an object was pushed */
    int non_stack = 0;          /* Number of objects on the stack */
    int nerrors = 0;            /* Number of errors returned from threads */
    int warned = 0;             /* Whether a warning message has been issued */
    unsigned nthreads = num_threads[curr_test];
    int i;

    /* Don't test with only 1 thread */
    if (nthreads < 2)
        return 0;

    TESTING("integer LL/SC stack", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (llsc_int_stack_t *) calloc(nthreads, sizeof(llsc_int_stack_t))))
        TEST_ERROR;

    /* Calculate number of groups and threads per group */
    if ((nthreads / LLSC_INT_STACK_TPG) >= 2)
        threads_per_group = LLSC_INT_STACK_TPG;
    else
        threads_per_group = (nthreads + 1) / 2;
    nobjs = (nthreads - 1) / threads_per_group;
    OPA_store_int(&npushers, nthreads - threads_per_group);

    /* Allocate objects.  Allocate each object individually instead of one large
     * array to add a random component to which objects share a cache line.  The
     * "objs" array is also used to allow integers to function as "pointers" to
     * objects, by being an index into this array. */
    if (NULL == (objs = (llsc_int_stack_obj_t **) malloc(nobjs * sizeof(llsc_int_stack_obj_t *))))
        TEST_ERROR;
    for (i = 0; i < nobjs; i++) {
        if (NULL == (objs[i] = (llsc_int_stack_obj_t *) malloc(sizeof(llsc_int_stack_obj_t))))
            TEST_ERROR;
        OPA_store_int(&objs[i]->on_stack, 0);
        OPA_store_int(&objs[i]->next, -1);
    }   /* end for */

    /* Initialize thread data structs */
    OPA_store_int(&head, -1);
    OPA_store_int(&failed, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].head = &head;
        thread_data[i].objs = objs;
        thread_data[i].obj = (i / threads_per_group) - 1;
        thread_data[i].npushers = &npushers;
        thread_data[i].failed = &failed;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++) {
        if (i < threads_per_group) {
            if (pthread_create(&threads[i], &ptattr, threaded_llsc_int_stack_pop, &thread_data[i]))
                TEST_ERROR;
        } else
            if (pthread_create(&threads[i], &ptattr, threaded_llsc_int_stack_push, &thread_data[i]))
            TEST_ERROR;
    }   /* end for */
    (void) threaded_llsc_int_stack_push(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Check for any errors returned from any threads */
    for (i = 0; i < nthreads; i++)
        nerrors += thread_data[i].nerrors;
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d error%s encountered in thread routines\n", nerrors,
                             nerrors == 1 ? "" : "s"));

    /* Walk the stack, and make sure it is consistent */
    i = OPA_load_int(&head);
    while (i >= 0 && i < nobjs) {
        /* Make sure it is marked as "on_stack" */
        if (1 != OPA_load_int(&objs[i]->on_stack))
            FAIL_OP_ERROR(printf
                          ("    Unexpected value of \"on stack\" for object %d: %d Expected: 1\n",
                           i, OPA_load_int(&objs[i]->on_stack)));

        /* Change "on_stack" to 2, so if it appears again it will fail */
        OPA_store_int(&objs[i]->on_stack, 2);

        /* Advance to next object in stack */
        non_stack++;
        i = OPA_load_int(&objs[i]->next);
    }   /* end while */

    /* Make sure the last "next" pointer was -1 */
    if (i != -1)
        FAIL_OP_ERROR(printf("    Last \"next\" pointer was %d Expected: -1\n", i));

    /* Walk through all of the objects and make sure that they are all either
     * off the stack or have been encountered by walking the stack */
    for (i = 0; i < nobjs; i++)
        if (OPA_load_int(&objs[i]->on_stack) != 0 && OPA_load_int(&objs[i]->on_stack) != 2)
            FAIL_OP_ERROR(printf
                          ("    After walking the stack, object %d's \"on_stack\" set to %d Expected: 0 or 2\n",
                           i, OPA_load_int(&objs[i]->on_stack)));

    /* Make sure the number of successessful pops is consistent with the number
     * of pushes */
    for (i = 0; i < threads_per_group; i++)
        npops += thread_data[i].nsuccess;
    for (i = threads_per_group; i < nthreads; i++)
        npushes += thread_data[i].nsuccess;
    if (npops != npushes - non_stack)
        FAIL_OP_ERROR(printf("    Unexpected number of pops: %d Expected: %d\n",
                             npops, npushes - non_stack));

    /* Check if there were any failed pushes (this is a warning, not an error)
     */
    if (npushes != (nthreads - threads_per_group) * LLSC_INT_STACK_NITER) {
        WARNING();
        printf("    Only %d/%d pushes succeeded.  There may be an issue with fairness.\n",
               npushes, (nthreads - threads_per_group) * LLSC_INT_STACK_NITER);
        warned = 1;
    }

    /* end if */
    /* Free memory */
    for (i = 0; i < nobjs; i++)
        free(objs[i]);
    free(objs);
    free(threads);
    free(thread_data);

    if (!warned)
        PASSED();

#else /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
    TESTING("integer LL/SC stack", 0);
    SKIPPED();
#if !defined(OPA_HAVE_PTHREAD_H)
    puts("    pthread.h not available");
#else /* OPA_HAVE_PTHREAD_H */
    puts("    LL/SC not available");
#endif /* OPA_HAVE_PTHREAD_H */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
  error:
    if (objs) {
        for (i = 0; i < nobjs; i++)
            if (objs[i])
                free(objs[i]);
        free(objs);
    }   /* end if */
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
}       /* end test_threaded_llsc_int_stack() */


#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
/*-------------------------------------------------------------------------
 * Function: threaded_llsc_ptr_stack_push
 *
 * Purpose: "Push" routine for test_threaded_llsc_ptr_stack
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Friday, June 25, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_llsc_ptr_stack_push(void *_udata)
{
    llsc_ptr_stack_t *udata = (llsc_ptr_stack_t *) _udata;
    llsc_ptr_stack_obj_t *obj = udata->obj;
    llsc_ptr_stack_obj_t *tmp_head = NULL;
    unsigned niter = LLSC_PTR_STACK_NITER;
    unsigned i, j;

    /* Main loop */
    for (i = 0; i < niter && !OPA_load_int(udata->failed); i++) {
        /* Acquire object */
        j = 0;
        while (OPA_cas_int(&obj->on_stack, 0, 1)) {
            OPA_TEST_YIELD();
            if (++j == LLSC_PTR_STACK_NLOOP)
                break;
        }       /* end while */

        /* Check if we failed to acquire the object */
        if (j == LLSC_PTR_STACK_NLOOP)
            continue;

        /* Push object onto stack */
        do {
            tmp_head = OPA_load_ptr(udata->head);
            OPA_store_ptr(&obj->next, tmp_head);
            /* don't let the "on_stack" CAS or the "next" store pass the "head" CAS */
            OPA_write_barrier();
        } while (tmp_head != OPA_cas_ptr(udata->head, tmp_head, obj));

        /* Increment the number of successful pushes */
        udata->nsuccess++;
    }   /* end for */

    /* don't allow npusher's decr to be perceived as happening before the pushes
     * are visible */
    OPA_write_barrier();

    /* Mark this thread as having completed */
    OPA_decr_int(udata->npushers);

    /* Exit */
    if (udata->master_thread)
        return (NULL);
    else
        pthread_exit(NULL);
}       /* end threaded_llsc_ptr_stack_push() */

/*-------------------------------------------------------------------------
 * Function: threaded_llsc_ptr_stack_pop
 *
 * Purpose: "Pop" routine for test_threaded_llsc_ptr_stack
 *
 * Return: NULL
 *
 * Programmer: Neil Fortner
 *             Friday, June 25, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static void *threaded_llsc_ptr_stack_pop(void *_udata)
{
    llsc_ptr_stack_t *udata = (llsc_ptr_stack_t *) _udata;
    llsc_ptr_stack_obj_t *obj = NULL;
    llsc_ptr_stack_obj_t *next = NULL;
    unsigned i;

    /* Main loop */
    do {
        /* Load-linked the object */
        if (NULL == (obj = OPA_LL_ptr(udata->head))) {
            OPA_TEST_YIELD();
            continue;
        }

        /* end if */
        /* Get the next object */
        next = OPA_load_ptr(&obj->next);

        /* Store conditional the "next" object as the new head */
        if (OPA_SC_ptr(udata->head, next)) {
            /* ensure that the head data is perceived as stored before reading
             * the on_stack variable */
            OPA_read_write_barrier();

            /* Make sure this object was marked as on the stack */
            if (!OPA_load_int(&obj->on_stack)) {
                OP_SUPPRESS(printf("    Popped object was not on the stack\n"), udata->nerrors, 20);
                udata->nerrors++;

                if (udata->nerrors >= 1000000) {
                    /* Cause all threads to break out so the test fails in a
                     * reasonable amount of time */
                    OPA_store_int(udata->failed, 1);
                    break;
                }       /* end if */
            }

            /* end if */
            /* Mark the object as off the stack, release for pushing */
            OPA_store_int(&obj->on_stack, 0);

            /* Increment the number of successful pops */
            udata->nsuccess++;
        }

        /* end if */
        /* We could include a read_write_barrier here to enforce ordering
         * between pop attempt above and the npushers load below, but leaving it
         * out is safe because npushers starts at max and monotonically
         * decreases.  Worst case is a few extra iterations. */
    } while (OPA_load_int(udata->npushers) && !OPA_load_int(udata->failed));

    pthread_exit(NULL);
}       /* end threaded_llsc_ptr_stack_pop() */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */


/*-------------------------------------------------------------------------
 * Function: test_threaded_llsc_ptr_stack
 *
 * Purpose: Tests atomicity of OPA_LL_ptr and OPA_SC_ptr.  Launches
 *          nthreads threads, subdivided into nthreads/3 or 2 groups,
 *          whichever is greater.  One group of threads continually tries
 *          to pop objects off a shared stack using LL/SC, while the
 *          other groups all try to push objects onto the stack.  The
 *          algorithm of the pop routine is such that LL/SC must be able
 *          to detect ABA conditions, or the wrong object might be marked
 *          as the head of the stack.
 *
 * Return: Success: 0
 *         Failure: 1
 *
 * Programmer: Neil Fortner
 *             Friday, June 25, 2010
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
static int test_threaded_llsc_ptr_stack(void)
{
#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
    pthread_t *threads = NULL;  /* Threads */
    pthread_attr_t ptattr;      /* Thread attributes */
    llsc_ptr_stack_t *thread_data = NULL;       /* User data structs for threads */
    llsc_ptr_stack_obj_t **objs = NULL; /* Array of pointers to objects to put on stack */
    llsc_ptr_stack_obj_t *obj = NULL;   /* Object on stack (used while walking the stack) */
    int nobjs;                  /* Number of objects */
    OPA_ptr_t head;             /* The head of the stack */
    OPA_int_t npushers;         /* Number of "push" threads running */
    OPA_int_t failed;           /* Whether the test has failed and must be terminated */
    int threads_per_group;      /* Threads per group */
    int npops = 0;              /* Total number of times an object was popped */
    int npushes = 0;            /* Total number of times an object was pushed */
    int non_stack = 0;          /* Number of objects on the stack */
    int nerrors = 0;            /* Number of errors returned from threads */
    int warned = 0;             /* Whether a warning message has been issued */
    unsigned nthreads = num_threads[curr_test];
    int i;

    /* Don't test with only 1 thread */
    if (nthreads < 2)
        return 0;

    TESTING("pointer LL/SC stack", nthreads);

    /* Allocate array of threads */
    if (NULL == (threads = (pthread_t *) malloc((nthreads - 1) * sizeof(pthread_t))))
        TEST_ERROR;

    /* Allocate array of thread data */
    if (NULL == (thread_data = (llsc_ptr_stack_t *) calloc(nthreads, sizeof(llsc_ptr_stack_t))))
        TEST_ERROR;

    /* Calculate number of groups and threads per group */
    if ((nthreads / LLSC_PTR_STACK_TPG) >= 2)
        threads_per_group = LLSC_PTR_STACK_TPG;
    else
        threads_per_group = (nthreads + 1) / 2;
    nobjs = (nthreads - 1) / threads_per_group;
    OPA_store_int(&npushers, nthreads - threads_per_group);

    /* Allocate objects.  Allocate each object individually instead of one large
     * array to add a random component to which objects share a cache line.  The
     * "objs" array is also used to allow integers to function as "pointers" to
     * objects, by being an index into this array. */
    if (NULL == (objs = (llsc_ptr_stack_obj_t **) malloc(nobjs * sizeof(llsc_ptr_stack_obj_t *))))
        TEST_ERROR;
    for (i = 0; i < nobjs; i++) {
        if (NULL == (objs[i] = (llsc_ptr_stack_obj_t *) malloc(sizeof(llsc_ptr_stack_obj_t))))
            TEST_ERROR;
        OPA_store_int(&objs[i]->on_stack, 0);
        OPA_store_ptr(&objs[i]->next, NULL);
    }   /* end for */

    /* Initialize thread data structs */
    OPA_store_ptr(&head, NULL);
    OPA_store_int(&failed, 0);
    for (i = 0; i < nthreads; i++) {
        thread_data[i].head = &head;
        if (i >= threads_per_group)
            thread_data[i].obj = objs[(i / threads_per_group) - 1];
        thread_data[i].npushers = &npushers;
        thread_data[i].failed = &failed;
    }   /* end for */
    thread_data[nthreads - 1].master_thread = 1;

    /* Set threads to be joinable */
    pthread_attr_init(&ptattr);
    pthread_attr_setdetachstate(&ptattr, PTHREAD_CREATE_JOINABLE);

    /* Create the threads */
    for (i = 0; i < (nthreads - 1); i++) {
        if (i < threads_per_group) {
            if (pthread_create(&threads[i], &ptattr, threaded_llsc_ptr_stack_pop, &thread_data[i]))
                TEST_ERROR;
        } else
            if (pthread_create(&threads[i], &ptattr, threaded_llsc_ptr_stack_push, &thread_data[i]))
            TEST_ERROR;
    }   /* end for */
    (void) threaded_llsc_ptr_stack_push(&thread_data[i]);

    /* Free the attribute */
    if (pthread_attr_destroy(&ptattr))
        TEST_ERROR;

    /* Join the threads */
    for (i = 0; i < (nthreads - 1); i++)
        if (pthread_join(threads[i], NULL))
            TEST_ERROR;

    /* Check for any errors returned from any threads */
    for (i = 0; i < nthreads; i++)
        nerrors += thread_data[i].nerrors;
    if (nerrors)
        FAIL_OP_ERROR(printf("    %d error%s encountered in thread routines\n", nerrors,
                             nerrors == 1 ? "" : "s"));

    /* Walk the stack, and make sure it is consistent */
    obj = OPA_load_ptr(&head);
    while (obj) {
        /* Make sure it is marked as "on_stack" */
        if (1 != OPA_load_int(&obj->on_stack))
            FAIL_OP_ERROR(printf
                          ("    Unexpected value of \"on stack\" for object %d: %d Expected: 1\n",
                           i, OPA_load_int(&obj->on_stack)));

        /* Change "on_stack" to 2, so if it appears again it will fail */
        OPA_store_int(&obj->on_stack, 2);

        /* Advance to next object in stack */
        non_stack++;
        obj = OPA_load_ptr(&obj->next);
    }   /* end while */

    /* Walk through all of the objects and make sure that they are all either
     * off the stack or have been encountered by walking the stack */
    for (i = 0; i < nobjs; i++)
        if (OPA_load_int(&objs[i]->on_stack) != 0 && OPA_load_int(&objs[i]->on_stack) != 2)
            FAIL_OP_ERROR(printf
                          ("    After walking the stack, object %d's \"on_stack\" set to %d Expected: 0 or 2\n",
                           i, OPA_load_int(&objs[i]->on_stack)));

    /* Make sure the number of successessful pops is consistent with the number
     * of pushes */
    for (i = 0; i < threads_per_group; i++)
        npops += thread_data[i].nsuccess;
    for (i = threads_per_group; i < nthreads; i++)
        npushes += thread_data[i].nsuccess;
    if (npops != npushes - non_stack)
        FAIL_OP_ERROR(printf("    Unexpected number of pops: %d Expected: %d\n",
                             npops, npushes - non_stack));

    /* Check if there were any failed pushes (this is a warning, not an error)
     */
    if (npushes != (nthreads - threads_per_group) * LLSC_PTR_STACK_NITER) {
        WARNING();
        printf("    Only %d/%d pushes succeeded.  There may be an issue with fairness.\n",
               npushes, (nthreads - threads_per_group) * LLSC_PTR_STACK_NITER);
        warned = 1;
    }

    /* end if */
    /* Free memory */
    for (i = 0; i < nobjs; i++)
        free(objs[i]);
    free(objs);
    free(threads);
    free(thread_data);

    if (!warned)
        PASSED();

#else /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
    TESTING("pointer LL/SC stack", 0);
    SKIPPED();
#if !defined(OPA_HAVE_PTHREAD_H)
    puts("    pthread.h not available");
#else /* OPA_HAVE_PTHREAD_H */
    puts("    LL/SC not available");
#endif /* OPA_HAVE_PTHREAD_H */
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */

    return 0;

#if defined(OPA_HAVE_PTHREAD_H) && defined(OPA_LL_SC_SUPPORTED)
  error:
    if (objs) {
        for (i = 0; i < nobjs; i++)
            if (objs[i])
                free(objs[i]);
        free(objs);
    }   /* end if */
    if (threads)
        free(threads);
    if (thread_data)
        free(thread_data);
    return 1;
#endif /* OPA_HAVE_PTHREAD_H && OPA_LL_SC_SUPPORTED */
}       /* end test_threaded_llsc_ptr_stack() */


/*-------------------------------------------------------------------------
 * Function:    main
 *
 * Purpose:     Tests the opa primitives
 *
 * Return:      Success:        exit(0)
 *
 *              Failure:        exit(1)
 *
 * Programmer:  Neil Fortner
 *              Thursday, March 19, 2009
 *
 * Modifications:
 *
 *-------------------------------------------------------------------------
 */
int main(int argc, char **argv)
{
    unsigned nerrors = 0;
#if defined(OPA_USE_LOCK_BASED_PRIMITIVES)
    OPA_emulation_ipl_t shm_lock;
    OPA_Interprocess_lock_init(&shm_lock, 1 /*isLeader */);
#endif

    srand((unsigned) time(NULL));

    /* Simple tests */
    nerrors += test_simple_loadstore_int();
    nerrors += test_simple_loadstore_ptr();
    nerrors += test_simple_add_incr_decr();
    nerrors += test_simple_decr_and_test();
    nerrors += test_simple_faa_fai_fad();
    nerrors += test_simple_cas_int();
    nerrors += test_simple_cas_ptr();
    nerrors += test_simple_swap_int();
    nerrors += test_simple_swap_ptr();
    nerrors += test_simple_llsc_int();
    nerrors += test_simple_llsc_ptr();

    /* Threaded tests with a fixed number of threads */
    nerrors += test_threaded_llsc_int_aba();
    nerrors += test_threaded_llsc_ptr_aba();

    /* Loop over test configurations */
    for (curr_test = 0; curr_test < num_thread_tests; curr_test++) {
        /* Threaded tests */
        nerrors += test_threaded_loadstore_int();
        nerrors += test_threaded_loadstore_ptr();
        nerrors += test_threaded_add();
        nerrors += test_threaded_incr_decr();
        nerrors += test_threaded_decr_and_test();
        nerrors += test_threaded_faa();
        nerrors += test_threaded_faa_ret();
        nerrors += test_threaded_fai_fad();
        nerrors += test_threaded_fai_ret();
        nerrors += test_threaded_fad_ret();
        nerrors += test_threaded_cas_int();
        nerrors += test_threaded_cas_ptr();
        nerrors += test_grouped_cas_int();
        nerrors += test_grouped_cas_ptr();
        nerrors += test_threaded_cas_int_fairness();
        nerrors += test_threaded_cas_ptr_fairness();
        nerrors += test_threaded_swap_int();
        nerrors += test_threaded_swap_ptr();
        nerrors += test_threaded_llsc_int_stack();
        nerrors += test_threaded_llsc_ptr_stack();
    }   /* end for */

    if (nerrors)
        goto error;
    printf("All primitives tests passed.\n");

    return 0;

  error:
    if (!nerrors)
        nerrors = 1;
    printf("***** %d PRIMITIVES TEST%s FAILED! *****\n", nerrors, 1 == nerrors ? "" : "S");
    return 1;
}       /* end main() */
