• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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