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#endif 190 191void atomic_incW ( UWord* w ) 192{ 193 while (1) { 194 UWord old = *w; 195 UWord nyu = old + 1; 196 UWord ok = do_acasW( w, old, nyu ); 197 if (ok) break; 198 }; 199} 200 201#if 0 202 203#define NNN 1000000 204 205void* thread_fn ( void* arg ) 206{ 207 UWord* w = (UWord*)arg; 208 int i; 209 for (i = 0; i < NNN; i++) 210 atomic_incW( w ); 211 return NULL; 212} 213 214 215int main ( void ) 216{ 217 int r; 218 //ANNOTATE_HAPPENS_BEFORE(0); 219 //return 0; 220 UWord w = 0; 221 pthread_t t1, t2; 222 223 r= pthread_create( &t1, NULL, &thread_fn, (void*)&w ); assert(!r); 224 r= pthread_create( &t2, NULL, &thread_fn, (void*)&w ); assert(!r); 225 226 r= pthread_join( t1, NULL ); assert(!r); 227 r= pthread_join( t2, NULL ); assert(!r); 228 229 printf("result = %lu\n", w ); 230 return 0; 231} 232 233#endif 234 235int shared_var = 0; // is not raced upon 236 237 238void delay500ms ( void ) 239{ 240 struct timespec ts = { 0, 500 * 1000 * 1000 }; 241 nanosleep(&ts, NULL); 242} 243 244void do_wait ( UWord* w ) 245{ 246 UWord w0 = *w; 247 UWord volatile * wV = w; 248 while (*wV == w0) 249 ; 250 ANNOTATE_HAPPENS_AFTER(w); 251} 252 253void do_signal ( UWord* w ) 254{ 255 ANNOTATE_HAPPENS_BEFORE(w); 256 atomic_incW(w); 257} 258 259 260 261void* thread_fn1 ( void* arg ) 262{ 263 UWord* w = (UWord*)arg; 264 delay500ms(); // ensure t2 gets to its wait first 265 shared_var = 1; // first access 266 do_signal(w); // cause h-b edge to second thread 267 268 delay500ms(); 269 return NULL; 270} 271 272void* thread_fn2 ( void* arg ) 273{ 274 UWord* w = (UWord*)arg; 275 do_wait(w); // wait for h-b edge from first thread 276 shared_var = 2; // second access 277 278 delay500ms(); 279 return NULL; 280} 281 282 283 284 285 286 287int main ( void ) 288{ 289 int r; 290 UWord w = 0; 291 pthread_t t1, t2; 292 293 r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w ); assert(!r); 294 r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w ); assert(!r); 295 296 r= pthread_join( t1, NULL ); assert(!r); 297 r= pthread_join( t2, NULL ); assert(!r); 298 return 0; 299} 300