/* 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
pthread_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
pthread_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;
}