| |
| /* This program is a marginally modified copy of |
| drd/tests/annotate_rwlock.c, |
| |
| which was originally written by Bart van Assche. |
| |
| Unfortunately due to the need to #include helgrind.h instead of |
| drd.h, it can't be an exact copy. |
| */ |
| |
| /** |
| * @file annotate_rwlock.c |
| * |
| * @brief Multithreaded test program that triggers various access patterns |
| * without triggering any race conditions using a reader-writer lock |
| * implemented via busy-waiting. Annotations are used to tell DRD |
| * which higher-level rwlock operations are being performed. |
| */ |
| |
| |
| #define _GNU_SOURCE 1 |
| |
| #include <assert.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <unistd.h> /* usleep() */ |
| #include "../../config.h" |
| #include "../../helgrind/helgrind.h" |
| |
| |
| #ifndef HAVE_BUILTIN_ATOMIC |
| #error Sorry, but this test program can only be compiled by a compiler that\ |
| has built-in functions for atomic memory access. |
| #endif |
| |
| |
| typedef struct { |
| volatile int locked; |
| int writer_count; |
| int reader_count; |
| } rwlock_t; |
| |
| |
| static rwlock_t s_rwlock; |
| static int s_counter; |
| |
| |
| static void rwlock_init(rwlock_t* p) |
| { |
| // DRD_IGNORE_VAR(*p); |
| p->locked = 0; |
| p->writer_count = 0; |
| p->reader_count = 0; |
| ANNOTATE_RWLOCK_CREATE(p); |
| } |
| |
| static void rwlock_destroy(rwlock_t* p) |
| { |
| ANNOTATE_RWLOCK_DESTROY(p); |
| assert(p->locked == 0); |
| assert(p->writer_count == 0); |
| assert(p->reader_count == 0); |
| } |
| |
| static void rwlock_rdlock(rwlock_t* p) |
| { |
| while (1) |
| { |
| while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1) |
| ; |
| if (p->writer_count == 0) |
| break; |
| #ifdef __APPLE__ |
| /* Darwin doesn't have an implementation of pthread_yield(). */ |
| usleep(100 * 1000); |
| #else |
| sched_yield(); |
| #endif |
| (void) __sync_fetch_and_sub(&p->locked, 1); |
| } |
| p->reader_count++; |
| assert(p->reader_count >= 0); |
| assert(p->writer_count >= 0); |
| assert(p->reader_count == 0 || p->writer_count == 0); |
| (void) __sync_fetch_and_sub(&p->locked, 1); |
| //ANNOTATE_READERLOCK_ACQUIRED(p); |
| ANNOTATE_RWLOCK_ACQUIRED(p, 0); |
| } |
| |
| static void rwlock_wrlock(rwlock_t* p) |
| { |
| while (1) |
| { |
| while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1) |
| ; |
| if (p->reader_count == 0) |
| break; |
| #ifdef __APPLE__ |
| /* Darwin doesn't have an implementation of pthread_yield(). */ |
| usleep(100 * 1000); |
| #else |
| sched_yield(); |
| #endif |
| (void) __sync_fetch_and_sub(&p->locked, 1); |
| } |
| p->writer_count++; |
| assert(p->reader_count >= 0); |
| assert(p->writer_count >= 0); |
| assert(p->reader_count == 0 || p->writer_count == 0); |
| (void) __sync_fetch_and_sub(&p->locked, 1); |
| // ANNOTATE_WRITERLOCK_ACQUIRED(p); |
| ANNOTATE_RWLOCK_ACQUIRED(p, 1); |
| } |
| |
| static void rwlock_unlock(rwlock_t* p) |
| { |
| while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1) |
| ; |
| if (p->reader_count > 0) |
| { |
| p->reader_count--; |
| //ANNOTATE_READERLOCK_RELEASED(p); |
| ANNOTATE_RWLOCK_RELEASED(p, 0); |
| } |
| else |
| { |
| p->writer_count--; |
| //ANNOTATE_WRITERLOCK_RELEASED(p); |
| ANNOTATE_RWLOCK_RELEASED(p, 1); |
| } |
| assert(p->reader_count >= 0); |
| assert(p->writer_count >= 0); |
| assert(p->reader_count == 0 || p->writer_count == 0); |
| (void) __sync_fetch_and_sub(&p->locked, 1); |
| } |
| |
| static void* thread_func(void* arg) |
| { |
| int i; |
| int sum = 0; |
| |
| for (i = 0; i < 1000; i++) |
| { |
| rwlock_rdlock(&s_rwlock); |
| sum += s_counter; |
| rwlock_unlock(&s_rwlock); |
| rwlock_wrlock(&s_rwlock); |
| s_counter++; |
| rwlock_unlock(&s_rwlock); |
| } |
| |
| return 0; |
| } |
| |
| int main(int argc, char** argv) |
| { |
| const int thread_count = 10; |
| pthread_t tid[thread_count]; |
| int i; |
| |
| rwlock_init(&s_rwlock); |
| for (i = 0; i < thread_count; i++) |
| { |
| pthread_create(&tid[i], 0, thread_func, 0); |
| } |
| |
| for (i = 0; i < thread_count; i++) |
| { |
| pthread_join(tid[i], 0); |
| } |
| rwlock_destroy(&s_rwlock); |
| |
| fprintf(stderr, "Finished.\n"); |
| |
| return 0; |
| } |