annotate_hbefore.c revision 9bea4c13fca0e3bb4b719dcb3ed63d47d479294e
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#endif
171
172void 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
186void* 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
196int 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
216int shared_var = 0;  // is not raced upon
217
218
219void delay100ms ( void )
220{
221   struct timespec ts = { 0, 100 * 1000 * 1000 };
222   nanosleep(&ts, NULL);
223}
224
225void 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
234void do_signal ( UWord* w )
235{
236  ANNOTATE_HAPPENS_BEFORE(w);
237  atomic_incW(w);
238}
239
240
241
242void* 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
253void* 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
268int 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