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