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 21typedef unsigned long int UWord; 22 23#if defined(VGA_ppc64) 24 25// ppc64 26/* return 1 if success, 0 if failure */ 27UWord 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 */ 60UWord 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 */ 93UWord 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 */ 116UWord 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 */ 141UWord 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 */ 174UWord 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 */ 193UWord 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 223void 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 237void* 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 247int 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 267int shared_var = 0; // is not raced upon 268 269 270void 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 284void 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 293void do_signal ( UWord* w ) 294{ 295 ANNOTATE_HAPPENS_BEFORE(w); 296 atomic_incW(w); 297} 298 299 300 301void* 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 312void* 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 327int 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