Home | History | Annotate | Download | only in tests
      1 
      2 /* FIXME: this is basically a bad test as it is scheduling-
      3    sensitive.  Sometimes the output is:
      4 
      5    child: new value 6
      6    child: new value 10
      7    done, x = 10
      8 
      9    and sometimes
     10 
     11    child: new value 10
     12    done, x = 10
     13 */
     14 
     15 #include <pthread.h>
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <unistd.h>
     19 
     20 /* Simple test program, no race.  Parent writes atomically to a counter
     21    whilst child reads it.  When counter reaches a prearranged value,
     22    child joins back to parent.  Parent (writer) uses hardware bus lock;
     23    child is only reading and so does not need to use a bus lock. */
     24 
     25 #undef PLAT_x86_darwin
     26 #undef PLAT_amd64_darwin
     27 #undef PLAT_x86_linux
     28 #undef PLAT_amd64_linux
     29 #undef PLAT_ppc32_linux
     30 #undef PLAT_ppc64_linux
     31 #undef PLAT_arm_linux
     32 #undef PLAT_s390x_linux
     33 #undef PLAT_mips32_linux
     34 
     35 #if defined(__APPLE__) && defined(__i386__)
     36 #  define PLAT_x86_darwin 1
     37 #elif defined(__APPLE__) && defined(__x86_64__)
     38 #  define PLAT_amd64_darwin 1
     39 #elif defined(__linux__) && defined(__i386__)
     40 #  define PLAT_x86_linux 1
     41 #elif defined(__linux__) && defined(__x86_64__)
     42 #  define PLAT_amd64_linux 1
     43 #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__)
     44 #  define PLAT_ppc32_linux 1
     45 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__)
     46 #  define PLAT_ppc64_linux 1
     47 #elif defined(__linux__) && defined(__arm__)
     48 #  define PLAT_arm_linux 1
     49 #elif defined(__linux__) && defined(__s390x__)
     50 #  define PLAT_s390x_linux 1
     51 #elif defined(__linux__) && defined(__mips__)
     52 #  define PLAT_mips32_linux 1
     53 #endif
     54 
     55 
     56 #if defined(PLAT_amd64_linux) || defined(PLAT_x86_linux) \
     57     || defined(PLAT_amd64_darwin) || defined(PLAT_x86_darwin)
     58 #  define INC(_lval,_lqual)	     \
     59       __asm__ __volatile__ ( \
     60       "lock ; incl (%0)" : /*out*/ : /*in*/"r"(&(_lval)) : "memory", "cc" )
     61 #elif defined(PLAT_ppc32_linux) || defined(PLAT_ppc64_linux)
     62 #  define INC(_lval,_lqual)		  \
     63    __asm__ __volatile__(                  \
     64       "1:\n"                              \
     65       "        lwarx 15,0,%0\n"           \
     66       "        addi 15,15,1\n"            \
     67       "        stwcx. 15,0,%0\n"          \
     68       "        bne- 1b\n"                 \
     69       : /*out*/ : /*in*/ "b"(&(_lval))    \
     70       : /*trash*/ "r15", "cr0", "memory"  \
     71    )
     72 #elif defined(PLAT_arm_linux)
     73 #  define INC(_lval,_lqual) \
     74   __asm__ __volatile__( \
     75       "1:\n"                                 \
     76       "        ldrex r8, [%0, #0]\n"         \
     77       "        add   r8, r8, #1\n"           \
     78       "        strex r9, r8, [%0, #0]\n"     \
     79       "        cmp   r9, #0\n"               \
     80       "        bne   1b\n"                   \
     81       : /*out*/ : /*in*/ "r"(&(_lval))       \
     82       : /*trash*/ "r8", "r9", "cc", "memory" \
     83   );
     84 #elif defined(PLAT_s390x_linux)
     85 #  define INC(_lval,_lqual) \
     86    __asm__ __volatile__( \
     87       "1: l     0,%0\n"                            \
     88       "   lr    1,0\n"                             \
     89       "   ahi   1,1\n"                             \
     90       "   cs    0,1,%0\n"                          \
     91       "   jl    1b\n"                              \
     92       : "+m" (_lval) :: "cc", "0","1" \
     93    )
     94 #elif defined(PLAT_mips32_linux)
     95 #  define INC(_lval,_lqual)                         \
     96      __asm__ __volatile__ (                         \
     97       "L1xyzzy1" _lqual":\n"                        \
     98       "        move $8, %0\n"                       \
     99       "        ll $9, 0($t0)\n"                     \
    100       "        addi $9, $9, 1\n"                    \
    101       "        sc $9, 0($t0)\n"                     \
    102       "        li $10, 1\n"                         \
    103       "        bne $9, $10, L1xyzzy1" _lqual        \
    104       : /*out*/ : /*in*/ "r"(&(_lval))              \
    105       : /*trash*/ "$8", "$9", "$10", "cc", "memory" \
    106         )
    107 #else
    108 #  error "Fix Me for this platform"
    109 #endif
    110 
    111 
    112 
    113 #define LIMIT 10
    114 
    115 volatile int x = 0;
    116 
    117 void* child_fn ( void* arg )
    118 {
    119    int q = 0;
    120    int oldx = 0;
    121    struct timespec ts = { 0, 1000 * 1000 };
    122 
    123    while (1) {
    124       q = (x >= LIMIT);
    125       if (x != oldx) {
    126          oldx = x;
    127          printf("child: new value %d\n", oldx);
    128          fflush(stdout);
    129       }
    130       if (q) break;
    131       nanosleep(&ts, 0);
    132    }
    133    return NULL;
    134 }
    135 
    136 int main ( void )
    137 {
    138    pthread_t child;
    139    int i;
    140 
    141    if (pthread_create(&child, NULL, child_fn, NULL)) {
    142       perror("pthread_create");
    143       exit(1);
    144    }
    145 
    146    for (i = 0; i < LIMIT; i++) {
    147       INC(x, "main");
    148       if (i == 5) sleep(1); /* make sure child doesn't starve */
    149    }
    150 
    151    if (pthread_join(child, NULL)) {
    152       perror("pthread join");
    153       exit(1);
    154    }
    155 
    156    printf("done, x = %d\n", x);
    157 
    158    return 0;
    159 }
    160