Skip to content

Commit 5deca65

Browse files
alex-katranovdvyukov
authored andcommitted
tsan: add lock free stack pattern test
Add a set of tests that iterate over possible combinations of memory orders for lock free stack implementation. Reviewed By: dvyukov Differential Revision: https://reviews.llvm.org/D110552
1 parent d5999bd commit 5deca65

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
2+
// RUN: %clangxx_tsan -O1 %s -DRACE -o %t && %deflake %run %t | FileCheck %s --check-prefix=CHECK-RACE
3+
#include "test.h"
4+
5+
const int kThreadCount = 4;
6+
#if RACE
7+
const int kTestCount = 16;
8+
#else
9+
const int kTestCount = 9;
10+
#endif
11+
12+
template <typename F> F for_each_mo(int mo, F f) {
13+
f(mo);
14+
return f;
15+
}
16+
17+
template <typename... Rest>
18+
auto for_each_mo(int mo, Rest... rest) -> decltype(for_each_mo(rest...)) {
19+
auto f = for_each_mo(rest...);
20+
f(mo);
21+
return f;
22+
}
23+
24+
void LockFreeStackImpl(int test, bool main_thread, int mo2, int mo4) {
25+
struct Node {
26+
int data;
27+
Node *next;
28+
};
29+
static Node *heads[kTestCount]{};
30+
auto head = heads + test;
31+
32+
auto concurrent_push = [head](Node *new_head, int mo1, int mo2, int mo3) {
33+
auto expected = __atomic_load_n(head, mo1);
34+
do {
35+
new_head->next = expected;
36+
} while (!__atomic_compare_exchange_n(head, &expected, new_head,
37+
/*weak*/ true, mo2, mo3));
38+
};
39+
40+
auto concurrent_grab_all = [head](int mo4) {
41+
volatile int sink{};
42+
(void)sink;
43+
44+
auto h = __atomic_exchange_n(head, nullptr, mo4);
45+
while (h) {
46+
sink = ++h->data;
47+
auto next = h->next;
48+
delete h;
49+
h = next;
50+
}
51+
};
52+
53+
if (main_thread) {
54+
concurrent_grab_all(mo4);
55+
} else {
56+
int i = 0;
57+
// We have 15 combinations of mo1 and mo3. Since we have two race reports
58+
// for each combination (the first report is for 'data' and the second
59+
// report for 'next'), there are 30 race reports in total that should match
60+
// to "CHECK-RACE-COUNT{-number_of_reports}" below
61+
for_each_mo(
62+
__ATOMIC_RELAXED, __ATOMIC_ACQUIRE, __ATOMIC_SEQ_CST, [&](int mo1) {
63+
for_each_mo(__ATOMIC_RELAXED, __ATOMIC_ACQUIRE, __ATOMIC_RELEASE,
64+
__ATOMIC_ACQ_REL, __ATOMIC_SEQ_CST, [&](int mo3) {
65+
concurrent_push(new Node{i++}, mo1, mo2, mo3);
66+
});
67+
});
68+
}
69+
}
70+
71+
void LockFreeStack(int test, bool main_thread, int mo2, int mo4) {
72+
barrier_wait(&barrier);
73+
if (main_thread) {
74+
// We need to call LockFreeStackImpl second time after the barrier
75+
// to guarantee at least one grab_all and cleanup.
76+
// However, it is better to have one instantiation of LockFreeStackImpl
77+
// on the main thread to merge the call stacks and prevent double race
78+
// reports. Therefore, we use two interation for loop and skip the barrier
79+
// on the second iteration.
80+
for (int i = 0; i < 2; ++i) {
81+
LockFreeStackImpl(test, main_thread, mo2, mo4);
82+
if (i == 0) {
83+
barrier_wait(&barrier);
84+
}
85+
}
86+
} else {
87+
LockFreeStackImpl(test, main_thread, mo2, mo4);
88+
barrier_wait(&barrier);
89+
}
90+
}
91+
92+
void Test(bool main_thread) {
93+
for (int test = 0; test < kTestCount; test++) {
94+
if (main_thread) {
95+
fprintf(stderr, "Test %d\n", test);
96+
}
97+
switch (test) {
98+
#if RACE
99+
case 0:
100+
LockFreeStack(test, main_thread, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
101+
break;
102+
case 1:
103+
LockFreeStack(test, main_thread, __ATOMIC_RELAXED, __ATOMIC_ACQUIRE);
104+
break;
105+
case 2:
106+
LockFreeStack(test, main_thread, __ATOMIC_RELAXED, __ATOMIC_RELEASE);
107+
break;
108+
case 3:
109+
LockFreeStack(test, main_thread, __ATOMIC_RELAXED, __ATOMIC_ACQ_REL);
110+
break;
111+
case 4:
112+
LockFreeStack(test, main_thread, __ATOMIC_RELAXED, __ATOMIC_SEQ_CST);
113+
break;
114+
case 5:
115+
LockFreeStack(test, main_thread, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
116+
break;
117+
case 6:
118+
LockFreeStack(test, main_thread, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
119+
break;
120+
case 7:
121+
LockFreeStack(test, main_thread, __ATOMIC_ACQUIRE, __ATOMIC_RELEASE);
122+
break;
123+
case 8:
124+
LockFreeStack(test, main_thread, __ATOMIC_ACQUIRE, __ATOMIC_ACQ_REL);
125+
break;
126+
case 9:
127+
LockFreeStack(test, main_thread, __ATOMIC_ACQUIRE, __ATOMIC_SEQ_CST);
128+
break;
129+
case 10:
130+
LockFreeStack(test, main_thread, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
131+
break;
132+
case 11:
133+
LockFreeStack(test, main_thread, __ATOMIC_RELEASE, __ATOMIC_RELEASE);
134+
break;
135+
case 12:
136+
LockFreeStack(test, main_thread, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
137+
break;
138+
case 13:
139+
LockFreeStack(test, main_thread, __ATOMIC_ACQ_REL, __ATOMIC_RELEASE);
140+
break;
141+
case 14:
142+
LockFreeStack(test, main_thread, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);
143+
break;
144+
case 15:
145+
LockFreeStack(test, main_thread, __ATOMIC_SEQ_CST, __ATOMIC_RELEASE);
146+
break;
147+
#else
148+
case 0:
149+
LockFreeStack(test, main_thread, __ATOMIC_RELEASE, __ATOMIC_ACQUIRE);
150+
break;
151+
case 1:
152+
LockFreeStack(test, main_thread, __ATOMIC_RELEASE, __ATOMIC_ACQ_REL);
153+
break;
154+
case 2:
155+
LockFreeStack(test, main_thread, __ATOMIC_RELEASE, __ATOMIC_SEQ_CST);
156+
break;
157+
case 3:
158+
LockFreeStack(test, main_thread, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
159+
break;
160+
case 4:
161+
LockFreeStack(test, main_thread, __ATOMIC_ACQ_REL, __ATOMIC_ACQ_REL);
162+
break;
163+
case 5:
164+
LockFreeStack(test, main_thread, __ATOMIC_ACQ_REL, __ATOMIC_SEQ_CST);
165+
break;
166+
case 6:
167+
LockFreeStack(test, main_thread, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE);
168+
break;
169+
case 7:
170+
LockFreeStack(test, main_thread, __ATOMIC_SEQ_CST, __ATOMIC_ACQ_REL);
171+
break;
172+
case 8:
173+
LockFreeStack(test, main_thread, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
174+
break;
175+
#endif
176+
}
177+
}
178+
}
179+
180+
void *Thread(void *p) {
181+
Test(false);
182+
return 0;
183+
}
184+
185+
int main() {
186+
barrier_init(&barrier, kThreadCount);
187+
pthread_t t[kThreadCount - 1];
188+
for (int i = 0; i < kThreadCount - 1; ++i)
189+
pthread_create(t + i, 0, Thread, (void *)(uintptr_t)(i + 1));
190+
Test(true);
191+
for (int i = 0; i < kThreadCount - 1; ++i)
192+
pthread_join(t[i], 0);
193+
}
194+
195+
// No race tests
196+
// CHECK-NOT: ThreadSanitizer: data race
197+
198+
// Race tests
199+
// 30 is the number of race reports for 15 possible combinations of mo1 and mo3
200+
// CHECK-RACE: Test 0
201+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
202+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
203+
// CHECK-RACE: Test 1
204+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
205+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
206+
// CHECK-RACE: Test 2
207+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
208+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
209+
// CHECK-RACE: Test 3
210+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
211+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
212+
// CHECK-RACE: Test 4
213+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
214+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
215+
// CHECK-RACE: Test 5
216+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
217+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
218+
// CHECK-RACE: Test 6
219+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
220+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
221+
// CHECK-RACE: Test 7
222+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
223+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
224+
// CHECK-RACE: Test 8
225+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
226+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
227+
// CHECK-RACE: Test 9
228+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
229+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
230+
// CHECK-RACE: Test 10
231+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
232+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
233+
// CHECK-RACE: Test 11
234+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
235+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
236+
// CHECK-RACE: Test 12
237+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
238+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
239+
// CHECK-RACE: Test 13
240+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
241+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
242+
// CHECK-RACE: Test 14
243+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
244+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race
245+
// CHECK-RACE: Test 15
246+
// CHECK-RACE-COUNT-30: SUMMARY: ThreadSanitizer: data race
247+
// CHECK-RACE-NOT: SUMMARY: ThreadSanitizer: data race

0 commit comments

Comments
 (0)