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