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