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