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