bart | d45d995 | 2009-05-31 18:53:54 +0000 | [diff] [blame] | 1 | /** |
| 2 | * @file rwlock_test.c |
| 3 | * |
| 4 | * @brief Multithreaded test program that triggers various access patterns |
| 5 | * without triggering any race conditions. |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 6 | */ |
| 7 | |
| 8 | |
bart | 33f1c96 | 2008-05-10 13:17:34 +0000 | [diff] [blame] | 9 | #define _GNU_SOURCE 1 |
| 10 | |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 11 | #include <assert.h> |
| 12 | #include <limits.h> /* PTHREAD_STACK_MIN */ |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 13 | #include <pthread.h> |
| 14 | #include <stdio.h> |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 15 | #include <stdlib.h> /* malloc() */ |
| 16 | #include <string.h> /* strerror() */ |
| 17 | #include <unistd.h> /* getopt() */ |
bart | f8f7629 | 2009-07-21 16:51:02 +0000 | [diff] [blame] | 18 | |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 19 | static int s_num_threads = 10; |
| 20 | static int s_num_iterations = 1000; |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 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. */ |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 25 | |
| 26 | static void* thread_func(void* arg) |
| 27 | { |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 28 | int i, r; |
| 29 | int sum1 = 0, sum2 = 0; |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 30 | |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 31 | for (i = s_num_iterations; i > 0; i--) |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 32 | { |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 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); |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 43 | } |
| 44 | |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 45 | pthread_mutex_lock(&s_mutex); |
| 46 | s_grand_sum += sum2; |
| 47 | pthread_mutex_unlock(&s_mutex); |
| 48 | |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 49 | return 0; |
| 50 | } |
| 51 | |
| 52 | int main(int argc, char** argv) |
| 53 | { |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 54 | pthread_attr_t attr; |
| 55 | pthread_t* tid; |
| 56 | int threads_created; |
| 57 | int optchar; |
| 58 | int err; |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 59 | int i; |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 60 | int expected_counter; |
| 61 | long long expected_grand_sum; |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 62 | |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 63 | while ((optchar = getopt(argc, argv, "i:t:")) != EOF) |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 64 | { |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 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 | } |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 77 | } |
| 78 | |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 79 | pthread_mutex_init(&s_mutex, NULL); |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 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++) |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 89 | { |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 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++; |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 95 | } |
| 96 | |
bart | 2e1470c | 2009-07-24 09:34:37 +0000 | [diff] [blame] | 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 | |
bart | 07c142e | 2009-07-24 11:10:05 +0000 | [diff] [blame] | 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); |
bart | 178cc16 | 2008-05-10 12:52:02 +0000 | [diff] [blame] | 112 | fprintf(stderr, "Finished.\n"); |
| 113 | |
| 114 | return 0; |
| 115 | } |