1
2 /* Program which uses a happens-before edge to coordinate an access to
3 variable 'shared_var' between two threads. The h-b edge is created
4 by a custom (kludgesome!) mechanism and hence we need to use
5 ANNOTATES_HAPPEN_{BEFORE,AFTER} to explain to Helgrind what's going
6 on (else it reports a race). */
7
8 #include <pthread.h>
9 #include <stdio.h>
10 #include <assert.h>
11
12 #include "../../helgrind/helgrind.h"
13
14 /* Todo: move all this do_acasW guff into a support library. It's
15 useful for multiple tests, not just this one.
16
17 XXX: all the do_acasW routines assume the supplied address
18 is UWord (naturally) aligned. */
19
20
21 typedef unsigned long int UWord;
22
23 #if defined(VGA_ppc64)
24
25 // ppc64
26 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)27 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
28 {
29 UWord old, success;
30
31 /* Fetch the old value, and set the reservation */
32 __asm__ __volatile__ (
33 "ldarx %0, 0,%1" "\n" // rD,rA,rB
34 : /*out*/ "=b"(old)
35 : /*in*/ "b"(addr)
36 : /*trash*/ "memory","cc"
37 );
38
39 /* If the old value isn't as expected, we've had it */
40 if (old != expected) return 0;
41
42 /* otherwise try to stuff the new value in */
43 __asm__ __volatile__(
44 "stdcx. %2, 0,%1" "\n" // rS,rA,rB
45 "mfcr %0" "\n\t"
46 "srdi %0,%0,29" "\n\t"
47 "andi. %0,%0,1" "\n"
48 : /*out*/ "=b"(success)
49 : /*in*/ "b"(addr), "b"(nyu)
50 );
51
52 assert(success == 0 || success == 1);
53 return success;
54 }
55
56 #elif defined(VGA_ppc32)
57
58 // ppc32
59 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)60 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
61 {
62 UWord old, success;
63
64 /* Fetch the old value, and set the reservation */
65 __asm__ __volatile__ (
66 "lwarx %0, 0,%1" "\n" // rD,rA,rB
67 : /*out*/ "=b"(old)
68 : /*in*/ "b"(addr)
69 : /*trash*/ "memory","cc"
70 );
71
72 /* If the old value isn't as expected, we've had it */
73 if (old != expected) return 0;
74
75 /* otherwise try to stuff the new value in */
76 __asm__ __volatile__(
77 "stwcx. %2, 0,%1" "\n" // rS,rA,rB
78 "mfcr %0" "\n\t"
79 "srwi %0,%0,29" "\n\t"
80 "andi. %0,%0,1" "\n"
81 : /*out*/ "=b"(success)
82 : /*in*/ "b"(addr), "b"(nyu)
83 );
84
85 assert(success == 0 || success == 1);
86 return success;
87 }
88
89 #elif defined(VGA_amd64)
90
91 // amd64
92 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)93 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
94 {
95 UWord block[4] = { (UWord)addr, expected, nyu, 2 };
96 __asm__ __volatile__(
97 "movq 0(%%rsi), %%rdi" "\n\t" // addr
98 "movq 8(%%rsi), %%rax" "\n\t" // expected
99 "movq 16(%%rsi), %%rbx" "\n\t" // nyu
100 "xorq %%rcx,%%rcx" "\n\t"
101 "lock; cmpxchgq %%rbx,(%%rdi)" "\n\t"
102 "setz %%cl" "\n\t"
103 "movq %%rcx, 24(%%rsi)" "\n"
104 : /*out*/
105 : /*in*/ "S"(&block[0])
106 : /*trash*/"memory","cc","rdi","rax","rbx","rcx"
107 );
108 assert(block[3] == 0 || block[3] == 1);
109 return block[3] & 1;
110 }
111
112 #elif defined(VGA_x86)
113
114 // x86
115 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)116 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
117 {
118 UWord block[4] = { (UWord)addr, expected, nyu, 2 };
119 __asm__ __volatile__(
120 "pushl %%ebx" "\n\t"
121 "movl 0(%%esi), %%edi" "\n\t" // addr
122 "movl 4(%%esi), %%eax" "\n\t" // expected
123 "movl 8(%%esi), %%ebx" "\n\t" // nyu
124 "xorl %%ecx,%%ecx" "\n\t"
125 "lock; cmpxchgl %%ebx,(%%edi)" "\n\t"
126 "setz %%cl" "\n\t"
127 "movl %%ecx, 12(%%esi)" "\n\t"
128 "popl %%ebx" "\n"
129 : /*out*/
130 : /*in*/ "S"(&block[0])
131 : /*trash*/"memory","cc","edi","eax","ecx"
132 );
133 assert(block[3] == 0 || block[3] == 1);
134 return block[3] & 1;
135 }
136
137 #elif defined(VGA_arm)
138
139 // arm
140 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)141 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
142 {
143 UWord old, success;
144 UWord block[2] = { (UWord)addr, nyu };
145
146 /* Fetch the old value, and set the reservation */
147 __asm__ __volatile__ (
148 "ldrex %0, [%1]" "\n"
149 : /*out*/ "=r"(old)
150 : /*in*/ "r"(addr)
151 );
152
153 /* If the old value isn't as expected, we've had it */
154 if (old != expected) return 0;
155
156 /* otherwise try to stuff the new value in */
157 __asm__ __volatile__(
158 "ldr r4, [%1, #0]" "\n\t"
159 "ldr r5, [%1, #4]" "\n\t"
160 "strex r6, r5, [r4, #0]" "\n\t"
161 "eor %0, r6, #1" "\n\t"
162 : /*out*/ "=r"(success)
163 : /*in*/ "r"(&block[0])
164 : /*trash*/ "r4","r5","r6","memory"
165 );
166 assert(success == 0 || success == 1);
167 return success;
168 }
169
170 #elif defined(VGA_s390x)
171
172 // s390x
173 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)174 UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
175 {
176 int cc;
177
178 __asm__ __volatile__ (
179 "csg %2,%3,%1\n\t"
180 "ipm %0\n\t"
181 "srl %0,28\n\t"
182 : /* out */ "=r" (cc)
183 : /* in */ "Q" (*addr), "d" (expected), "d" (nyu)
184 : "memory", "cc"
185 );
186 return cc == 0;
187 }
188
189 #elif defined(VGA_mips32)
190
191 // mips
192 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)193 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
194 {
195 UWord old, success;
196 UWord block[3] = { (UWord)addr, nyu, expected};
197
198 __asm__ __volatile__(
199 ".set noreorder" "\n\t"
200 "lw $t0, 0(%1)" "\n\t"
201 "lw $t2, 8(%1)" "\n\t"
202 "lw $t3, 4(%1)" "\n\t"
203 "ll $t1, 0($t0)" "\n\t"
204 "bne $t1, $t2, exit_0" "\n\t"
205 "sc $t3, 0($t0)" "\n\t"
206 "move %0, $t3" "\n\t"
207 "b exit" "\n\t"
208 "nop" "\n\t"
209 "exit_0:" "\n\t"
210 "move %0, $0" "\n\t"
211 "exit:" "\n\t"
212 : /*out*/ "=r"(success)
213 : /*in*/ "r"(&block[0])
214 : /*trash*/ "t0", "t1", "t2", "t3", "memory"
215 );
216
217 assert(success == 0 || success == 1);
218 return success;
219 }
220
221 #endif
222
atomic_incW(UWord * w)223 void atomic_incW ( UWord* w )
224 {
225 while (1) {
226 UWord old = *w;
227 UWord nyu = old + 1;
228 UWord ok = do_acasW( w, old, nyu );
229 if (ok) break;
230 };
231 }
232
233 #if 0
234
235 #define NNN 1000000
236
237 void* thread_fn ( void* arg )
238 {
239 UWord* w = (UWord*)arg;
240 int i;
241 for (i = 0; i < NNN; i++)
242 atomic_incW( w );
243 return NULL;
244 }
245
246
247 int main ( void )
248 {
249 int r;
250 //ANNOTATE_HAPPENS_BEFORE(0);
251 //return 0;
252 UWord w = 0;
253 pthread_t t1, t2;
254
255 r= pthread_create( &t1, NULL, &thread_fn, (void*)&w ); assert(!r);
256 r= pthread_create( &t2, NULL, &thread_fn, (void*)&w ); assert(!r);
257
258 r= pthread_join( t1, NULL ); assert(!r);
259 r= pthread_join( t2, NULL ); assert(!r);
260
261 printf("result = %lu\n", w );
262 return 0;
263 }
264
265 #endif
266
267 int shared_var = 0; // is not raced upon
268
269
delayXms(int i)270 void delayXms ( int i )
271 {
272 struct timespec ts = { 0, 1 * 1000 * 1000 };
273 // We do the sleep in small pieces to have scheduling
274 // events ensuring a fair switch between threads, even
275 // without --fair-sched=yes. This is a.o. needed for
276 // running this test under an outer helgrind or an outer
277 // sgcheck.
278 while (i > 0) {
279 nanosleep(&ts, NULL);
280 i--;
281 }
282 }
283
do_wait(UWord * w)284 void do_wait ( UWord* w )
285 {
286 UWord w0 = *w;
287 UWord volatile * wV = w;
288 while (*wV == w0)
289 delayXms(1); // small sleeps, ensuring context switches
290 ANNOTATE_HAPPENS_AFTER(w);
291 }
292
do_signal(UWord * w)293 void do_signal ( UWord* w )
294 {
295 ANNOTATE_HAPPENS_BEFORE(w);
296 atomic_incW(w);
297 }
298
299
300
thread_fn1(void * arg)301 void* thread_fn1 ( void* arg )
302 {
303 UWord* w = (UWord*)arg;
304 delayXms(500); // ensure t2 gets to its wait first
305 shared_var = 1; // first access
306 do_signal(w); // cause h-b edge to second thread
307
308 delayXms(500);
309 return NULL;
310 }
311
thread_fn2(void * arg)312 void* thread_fn2 ( void* arg )
313 {
314 UWord* w = (UWord*)arg;
315 do_wait(w); // wait for h-b edge from first thread
316 shared_var = 2; // second access
317
318 delayXms(500);
319 return NULL;
320 }
321
322
323
324
325
326
main(void)327 int main ( void )
328 {
329 int r;
330 UWord w = 0;
331 pthread_t t1, t2;
332
333 r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w ); assert(!r);
334 r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w ); assert(!r);
335
336 r= pthread_join( t1, NULL ); assert(!r);
337 r= pthread_join( t2, NULL ); assert(!r);
338 return 0;
339 }
340