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