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_ppc64be) || defined(VGA_ppc64le)
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_arm64)
171
172 // arm64
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 UWord old, success;
177 UWord block[2] = { (UWord)addr, nyu };
178
179 /* Fetch the old value, and set the reservation */
180 __asm__ __volatile__ (
181 "ldxr %0, [%1]" "\n"
182 : /*out*/ "=r"(old)
183 : /*in*/ "r"(addr)
184 );
185
186 /* If the old value isn't as expected, we've had it */
187 if (old != expected) return 0;
188
189 /* otherwise try to stuff the new value in */
190 __asm__ __volatile__(
191 "ldr x4, [%1, #0]" "\n\t"
192 "ldr x5, [%1, #8]" "\n\t"
193 "stxr w6, x5, [x4, #0]" "\n\t"
194 "eor %0, x6, #1" "\n\t"
195 : /*out*/ "=r"(success)
196 : /*in*/ "r"(&block[0])
197 : /*trash*/ "x4","x5","x6","memory"
198 );
199 assert(success == 0 || success == 1);
200 return success;
201 }
202
203 #elif defined(VGA_s390x)
204
205 // s390x
206 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)207 UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
208 {
209 int cc;
210
211 __asm__ __volatile__ (
212 "csg %2,%3,%1\n\t"
213 "ipm %0\n\t"
214 "srl %0,28\n\t"
215 : /* out */ "=r" (cc)
216 : /* in */ "Q" (*addr), "d" (expected), "d" (nyu)
217 : "memory", "cc"
218 );
219 return cc == 0;
220 }
221
222 #elif defined(VGA_mips32)
223
224 // mips32
225 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)226 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
227 {
228 UWord success;
229 UWord block[3] = { (UWord)addr, nyu, expected};
230
231 __asm__ __volatile__(
232 ".set noreorder" "\n\t"
233 "lw $t0, 0(%1)" "\n\t"
234 "lw $t2, 8(%1)" "\n\t"
235 "lw $t3, 4(%1)" "\n\t"
236 "ll $t1, 0($t0)" "\n\t"
237 "bne $t1, $t2, exit_0" "\n\t"
238 "nop" "\n\t"
239 "sc $t3, 0($t0)" "\n\t"
240 "move %0, $t3" "\n\t"
241 "b exit" "\n\t"
242 "nop" "\n\t"
243 "exit_0:" "\n\t"
244 "move %0, $zero" "\n\t"
245 "exit:" "\n\t"
246 : /*out*/ "=r"(success)
247 : /*in*/ "r"(&block[0])
248 : /*trash*/ "t0", "t1", "t2", "t3", "memory"
249 );
250
251 assert(success == 0 || success == 1);
252 return success;
253 }
254
255 #elif defined(VGA_mips64)
256
257 // mips64
258 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)259 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
260 {
261 UWord success;
262 UWord block[3] = { (UWord)addr, nyu, expected};
263
264 __asm__ __volatile__(
265 ".set noreorder" "\n\t"
266 "ld $t0, 0(%1)" "\n\t"
267 "ld $t2, 16(%1)" "\n\t"
268 "ld $t3, 8(%1)" "\n\t"
269 "ll $t1, 0($t0)" "\n\t"
270 "bne $t1, $t2, exit_0" "\n\t"
271 "nop" "\n\t"
272 "sc $t3, 0($t0)" "\n\t"
273 "move %0, $t3" "\n\t"
274 "b exit" "\n\t"
275 "nop" "\n\t"
276 "exit_0:" "\n\t"
277 "move %0, $zero" "\n\t"
278 "exit:" "\n\t"
279 : /*out*/ "=r"(success)
280 : /*in*/ "r"(&block[0])
281 : /*trash*/ "t0", "t1", "t2", "t3", "memory"
282 );
283
284 assert(success == 0 || success == 1);
285 return success;
286 }
287
288 #elif defined(VGA_tilegx)
289
290 /* return 1 if success, 0 if failure */
do_acasW(UWord * addr,UWord expected,UWord nyu)291 UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
292 {
293 /* Load the compare value into special register 0x2780 */
294 __insn_mtspr(0x2780, expected);
295 return __insn_cmpexch(addr, nyu);
296 }
297
298 #endif
299
atomic_incW(UWord * w)300 void atomic_incW ( UWord* w )
301 {
302 while (1) {
303 UWord old = *w;
304 UWord nyu = old + 1;
305 UWord ok = do_acasW( w, old, nyu );
306 if (ok) break;
307 };
308 }
309
310 #if 0
311
312 #define NNN 1000000
313
314 void* thread_fn ( void* arg )
315 {
316 UWord* w = (UWord*)arg;
317 int i;
318 for (i = 0; i < NNN; i++)
319 atomic_incW( w );
320 return NULL;
321 }
322
323
324 int main ( void )
325 {
326 int r;
327 //ANNOTATE_HAPPENS_BEFORE(0);
328 //return 0;
329 UWord w = 0;
330 pthread_t t1, t2;
331
332 r= pthread_create( &t1, NULL, &thread_fn, (void*)&w ); assert(!r);
333 r= pthread_create( &t2, NULL, &thread_fn, (void*)&w ); assert(!r);
334
335 r= pthread_join( t1, NULL ); assert(!r);
336 r= pthread_join( t2, NULL ); assert(!r);
337
338 printf("result = %lu\n", w );
339 return 0;
340 }
341
342 #endif
343
344 int shared_var = 0; // is not raced upon
345
346
delayXms(int i)347 void delayXms ( int i )
348 {
349 struct timespec ts = { 0, 1 * 1000 * 1000 };
350 // We do the sleep in small pieces to have scheduling
351 // events ensuring a fair switch between threads, even
352 // without --fair-sched=yes. This is a.o. needed for
353 // running this test under an outer helgrind or an outer
354 // sgcheck.
355 while (i > 0) {
356 nanosleep(&ts, NULL);
357 i--;
358 }
359 }
360
do_wait(UWord * w)361 void do_wait ( UWord* w )
362 {
363 UWord w0 = *w;
364 UWord volatile * wV = w;
365 while (*wV == w0)
366 delayXms(1); // small sleeps, ensuring context switches
367 ANNOTATE_HAPPENS_AFTER(w);
368 }
369
do_signal(UWord * w)370 void do_signal ( UWord* w )
371 {
372 ANNOTATE_HAPPENS_BEFORE(w);
373 atomic_incW(w);
374 }
375
376
377
thread_fn1(void * arg)378 void* thread_fn1 ( void* arg )
379 {
380 UWord* w = (UWord*)arg;
381 delayXms(500); // ensure t2 gets to its wait first
382 shared_var = 1; // first access
383 do_signal(w); // cause h-b edge to second thread
384
385 delayXms(500);
386 return NULL;
387 }
388
thread_fn2(void * arg)389 void* thread_fn2 ( void* arg )
390 {
391 UWord* w = (UWord*)arg;
392 do_wait(w); // wait for h-b edge from first thread
393 shared_var = 2; // second access
394
395 delayXms(500);
396 return NULL;
397 }
398
399
400
401
402
403
main(void)404 int main ( void )
405 {
406 int r;
407 UWord w = 0;
408 pthread_t t1, t2;
409
410 r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w ); assert(!r);
411 r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w ); assert(!r);
412
413 r= pthread_join( t1, NULL ); assert(!r);
414 r= pthread_join( t2, NULL ); assert(!r);
415 return 0;
416 }
417