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
rwlock_init(rwlock_t * p)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
rwlock_destroy(rwlock_t * p)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
rwlock_rdlock(rwlock_t * p)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
rwlock_wrlock(rwlock_t * p)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
rwlock_unlock(rwlock_t * p)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
thread_func(void * arg)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
main(int argc,char ** argv)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