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