1 /**
2  * @file  rwlock_test.c
3  *
4  * @brief Multithreaded test program that triggers various access patterns
5  *        without triggering any race conditions.
6  */
7 
8 
9 #define _GNU_SOURCE 1
10 
11 #include <assert.h>
12 #include <limits.h>  /* PTHREAD_STACK_MIN */
13 #include <pthread.h>
14 #include <stdio.h>
15 #include <stdlib.h>  /* malloc() */
16 #include <string.h>  /* strerror() */
17 #include <unistd.h>  /* getopt() */
18 
19 static int s_num_threads = 10;
20 static int s_num_iterations = 1000;
21 static pthread_mutex_t s_mutex;
22 static long long s_grand_sum; /* protected by s_mutex. */
23 static pthread_rwlock_t s_rwlock;
24 static int s_counter; /* protected by s_rwlock. */
25 
thread_func(void * arg)26 static void* thread_func(void* arg)
27 {
28   int i, r;
29   int sum1 = 0, sum2 = 0;
30 
31   for (i = s_num_iterations; i > 0; i--)
32   {
33     r = pthread_rwlock_rdlock(&s_rwlock);
34     assert(! r);
35     sum1 += s_counter;
36     r = pthread_rwlock_unlock(&s_rwlock);
37     assert(! r);
38     r = pthread_rwlock_wrlock(&s_rwlock);
39     assert(! r);
40     sum2 += s_counter++;
41     r = pthread_rwlock_unlock(&s_rwlock);
42     assert(! r);
43   }
44 
45   pthread_mutex_lock(&s_mutex);
46   s_grand_sum += sum2;
47   pthread_mutex_unlock(&s_mutex);
48 
49   return 0;
50 }
51 
main(int argc,char ** argv)52 int main(int argc, char** argv)
53 {
54   pthread_attr_t attr;
55   pthread_t* tid;
56   int threads_created;
57   int optchar;
58   int err;
59   int i;
60   int expected_counter;
61   long long expected_grand_sum;
62 
63   while ((optchar = getopt(argc, argv, "i:t:")) != EOF)
64   {
65     switch (optchar)
66     {
67     case 'i':
68       s_num_iterations = atoi(optarg);
69       break;
70     case 't':
71       s_num_threads = atoi(optarg);
72       break;
73     default:
74       fprintf(stderr, "Error: unknown option '%c'.\n", optchar);
75       return 1;
76     }
77   }
78 
79   pthread_mutex_init(&s_mutex, NULL);
80   pthread_rwlock_init(&s_rwlock, NULL);
81 
82   pthread_attr_init(&attr);
83   err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + 4096);
84   assert(err == 0);
85 
86   tid = calloc(s_num_threads, sizeof(*tid));
87   threads_created = 0;
88   for (i = 0; i < s_num_threads; i++)
89   {
90     err = pthread_create(&tid[i], &attr, thread_func, 0);
91     if (err)
92       printf("failed to create thread %d: %s\n", i, strerror(err));
93     else
94       threads_created++;
95   }
96 
97   pthread_attr_destroy(&attr);
98 
99   for (i = 0; i < s_num_threads; i++)
100   {
101     if (tid[i])
102       pthread_join(tid[i], 0);
103   }
104   free(tid);
105 
106   expected_counter = threads_created * s_num_iterations;
107   fprintf(stderr, "s_counter - expected_counter = %d\n",
108           s_counter - expected_counter);
109   expected_grand_sum = 1ULL * expected_counter * (expected_counter - 1) / 2;
110   fprintf(stderr, "s_grand_sum - expected_grand_sum = %lld\n",
111           s_grand_sum - expected_grand_sum);
112   fprintf(stderr, "Finished.\n");
113 
114   return 0;
115 }
116