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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 #endif 289 290 void atomic_incW ( UWord* w ) 291 { 292 while (1) { 293 UWord old = *w; 294 UWord nyu = old + 1; 295 UWord ok = do_acasW( w, old, nyu ); 296 if (ok) break; 297 }; 298 } 299 300 #if 0 301 302 #define NNN 1000000 303 304 void* thread_fn ( void* arg ) 305 { 306 UWord* w = (UWord*)arg; 307 int i; 308 for (i = 0; i < NNN; i++) 309 atomic_incW( w ); 310 return NULL; 311 } 312 313 314 int main ( void ) 315 { 316 int r; 317 //ANNOTATE_HAPPENS_BEFORE(0); 318 //return 0; 319 UWord w = 0; 320 pthread_t t1, t2; 321 322 r= pthread_create( &t1, NULL, &thread_fn, (void*)&w ); assert(!r); 323 r= pthread_create( &t2, NULL, &thread_fn, (void*)&w ); assert(!r); 324 325 r= pthread_join( t1, NULL ); assert(!r); 326 r= pthread_join( t2, NULL ); assert(!r); 327 328 printf("result = %lu\n", w ); 329 return 0; 330 } 331 332 #endif 333 334 int shared_var = 0; // is not raced upon 335 336 337 void delayXms ( int i ) 338 { 339 struct timespec ts = { 0, 1 * 1000 * 1000 }; 340 // We do the sleep in small pieces to have scheduling 341 // events ensuring a fair switch between threads, even 342 // without --fair-sched=yes. This is a.o. needed for 343 // running this test under an outer helgrind or an outer 344 // sgcheck. 345 while (i > 0) { 346 nanosleep(&ts, NULL); 347 i--; 348 } 349 } 350 351 void do_wait ( UWord* w ) 352 { 353 UWord w0 = *w; 354 UWord volatile * wV = w; 355 while (*wV == w0) 356 delayXms(1); // small sleeps, ensuring context switches 357 ANNOTATE_HAPPENS_AFTER(w); 358 } 359 360 void do_signal ( UWord* w ) 361 { 362 ANNOTATE_HAPPENS_BEFORE(w); 363 atomic_incW(w); 364 } 365 366 367 368 void* thread_fn1 ( void* arg ) 369 { 370 UWord* w = (UWord*)arg; 371 delayXms(500); // ensure t2 gets to its wait first 372 shared_var = 1; // first access 373 do_signal(w); // cause h-b edge to second thread 374 375 delayXms(500); 376 return NULL; 377 } 378 379 void* thread_fn2 ( void* arg ) 380 { 381 UWord* w = (UWord*)arg; 382 do_wait(w); // wait for h-b edge from first thread 383 shared_var = 2; // second access 384 385 delayXms(500); 386 return NULL; 387 } 388 389 390 391 392 393 394 int main ( void ) 395 { 396 int r; 397 UWord w = 0; 398 pthread_t t1, t2; 399 400 r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w ); assert(!r); 401 r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w ); assert(!r); 402 403 r= pthread_join( t1, NULL ); assert(!r); 404 r= pthread_join( t2, NULL ); assert(!r); 405 return 0; 406 } 407