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#elif defined(VGA_mips32)
190
191// mips
192/* return 1 if success, 0 if failure */
193UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
194{
195  UWord old, success;
196  UWord block[3] = { (UWord)addr, nyu, expected};
197
198   __asm__ __volatile__(
199      ".set noreorder"           "\n\t"
200      "lw     $t0, 0(%1)"        "\n\t"
201      "lw     $t2, 8(%1)"        "\n\t"
202      "lw     $t3, 4(%1)"        "\n\t"
203      "ll     $t1, 0($t0)"       "\n\t"
204      "bne    $t1, $t2, exit_0"  "\n\t"
205      "sc     $t3, 0($t0)"       "\n\t"
206      "move   %0, $t3"           "\n\t"
207      "b exit"                   "\n\t"
208      "nop"                      "\n\t"
209      "exit_0:"                  "\n\t"
210      "move   %0, $0"            "\n\t"
211      "exit:"                     "\n\t"
212      : /*out*/ "=r"(success)
213      : /*in*/ "r"(&block[0])
214      : /*trash*/ "t0", "t1", "t2", "t3", "memory"
215   );
216
217   assert(success == 0 || success == 1);
218   return success;
219}
220
221#endif
222
223void atomic_incW ( UWord* w )
224{
225   while (1) {
226      UWord old = *w;
227      UWord nyu = old + 1;
228      UWord ok = do_acasW( w, old, nyu );
229      if (ok) break;
230   };
231}
232
233#if 0
234
235#define NNN 1000000
236
237void* thread_fn ( void* arg )
238{
239  UWord* w = (UWord*)arg;
240  int i;
241  for (i = 0; i < NNN; i++)
242     atomic_incW( w );
243  return NULL;
244}
245
246
247int main ( void )
248{
249   int r;
250  //ANNOTATE_HAPPENS_BEFORE(0);
251  //return 0;
252   UWord w = 0;
253  pthread_t t1, t2;
254
255  r= pthread_create( &t1, NULL, &thread_fn, (void*)&w );   assert(!r);
256  r= pthread_create( &t2, NULL, &thread_fn, (void*)&w );   assert(!r);
257
258  r= pthread_join( t1, NULL );   assert(!r);
259  r= pthread_join( t2, NULL );   assert(!r);
260
261  printf("result = %lu\n", w );
262  return 0;
263}
264
265#endif
266
267int shared_var = 0;  // is not raced upon
268
269
270void delayXms ( int i )
271{
272   struct timespec ts = { 0, 1 * 1000 * 1000 };
273   // We do the sleep in small pieces to have scheduling
274   // events ensuring a fair switch between threads, even
275   // without --fair-sched=yes. This is a.o. needed for
276   // running this test under an outer helgrind or an outer
277   // sgcheck.
278   while (i > 0) {
279      nanosleep(&ts, NULL);
280      i--;
281   }
282}
283
284void do_wait ( UWord* w )
285{
286  UWord w0 = *w;
287  UWord volatile * wV = w;
288  while (*wV == w0)
289    delayXms(1); // small sleeps, ensuring context switches
290  ANNOTATE_HAPPENS_AFTER(w);
291}
292
293void do_signal ( UWord* w )
294{
295  ANNOTATE_HAPPENS_BEFORE(w);
296  atomic_incW(w);
297}
298
299
300
301void* thread_fn1 ( void* arg )
302{
303  UWord* w = (UWord*)arg;
304  delayXms(500);    // ensure t2 gets to its wait first
305  shared_var = 1;  // first access
306  do_signal(w);    // cause h-b edge to second thread
307
308  delayXms(500);
309  return NULL;
310}
311
312void* thread_fn2 ( void* arg )
313{
314  UWord* w = (UWord*)arg;
315  do_wait(w);      // wait for h-b edge from first thread
316  shared_var = 2;  // second access
317
318  delayXms(500);
319  return NULL;
320}
321
322
323
324
325
326
327int main ( void )
328{
329   int r;
330   UWord w = 0;
331   pthread_t t1, t2;
332
333   r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w );   assert(!r);
334   r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w );   assert(!r);
335
336   r= pthread_join( t1, NULL );   assert(!r);
337   r= pthread_join( t2, NULL );   assert(!r);
338   return 0;
339}
340