• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
rwlock_init(rwlock_t * p)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 
rwlock_destroy(rwlock_t * p)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 
rwlock_rdlock(rwlock_t * p)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 
rwlock_wrlock(rwlock_t * p)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 
rwlock_unlock(rwlock_t * p)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 
thread_func(void * arg)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 
main(int argc,char ** argv)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