/**
* @file rwlock_test.c
*
* @brief Multithreaded test program that triggers various access patterns
* without triggering any race conditions.
*/
#define _GNU_SOURCE 1
#include <assert.h>
#include <limits.h> /* PTHREAD_STACK_MIN */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h> /* malloc() */
#include <string.h> /* strerror() */
#include <unistd.h> /* getopt() */
static int s_num_threads = 10;
static int s_num_iterations = 1000;
static pthread_mutex_t s_mutex;
static long long s_grand_sum; /* protected by s_mutex. */
static pthread_rwlock_t s_rwlock;
static int s_counter; /* protected by s_rwlock. */
static void* thread_func(void* arg)
{
int i, r;
int sum1 = 0, sum2 = 0;
for (i = s_num_iterations; i > 0; i--)
{
r = pthread_rwlock_rdlock(&s_rwlock);
assert(! r);
sum1 += s_counter;
r = pthread_rwlock_unlock(&s_rwlock);
assert(! r);
r = pthread_rwlock_wrlock(&s_rwlock);
assert(! r);
sum2 += s_counter++;
r = pthread_rwlock_unlock(&s_rwlock);
assert(! r);
}
pthread_mutex_lock(&s_mutex);
s_grand_sum += sum2;
pthread_mutex_unlock(&s_mutex);
return 0;
}
int main(int argc, char** argv)
{
pthread_attr_t attr;
pthread_t* tid;
int threads_created;
int optchar;
int err;
int i;
int expected_counter;
long long expected_grand_sum;
while ((optchar = getopt(argc, argv, "i:t:")) != EOF)
{
switch (optchar)
{
case 'i':
s_num_iterations = atoi(optarg);
break;
case 't':
s_num_threads = atoi(optarg);
break;
default:
fprintf(stderr, "Error: unknown option '%c'.\n", optchar);
return 1;
}
}
pthread_mutex_init(&s_mutex, NULL);
pthread_rwlock_init(&s_rwlock, NULL);
pthread_attr_init(&attr);
err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + 4096);
assert(err == 0);
tid = calloc(s_num_threads, sizeof(*tid));
threads_created = 0;
for (i = 0; i < s_num_threads; i++)
{
err = pthread_create(&tid[i], &attr, thread_func, 0);
if (err)
printf("failed to create thread %d: %s\n", i, strerror(err));
else
threads_created++;
}
pthread_attr_destroy(&attr);
for (i = 0; i < s_num_threads; i++)
{
if (tid[i])
pthread_join(tid[i], 0);
}
free(tid);
expected_counter = threads_created * s_num_iterations;
fprintf(stderr, "s_counter - expected_counter = %d\n",
s_counter - expected_counter);
expected_grand_sum = 1ULL * expected_counter * (expected_counter - 1) / 2;
fprintf(stderr, "s_grand_sum - expected_grand_sum = %lld\n",
s_grand_sum - expected_grand_sum);
fprintf(stderr, "Finished.\n");
return 0;
}