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 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 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 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 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 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 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 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