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);
TEST_BEGIN(test_atomic_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);
TEST_BEGIN(test_atomic_u32)190 TEST_BEGIN(test_atomic_u32) {
191 INTEGER_TEST_BODY(uint32_t, u32);
192 }
193 TEST_END
194
195 TEST_STRUCT(void *, p);
TEST_BEGIN(test_atomic_p)196 TEST_BEGIN(test_atomic_p) {
197 TEST_BODY(void *, p);
198 }
199 TEST_END
200
201 TEST_STRUCT(size_t, zu);
TEST_BEGIN(test_atomic_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);
TEST_BEGIN(test_atomic_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);
TEST_BEGIN(test_atomic_u)215 TEST_BEGIN(test_atomic_u) {
216 INTEGER_TEST_BODY(unsigned, u);
217 }
218 TEST_END
219
220 int
main(void)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