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