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