annotate_rwlock.c revision 0ccdc4a1e148ce89d99a4593b777c0fd9192bce0
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\
23has built-in functions for atomic memory access.
24#endif
25
26
27typedef struct {
28  volatile int locked;
29  int          writer_count;
30  int          reader_count;
31} rwlock_t;
32
33
34static rwlock_t s_rwlock;
35static int s_counter;
36
37
38static 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
47static 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
55static 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
79static 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
103static 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
123static 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
141int 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