1*8e33eff8Schristos #include "test/jemalloc_test.h" 2*8e33eff8Schristos 3*8e33eff8Schristos /* 4*8e33eff8Schristos * We *almost* have consistent short names (e.g. "u32" for uint32_t, "b" for 5*8e33eff8Schristos * bool, etc. The one exception is that the short name for void * is "p" in 6*8e33eff8Schristos * some places and "ptr" in others. In the long run it would be nice to unify 7*8e33eff8Schristos * these, but in the short run we'll use this shim. 8*8e33eff8Schristos */ 9*8e33eff8Schristos #define assert_p_eq assert_ptr_eq 10*8e33eff8Schristos 11*8e33eff8Schristos /* 12*8e33eff8Schristos * t: the non-atomic type, like "uint32_t". 13*8e33eff8Schristos * ta: the short name for the type, like "u32". 14*8e33eff8Schristos * val[1,2,3]: Values of the given type. The CAS tests use val2 for expected, 15*8e33eff8Schristos * and val3 for desired. 16*8e33eff8Schristos */ 17*8e33eff8Schristos 18*8e33eff8Schristos #define DO_TESTS(t, ta, val1, val2, val3) do { \ 19*8e33eff8Schristos t val; \ 20*8e33eff8Schristos t expected; \ 21*8e33eff8Schristos bool success; \ 22*8e33eff8Schristos /* This (along with the load below) also tests ATOMIC_LOAD. */ \ 23*8e33eff8Schristos atomic_##ta##_t atom = ATOMIC_INIT(val1); \ 24*8e33eff8Schristos \ 25*8e33eff8Schristos /* ATOMIC_INIT and load. */ \ 26*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 27*8e33eff8Schristos assert_##ta##_eq(val1, val, "Load or init failed"); \ 28*8e33eff8Schristos \ 29*8e33eff8Schristos /* Store. */ \ 30*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 31*8e33eff8Schristos atomic_store_##ta(&atom, val2, ATOMIC_RELAXED); \ 32*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 33*8e33eff8Schristos assert_##ta##_eq(val2, val, "Store failed"); \ 34*8e33eff8Schristos \ 35*8e33eff8Schristos /* Exchange. */ \ 36*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 37*8e33eff8Schristos val = atomic_exchange_##ta(&atom, val2, ATOMIC_RELAXED); \ 38*8e33eff8Schristos assert_##ta##_eq(val1, val, "Exchange returned invalid value"); \ 39*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 40*8e33eff8Schristos assert_##ta##_eq(val2, val, "Exchange store invalid value"); \ 41*8e33eff8Schristos \ 42*8e33eff8Schristos /* \ 43*8e33eff8Schristos * Weak CAS. Spurious failures are allowed, so we loop a few \ 44*8e33eff8Schristos * times. \ 45*8e33eff8Schristos */ \ 46*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 47*8e33eff8Schristos success = false; \ 48*8e33eff8Schristos for (int i = 0; i < 10 && !success; i++) { \ 49*8e33eff8Schristos expected = val2; \ 50*8e33eff8Schristos success = atomic_compare_exchange_weak_##ta(&atom, \ 51*8e33eff8Schristos &expected, val3, ATOMIC_RELAXED, ATOMIC_RELAXED); \ 52*8e33eff8Schristos assert_##ta##_eq(val1, expected, \ 53*8e33eff8Schristos "CAS should update expected"); \ 54*8e33eff8Schristos } \ 55*8e33eff8Schristos assert_b_eq(val1 == val2, success, \ 56*8e33eff8Schristos "Weak CAS did the wrong state update"); \ 57*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 58*8e33eff8Schristos if (success) { \ 59*8e33eff8Schristos assert_##ta##_eq(val3, val, \ 60*8e33eff8Schristos "Successful CAS should update atomic"); \ 61*8e33eff8Schristos } else { \ 62*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 63*8e33eff8Schristos "Unsuccessful CAS should not update atomic"); \ 64*8e33eff8Schristos } \ 65*8e33eff8Schristos \ 66*8e33eff8Schristos /* Strong CAS. */ \ 67*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 68*8e33eff8Schristos expected = val2; \ 69*8e33eff8Schristos success = atomic_compare_exchange_strong_##ta(&atom, &expected, \ 70*8e33eff8Schristos val3, ATOMIC_RELAXED, ATOMIC_RELAXED); \ 71*8e33eff8Schristos assert_b_eq(val1 == val2, success, \ 72*8e33eff8Schristos "Strong CAS did the wrong state update"); \ 73*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 74*8e33eff8Schristos if (success) { \ 75*8e33eff8Schristos assert_##ta##_eq(val3, val, \ 76*8e33eff8Schristos "Successful CAS should update atomic"); \ 77*8e33eff8Schristos } else { \ 78*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 79*8e33eff8Schristos "Unsuccessful CAS should not update atomic"); \ 80*8e33eff8Schristos } \ 81*8e33eff8Schristos \ 82*8e33eff8Schristos \ 83*8e33eff8Schristos } while (0) 84*8e33eff8Schristos 85*8e33eff8Schristos #define DO_INTEGER_TESTS(t, ta, val1, val2) do { \ 86*8e33eff8Schristos atomic_##ta##_t atom; \ 87*8e33eff8Schristos t val; \ 88*8e33eff8Schristos \ 89*8e33eff8Schristos /* Fetch-add. */ \ 90*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 91*8e33eff8Schristos val = atomic_fetch_add_##ta(&atom, val2, ATOMIC_RELAXED); \ 92*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 93*8e33eff8Schristos "Fetch-add should return previous value"); \ 94*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 95*8e33eff8Schristos assert_##ta##_eq(val1 + val2, val, \ 96*8e33eff8Schristos "Fetch-add should update atomic"); \ 97*8e33eff8Schristos \ 98*8e33eff8Schristos /* Fetch-sub. */ \ 99*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 100*8e33eff8Schristos val = atomic_fetch_sub_##ta(&atom, val2, ATOMIC_RELAXED); \ 101*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 102*8e33eff8Schristos "Fetch-sub should return previous value"); \ 103*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 104*8e33eff8Schristos assert_##ta##_eq(val1 - val2, val, \ 105*8e33eff8Schristos "Fetch-sub should update atomic"); \ 106*8e33eff8Schristos \ 107*8e33eff8Schristos /* Fetch-and. */ \ 108*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 109*8e33eff8Schristos val = atomic_fetch_and_##ta(&atom, val2, ATOMIC_RELAXED); \ 110*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 111*8e33eff8Schristos "Fetch-and should return previous value"); \ 112*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 113*8e33eff8Schristos assert_##ta##_eq(val1 & val2, val, \ 114*8e33eff8Schristos "Fetch-and should update atomic"); \ 115*8e33eff8Schristos \ 116*8e33eff8Schristos /* Fetch-or. */ \ 117*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 118*8e33eff8Schristos val = atomic_fetch_or_##ta(&atom, val2, ATOMIC_RELAXED); \ 119*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 120*8e33eff8Schristos "Fetch-or should return previous value"); \ 121*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 122*8e33eff8Schristos assert_##ta##_eq(val1 | val2, val, \ 123*8e33eff8Schristos "Fetch-or should update atomic"); \ 124*8e33eff8Schristos \ 125*8e33eff8Schristos /* Fetch-xor. */ \ 126*8e33eff8Schristos atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \ 127*8e33eff8Schristos val = atomic_fetch_xor_##ta(&atom, val2, ATOMIC_RELAXED); \ 128*8e33eff8Schristos assert_##ta##_eq(val1, val, \ 129*8e33eff8Schristos "Fetch-xor should return previous value"); \ 130*8e33eff8Schristos val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \ 131*8e33eff8Schristos assert_##ta##_eq(val1 ^ val2, val, \ 132*8e33eff8Schristos "Fetch-xor should update atomic"); \ 133*8e33eff8Schristos } while (0) 134*8e33eff8Schristos 135*8e33eff8Schristos #define TEST_STRUCT(t, ta) \ 136*8e33eff8Schristos typedef struct { \ 137*8e33eff8Schristos t val1; \ 138*8e33eff8Schristos t val2; \ 139*8e33eff8Schristos t val3; \ 140*8e33eff8Schristos } ta##_test_t; 141*8e33eff8Schristos 142*8e33eff8Schristos #define TEST_CASES(t) { \ 143*8e33eff8Schristos {(t)-1, (t)-1, (t)-2}, \ 144*8e33eff8Schristos {(t)-1, (t) 0, (t)-2}, \ 145*8e33eff8Schristos {(t)-1, (t) 1, (t)-2}, \ 146*8e33eff8Schristos \ 147*8e33eff8Schristos {(t) 0, (t)-1, (t)-2}, \ 148*8e33eff8Schristos {(t) 0, (t) 0, (t)-2}, \ 149*8e33eff8Schristos {(t) 0, (t) 1, (t)-2}, \ 150*8e33eff8Schristos \ 151*8e33eff8Schristos {(t) 1, (t)-1, (t)-2}, \ 152*8e33eff8Schristos {(t) 1, (t) 0, (t)-2}, \ 153*8e33eff8Schristos {(t) 1, (t) 1, (t)-2}, \ 154*8e33eff8Schristos \ 155*8e33eff8Schristos {(t)0, (t)-(1 << 22), (t)-2}, \ 156*8e33eff8Schristos {(t)0, (t)(1 << 22), (t)-2}, \ 157*8e33eff8Schristos {(t)(1 << 22), (t)-(1 << 22), (t)-2}, \ 158*8e33eff8Schristos {(t)(1 << 22), (t)(1 << 22), (t)-2} \ 159*8e33eff8Schristos } 160*8e33eff8Schristos 161*8e33eff8Schristos #define TEST_BODY(t, ta) do { \ 162*8e33eff8Schristos const ta##_test_t tests[] = TEST_CASES(t); \ 163*8e33eff8Schristos for (unsigned i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { \ 164*8e33eff8Schristos ta##_test_t test = tests[i]; \ 165*8e33eff8Schristos DO_TESTS(t, ta, test.val1, test.val2, test.val3); \ 166*8e33eff8Schristos } \ 167*8e33eff8Schristos } while (0) 168*8e33eff8Schristos 169*8e33eff8Schristos #define INTEGER_TEST_BODY(t, ta) do { \ 170*8e33eff8Schristos const ta##_test_t tests[] = TEST_CASES(t); \ 171*8e33eff8Schristos for (unsigned i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { \ 172*8e33eff8Schristos ta##_test_t test = tests[i]; \ 173*8e33eff8Schristos DO_TESTS(t, ta, test.val1, test.val2, test.val3); \ 174*8e33eff8Schristos DO_INTEGER_TESTS(t, ta, test.val1, test.val2); \ 175*8e33eff8Schristos } \ 176*8e33eff8Schristos } while (0) 177*8e33eff8Schristos 178*8e33eff8Schristos TEST_STRUCT(uint64_t, u64); 179*8e33eff8Schristos TEST_BEGIN(test_atomic_u64) { 180*8e33eff8Schristos #if !(LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3) 181*8e33eff8Schristos test_skip("64-bit atomic operations not supported"); 182*8e33eff8Schristos #else 183*8e33eff8Schristos INTEGER_TEST_BODY(uint64_t, u64); 184*8e33eff8Schristos #endif 185*8e33eff8Schristos } 186*8e33eff8Schristos TEST_END 187*8e33eff8Schristos 188*8e33eff8Schristos 189*8e33eff8Schristos TEST_STRUCT(uint32_t, u32); 190*8e33eff8Schristos TEST_BEGIN(test_atomic_u32) { 191*8e33eff8Schristos INTEGER_TEST_BODY(uint32_t, u32); 192*8e33eff8Schristos } 193*8e33eff8Schristos TEST_END 194*8e33eff8Schristos 195*8e33eff8Schristos TEST_STRUCT(void *, p); 196*8e33eff8Schristos TEST_BEGIN(test_atomic_p) { 197*8e33eff8Schristos TEST_BODY(void *, p); 198*8e33eff8Schristos } 199*8e33eff8Schristos TEST_END 200*8e33eff8Schristos 201*8e33eff8Schristos TEST_STRUCT(size_t, zu); 202*8e33eff8Schristos TEST_BEGIN(test_atomic_zu) { 203*8e33eff8Schristos INTEGER_TEST_BODY(size_t, zu); 204*8e33eff8Schristos } 205*8e33eff8Schristos TEST_END 206*8e33eff8Schristos 207*8e33eff8Schristos TEST_STRUCT(ssize_t, zd); 208*8e33eff8Schristos TEST_BEGIN(test_atomic_zd) { 209*8e33eff8Schristos INTEGER_TEST_BODY(ssize_t, zd); 210*8e33eff8Schristos } 211*8e33eff8Schristos TEST_END 212*8e33eff8Schristos 213*8e33eff8Schristos 214*8e33eff8Schristos TEST_STRUCT(unsigned, u); 215*8e33eff8Schristos TEST_BEGIN(test_atomic_u) { 216*8e33eff8Schristos INTEGER_TEST_BODY(unsigned, u); 217*8e33eff8Schristos } 218*8e33eff8Schristos TEST_END 219*8e33eff8Schristos 220*8e33eff8Schristos int 221*8e33eff8Schristos main(void) { 222*8e33eff8Schristos return test( 223*8e33eff8Schristos test_atomic_u64, 224*8e33eff8Schristos test_atomic_u32, 225*8e33eff8Schristos test_atomic_p, 226*8e33eff8Schristos test_atomic_zu, 227*8e33eff8Schristos test_atomic_zd, 228*8e33eff8Schristos test_atomic_u); 229*8e33eff8Schristos } 230