| /* |
| * rwlock8.c |
| * |
| * Hammer on a bunch of rwlocks to test robustness and fairness. |
| * Printed stats should be roughly even for each thread. |
| * |
| * Yield during each access to exercise lock contention code paths |
| * more than rwlock7.c does (particularly on uni-processor systems). |
| */ |
| |
| #include "test.h" |
| #include <sys/timeb.h> |
| |
| #ifdef __GNUC__ |
| #include <stdlib.h> |
| #endif |
| |
| #define THREADS 5 |
| #define DATASIZE 7 |
| #define ITERATIONS 100000 |
| |
| /* |
| * Keep statistics for each thread. |
| */ |
| typedef struct thread_tag { |
| int thread_num; |
| pthread_t thread_id; |
| int updates; |
| int reads; |
| int changed; |
| int seed; |
| } thread_t; |
| |
| /* |
| * Read-write lock and shared data |
| */ |
| typedef struct data_tag { |
| pthread_rwlock_t lock; |
| int data; |
| int updates; |
| } data_t; |
| |
| static thread_t threads[THREADS]; |
| static data_t data[DATASIZE]; |
| |
| /* |
| * Thread start routine that uses read-write locks |
| */ |
| void *thread_routine (void *arg) |
| { |
| thread_t *self = (thread_t*)arg; |
| int iteration; |
| int element = 0; |
| int seed = self->seed; |
| int interval = 1 + rand_r (&seed) % 71; |
| |
| self->changed = 0; |
| |
| for (iteration = 0; iteration < ITERATIONS; iteration++) |
| { |
| if (iteration % (ITERATIONS / 10) == 0) |
| { |
| putchar('.'); |
| fflush(stdout); |
| } |
| /* |
| * Each "self->interval" iterations, perform an |
| * update operation (write lock instead of read |
| * lock). |
| */ |
| if ((iteration % interval) == 0) |
| { |
| pthread_rwlock_wrlock (&data[element].lock); |
| data[element].data = self->thread_num; |
| data[element].updates++; |
| self->updates++; |
| interval = 1 + rand_r (&seed) % 71; |
| sched_yield(); |
| pthread_rwlock_unlock (&data[element].lock); |
| } else { |
| /* |
| * Look at the current data element to see whether |
| * the current thread last updated it. Count the |
| * times, to report later. |
| */ |
| pthread_rwlock_rdlock (&data[element].lock); |
| |
| self->reads++; |
| |
| if (data[element].data != self->thread_num) |
| { |
| self->changed++; |
| interval = 1 + self->changed % 71; |
| } |
| |
| sched_yield(); |
| |
| pthread_rwlock_unlock (&data[element].lock); |
| } |
| |
| element = (element + 1) % DATASIZE; |
| |
| } |
| |
| return NULL; |
| } |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| int count; |
| int data_count; |
| int thread_updates = 0; |
| int data_updates = 0; |
| int seed = 1; |
| |
| struct _timeb currSysTime1; |
| struct _timeb currSysTime2; |
| |
| //printf ("Skipped (pre-tested)\n"); |
| //return 0; |
| |
| /* |
| * Initialize the shared data. |
| */ |
| for (data_count = 0; data_count < DATASIZE; data_count++) |
| { |
| data[data_count].data = 0; |
| data[data_count].updates = 0; |
| |
| assert(pthread_rwlock_init (&data[data_count].lock, NULL) == 0); |
| } |
| |
| _ftime(&currSysTime1); |
| |
| /* |
| * Create THREADS threads to access shared data. |
| */ |
| for (count = 0; count < THREADS; count++) |
| { |
| threads[count].thread_num = count; |
| threads[count].updates = 0; |
| threads[count].reads = 0; |
| threads[count].seed = 1 + rand_r (&seed) % 71; |
| |
| assert(pthread_create (&threads[count].thread_id, |
| NULL, thread_routine, (void*)&threads[count]) == 0); |
| } |
| |
| /* |
| * Wait for all threads to complete, and collect |
| * statistics. |
| */ |
| for (count = 0; count < THREADS; count++) |
| { |
| assert(pthread_join (threads[count].thread_id, NULL) == 0); |
| } |
| |
| putchar('\n'); |
| fflush(stdout); |
| |
| for (count = 0; count < THREADS; count++) |
| { |
| if (threads[count].changed > 0) |
| { |
| printf ("Thread %d found changed elements %d times\n", |
| count, threads[count].changed); |
| } |
| } |
| |
| putchar('\n'); |
| fflush(stdout); |
| |
| for (count = 0; count < THREADS; count++) |
| { |
| thread_updates += threads[count].updates; |
| printf ("%02d: seed %d, updates %d, reads %d\n", |
| count, threads[count].seed, |
| threads[count].updates, threads[count].reads); |
| } |
| |
| putchar('\n'); |
| fflush(stdout); |
| |
| /* |
| * Collect statistics for the data. |
| */ |
| for (data_count = 0; data_count < DATASIZE; data_count++) |
| { |
| data_updates += data[data_count].updates; |
| printf ("data %02d: value %d, %d updates\n", |
| data_count, data[data_count].data, data[data_count].updates); |
| assert(pthread_rwlock_destroy (&data[data_count].lock) == 0); |
| } |
| |
| printf ("%d thread updates, %d data updates\n", |
| thread_updates, data_updates); |
| |
| _ftime(&currSysTime2); |
| |
| printf( "\nstart: %ld/%d, stop: %ld/%d, duration:%ld\n", |
| currSysTime1.time,currSysTime1.millitm, |
| currSysTime2.time,currSysTime2.millitm, |
| (currSysTime2.time*1000+currSysTime2.millitm) - |
| (currSysTime1.time*1000+currSysTime1.millitm)); |
| |
| return 0; |
| } |