/* -*- 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() */