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\
33has built-in functions for atomic memory access.
34#endif
35
36
37typedef struct {
38  volatile int locked;
39  int          writer_count;
40  int          reader_count;
41} rwlock_t;
42
43
44static rwlock_t s_rwlock;
45static int s_counter;
46
47
48static 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
57static 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
65static 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    pthread_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
90static 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    pthread_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
115static 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
137static 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
155int 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