Home | History | Annotate | Download | only in tests
      1 
      2 /* This program is a marginally modified copy of
      3    drd/tests/annotate_rwlock.c,
      4 
      5    which was originally written by Bart van Assche.
      6 
      7    Unfortunately due to the need to #include helgrind.h instead of
      8    drd.h, it can't be an exact copy.
      9 */
     10 
     11 /**
     12  * @file  annotate_rwlock.c
     13  *
     14  * @brief Multithreaded test program that triggers various access patterns
     15  *        without triggering any race conditions using a reader-writer lock
     16  *        implemented via busy-waiting. Annotations are used to tell DRD
     17  *        which higher-level rwlock operations are being performed.
     18  */
     19 
     20 
     21 #define _GNU_SOURCE 1
     22 
     23 #include <assert.h>
     24 #include <pthread.h>
     25 #include <stdio.h>
     26 #include <unistd.h>        /* usleep() */
     27 #include "../../config.h"
     28 #include "../../helgrind/helgrind.h"
     29 
     30 
     31 #ifndef HAVE_BUILTIN_ATOMIC
     32 #error Sorry, but this test program can only be compiled by a compiler that\
     33 has built-in functions for atomic memory access.
     34 #endif
     35 
     36 
     37 typedef struct {
     38   volatile int locked;
     39   int          writer_count;
     40   int          reader_count;
     41 } rwlock_t;
     42 
     43 
     44 static rwlock_t s_rwlock;
     45 static int s_counter;
     46 
     47 
     48 static void rwlock_init(rwlock_t* p)
     49 {
     50   //  DRD_IGNORE_VAR(*p);
     51   p->locked       = 0;
     52   p->writer_count = 0;
     53   p->reader_count = 0;
     54   ANNOTATE_RWLOCK_CREATE(p);
     55 }
     56 
     57 static void rwlock_destroy(rwlock_t* p)
     58 {
     59   ANNOTATE_RWLOCK_DESTROY(p);
     60   assert(p->locked       == 0);
     61   assert(p->writer_count == 0);
     62   assert(p->reader_count == 0);
     63 }
     64 
     65 static void rwlock_rdlock(rwlock_t* p)
     66 {
     67   while (1)
     68   {
     69     while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
     70       ;
     71     if (p->writer_count == 0)
     72       break;
     73 #ifdef __APPLE__
     74     /* Darwin doesn't have an implementation of pthread_yield(). */
     75     usleep(100 * 1000);
     76 #else
     77     sched_yield();
     78 #endif
     79     (void) __sync_fetch_and_sub(&p->locked, 1);
     80   }
     81   p->reader_count++;
     82   assert(p->reader_count >= 0);
     83   assert(p->writer_count >= 0);
     84   assert(p->reader_count == 0 || p->writer_count == 0);
     85   (void) __sync_fetch_and_sub(&p->locked, 1);
     86   //ANNOTATE_READERLOCK_ACQUIRED(p);
     87   ANNOTATE_RWLOCK_ACQUIRED(p, 0);
     88 }
     89 
     90 static void rwlock_wrlock(rwlock_t* p)
     91 {
     92   while (1)
     93   {
     94     while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
     95       ;
     96     if (p->reader_count == 0)
     97       break;
     98 #ifdef __APPLE__
     99     /* Darwin doesn't have an implementation of pthread_yield(). */
    100     usleep(100 * 1000);
    101 #else
    102     sched_yield();
    103 #endif
    104     (void) __sync_fetch_and_sub(&p->locked, 1);
    105   }
    106   p->writer_count++;
    107   assert(p->reader_count >= 0);
    108   assert(p->writer_count >= 0);
    109   assert(p->reader_count == 0 || p->writer_count == 0);
    110   (void) __sync_fetch_and_sub(&p->locked, 1);
    111   //  ANNOTATE_WRITERLOCK_ACQUIRED(p);
    112   ANNOTATE_RWLOCK_ACQUIRED(p, 1);
    113 }
    114 
    115 static void rwlock_unlock(rwlock_t* p)
    116 {
    117   while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1)
    118     ;
    119   if (p->reader_count > 0)
    120   {
    121     p->reader_count--;
    122     //ANNOTATE_READERLOCK_RELEASED(p);
    123     ANNOTATE_RWLOCK_RELEASED(p, 0);
    124   }
    125   else
    126   {
    127     p->writer_count--;
    128     //ANNOTATE_WRITERLOCK_RELEASED(p);
    129     ANNOTATE_RWLOCK_RELEASED(p, 1);
    130   }
    131   assert(p->reader_count >= 0);
    132   assert(p->writer_count >= 0);
    133   assert(p->reader_count == 0 || p->writer_count == 0);
    134   (void) __sync_fetch_and_sub(&p->locked, 1);
    135 }
    136 
    137 static void* thread_func(void* arg)
    138 {
    139   int i;
    140   int sum = 0;
    141 
    142   for (i = 0; i < 1000; i++)
    143   {
    144     rwlock_rdlock(&s_rwlock);
    145     sum += s_counter;
    146     rwlock_unlock(&s_rwlock);
    147     rwlock_wrlock(&s_rwlock);
    148     s_counter++;
    149     rwlock_unlock(&s_rwlock);
    150   }
    151 
    152   return 0;
    153 }
    154 
    155 int main(int argc, char** argv)
    156 {
    157   const int thread_count = 10;
    158   pthread_t tid[thread_count];
    159   int i;
    160 
    161   rwlock_init(&s_rwlock);
    162   for (i = 0; i < thread_count; i++)
    163   {
    164     pthread_create(&tid[i], 0, thread_func, 0);
    165   }
    166 
    167   for (i = 0; i < thread_count; i++)
    168   {
    169     pthread_join(tid[i], 0);
    170   }
    171   rwlock_destroy(&s_rwlock);
    172 
    173   fprintf(stderr, "Finished.\n");
    174 
    175   return 0;
    176 }
    177