xref: /netbsd-src/external/bsd/jemalloc.old/dist/test/unit/atomic.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
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