Home | History | Annotate | Download | only in tests
      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_ppc64be) || defined(VGA_ppc64le)
     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 #elif defined(VGA_arm64)
    171 
    172 // arm64
    173 /* return 1 if success, 0 if failure */
    174 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
    175 {
    176   UWord old, success;
    177   UWord block[2] = { (UWord)addr, nyu };
    178 
    179   /* Fetch the old value, and set the reservation */
    180   __asm__ __volatile__ (
    181      "ldxr  %0, [%1]"    "\n"
    182       : /*out*/   "=r"(old)
    183       : /*in*/    "r"(addr)
    184    );
    185 
    186    /* If the old value isn't as expected, we've had it */
    187    if (old != expected) return 0;
    188 
    189    /* otherwise try to stuff the new value in */
    190    __asm__ __volatile__(
    191       "ldr    x4, [%1, #0]"      "\n\t"
    192       "ldr    x5, [%1, #8]"      "\n\t"
    193       "stxr   w6, x5, [x4, #0]"  "\n\t"
    194       "eor    %0, x6, #1"        "\n\t"
    195       : /*out*/ "=r"(success)
    196       : /*in*/ "r"(&block[0])
    197       : /*trash*/ "x4","x5","x6","memory"
    198    );
    199    assert(success == 0 || success == 1);
    200    return success;
    201 }
    202 
    203 #elif defined(VGA_s390x)
    204 
    205 // s390x
    206 /* return 1 if success, 0 if failure */
    207 UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
    208 {
    209    int cc;
    210 
    211    __asm__ __volatile__ (
    212      "csg %2,%3,%1\n\t"
    213      "ipm %0\n\t"
    214      "srl %0,28\n\t"
    215      : /* out */  "=r" (cc)
    216      : /* in */ "Q" (*addr), "d" (expected), "d" (nyu)
    217      : "memory", "cc"
    218    );
    219    return cc == 0;
    220 }
    221 
    222 #elif defined(VGA_mips32)
    223 
    224 // mips32
    225 /* return 1 if success, 0 if failure */
    226 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
    227 {
    228   UWord success;
    229   UWord block[3] = { (UWord)addr, nyu, expected};
    230 
    231    __asm__ __volatile__(
    232       ".set noreorder"           "\n\t"
    233       "lw     $t0, 0(%1)"        "\n\t"
    234       "lw     $t2, 8(%1)"        "\n\t"
    235       "lw     $t3, 4(%1)"        "\n\t"
    236       "ll     $t1, 0($t0)"       "\n\t"
    237       "bne    $t1, $t2, exit_0"  "\n\t"
    238       "nop"                      "\n\t"
    239       "sc     $t3, 0($t0)"       "\n\t"
    240       "move   %0, $t3"           "\n\t"
    241       "b exit"                   "\n\t"
    242       "nop"                      "\n\t"
    243       "exit_0:"                  "\n\t"
    244       "move   %0, $zero"         "\n\t"
    245       "exit:"                    "\n\t"
    246       : /*out*/ "=r"(success)
    247       : /*in*/ "r"(&block[0])
    248       : /*trash*/ "t0", "t1", "t2", "t3", "memory"
    249    );
    250 
    251    assert(success == 0 || success == 1);
    252    return success;
    253 }
    254 
    255 #elif defined(VGA_mips64)
    256 
    257 // mips64
    258 /* return 1 if success, 0 if failure */
    259 UWord do_acasW ( UWord* addr, UWord expected, UWord nyu )
    260 {
    261   UWord success;
    262   UWord block[3] = { (UWord)addr, nyu, expected};
    263 
    264    __asm__ __volatile__(
    265       ".set noreorder"           "\n\t"
    266       "ld     $t0, 0(%1)"        "\n\t"
    267       "ld     $t2, 16(%1)"       "\n\t"
    268       "ld     $t3, 8(%1)"        "\n\t"
    269       "ll     $t1, 0($t0)"       "\n\t"
    270       "bne    $t1, $t2, exit_0"  "\n\t"
    271       "nop"                      "\n\t"
    272       "sc     $t3, 0($t0)"       "\n\t"
    273       "move   %0, $t3"           "\n\t"
    274       "b exit"                   "\n\t"
    275       "nop"                      "\n\t"
    276       "exit_0:"                  "\n\t"
    277       "move   %0, $zero"         "\n\t"
    278       "exit:"                    "\n\t"
    279       : /*out*/ "=r"(success)
    280       : /*in*/ "r"(&block[0])
    281       : /*trash*/ "t0", "t1", "t2", "t3", "memory"
    282    );
    283 
    284    assert(success == 0 || success == 1);
    285    return success;
    286 }
    287 
    288 #elif defined(VGA_tilegx)
    289 
    290 /* return 1 if success, 0 if failure */
    291 UWord do_acasW(UWord* addr, UWord expected, UWord nyu )
    292 {
    293   /* Load the compare value into special register 0x2780 */
    294   __insn_mtspr(0x2780, expected);
    295   return __insn_cmpexch(addr, nyu);
    296 }
    297 
    298 #endif
    299 
    300 void atomic_incW ( UWord* w )
    301 {
    302    while (1) {
    303       UWord old = *w;
    304       UWord nyu = old + 1;
    305       UWord ok = do_acasW( w, old, nyu );
    306       if (ok) break;
    307    };
    308 }
    309 
    310 #if 0
    311 
    312 #define NNN 1000000
    313 
    314 void* thread_fn ( void* arg )
    315 {
    316   UWord* w = (UWord*)arg;
    317   int i;
    318   for (i = 0; i < NNN; i++)
    319      atomic_incW( w );
    320   return NULL;
    321 }
    322 
    323 
    324 int main ( void )
    325 {
    326    int r;
    327   //ANNOTATE_HAPPENS_BEFORE(0);
    328   //return 0;
    329    UWord w = 0;
    330   pthread_t t1, t2;
    331 
    332   r= pthread_create( &t1, NULL, &thread_fn, (void*)&w );   assert(!r);
    333   r= pthread_create( &t2, NULL, &thread_fn, (void*)&w );   assert(!r);
    334 
    335   r= pthread_join( t1, NULL );   assert(!r);
    336   r= pthread_join( t2, NULL );   assert(!r);
    337 
    338   printf("result = %lu\n", w );
    339   return 0;
    340 }
    341 
    342 #endif
    343 
    344 int shared_var = 0;  // is not raced upon
    345 
    346 
    347 void delayXms ( int i )
    348 {
    349    struct timespec ts = { 0, 1 * 1000 * 1000 };
    350    // We do the sleep in small pieces to have scheduling
    351    // events ensuring a fair switch between threads, even
    352    // without --fair-sched=yes. This is a.o. needed for
    353    // running this test under an outer helgrind or an outer
    354    // sgcheck.
    355    while (i > 0) {
    356       nanosleep(&ts, NULL);
    357       i--;
    358    }
    359 }
    360 
    361 void do_wait ( UWord* w )
    362 {
    363   UWord w0 = *w;
    364   UWord volatile * wV = w;
    365   while (*wV == w0)
    366     delayXms(1); // small sleeps, ensuring context switches
    367   ANNOTATE_HAPPENS_AFTER(w);
    368 }
    369 
    370 void do_signal ( UWord* w )
    371 {
    372   ANNOTATE_HAPPENS_BEFORE(w);
    373   atomic_incW(w);
    374 }
    375 
    376 
    377 
    378 void* thread_fn1 ( void* arg )
    379 {
    380   UWord* w = (UWord*)arg;
    381   delayXms(500);    // ensure t2 gets to its wait first
    382   shared_var = 1;  // first access
    383   do_signal(w);    // cause h-b edge to second thread
    384 
    385   delayXms(500);
    386   return NULL;
    387 }
    388 
    389 void* thread_fn2 ( void* arg )
    390 {
    391   UWord* w = (UWord*)arg;
    392   do_wait(w);      // wait for h-b edge from first thread
    393   shared_var = 2;  // second access
    394 
    395   delayXms(500);
    396   return NULL;
    397 }
    398 
    399 
    400 
    401 
    402 
    403 
    404 int main ( void )
    405 {
    406    int r;
    407    UWord w = 0;
    408    pthread_t t1, t2;
    409 
    410    r= pthread_create( &t1, NULL, &thread_fn1, (void*)&w );   assert(!r);
    411    r= pthread_create( &t2, NULL, &thread_fn2, (void*)&w );   assert(!r);
    412 
    413    r= pthread_join( t1, NULL );   assert(!r);
    414    r= pthread_join( t2, NULL );   assert(!r);
    415    return 0;
    416 }
    417