hdf5/test/ttsafe_rwlock.c
jhendersonHDF 34d6ef545a
Refactor h5test.c, testframe.c and testpar.h testing frameworks (#4891)
Added new testframe.h header to document testing framework functions and
split them away from h5test.h and from test programs that don't
integrate with the testframe.c testing framework

Added new test setup callback to testframe.c testing framework

Added parameters to AddTest() to specify size of test parameters so they
can be copied for later use

Enabled HDF5 error stacks in testframe.c framework by default and added
some error stack suppressions to some testhdf5 tests

Added new maxthreads option to testframe.c framework to allow specifying
the maximum number of threads a multi-threaded test can use

Moved TestExpress functionality out of testframe.c and into more general
h5test.c for wider use by tests through getter and setter

Updated some tests to not mix and match functionality between h5test.c/h
and testframe.c/h

Moved some functionality from testphdf5.h into testpar.h for parallel
tests that aren't part of testphdf5

Added new parallel test library that contains common shared
functionality for parallel tests (similar to h5test library)
2024-10-01 16:10:03 -05:00

318 lines
9.9 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/********************************************************************
*
* Test the correctness of the non-recursive R/W lock routines
*
********************************************************************/
#include "ttsafe.h"
#ifdef H5_HAVE_THREADS
#define NUM_THREADS 16
#define NUM_WRITERS 4
#define NUM_ITERS 12
#define COUNT_MAX 512
typedef struct {
H5TS_rwlock_t lock;
int val;
H5TS_barrier_t barrier;
} atomic_counter_t;
static H5TS_THREAD_RETURN_TYPE
incr_task(void *_counter)
{
atomic_counter_t *counter = (atomic_counter_t *)_counter;
herr_t result;
H5TS_thread_ret_t ret_value = 0;
result = H5TS_rwlock_wrlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_wrlock");
/* Increment value */
counter->val++;
result = H5TS_rwlock_wrunlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_wrunlock");
return ret_value;
}
static H5TS_THREAD_RETURN_TYPE
many_read(void *_counter)
{
atomic_counter_t *counter = (atomic_counter_t *)_counter;
herr_t result;
H5TS_thread_ret_t ret_value = 0;
result = H5TS_rwlock_rdlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_rdlock");
/* Wait at barrier, to confirm that many readers can hold lock */
result = H5TS_barrier_wait(&counter->barrier);
CHECK_I(result, "H5TS_barrier_wait");
result = H5TS_rwlock_rdunlock(&counter->lock);
CHECK_I(result, "H5TS_rdlock_rdunlock");
return ret_value;
}
static H5TS_THREAD_RETURN_TYPE
count_up_and_down(void *_counter)
{
atomic_counter_t *counter = (atomic_counter_t *)_counter;
herr_t result;
H5TS_thread_ret_t ret_value = 0;
/* Count up & down a number of times */
for (unsigned u = 0; u < NUM_ITERS; u++) {
/* Wait at barrier, to ensure all threads are ready to count */
result = H5TS_barrier_wait(&counter->barrier);
CHECK_I(result, "H5TS_barrier_wait");
/* Count up */
for (unsigned v = 0; v < COUNT_MAX; v++) {
result = H5TS_rwlock_wrlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_wrlock");
/* Increment value */
counter->val++;
result = H5TS_rwlock_wrunlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_wrunlock");
}
/* Wait at barrier, to ensure all threads have finishend counting up */
result = H5TS_barrier_wait(&counter->barrier);
CHECK_I(result, "H5TS_barrier_wait");
/* Count down */
for (unsigned v = 0; v < COUNT_MAX; v++) {
result = H5TS_rwlock_wrlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_wrlock");
/* Decrement value */
counter->val--;
result = H5TS_rwlock_wrunlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_wrunlock");
}
}
return ret_value;
}
static H5TS_THREAD_RETURN_TYPE
verify_counting(void *_counter)
{
atomic_counter_t *counter = (atomic_counter_t *)_counter;
herr_t result;
int last_val = 0;
H5TS_thread_ret_t ret_value = 0;
/* Count up & down a number of times */
for (unsigned u = 0; u < NUM_ITERS; u++) {
/* Wait at barrier, to ensure all threads are ready to count */
result = H5TS_barrier_wait(&counter->barrier);
CHECK_I(result, "H5TS_barrier_wait");
/* Verify that counter goes only up */
do {
result = H5TS_rwlock_rdlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_rdlock");
/* Check counter value */
if (counter->val < last_val)
ERROR("incorrect counter value");
/* Save value */
last_val = counter->val;
result = H5TS_rwlock_rdunlock(&counter->lock);
CHECK_I(result, "H5TS_rdlock_wrunlock");
/* Give the writers a chance to make progress */
H5TS_thread_yield();
} while (last_val < (NUM_WRITERS * COUNT_MAX));
/* Wait at barrier, to ensure all threads have finishend counting up */
result = H5TS_barrier_wait(&counter->barrier);
CHECK_I(result, "H5TS_barrier_wait");
/* Verify that counter goes only down */
do {
result = H5TS_rwlock_rdlock(&counter->lock);
CHECK_I(result, "H5TS_rwlock_rdlock");
/* Check counter value */
if (counter->val > last_val)
ERROR("incorrect counter value");
/* Save value */
last_val = counter->val;
result = H5TS_rwlock_rdunlock(&counter->lock);
CHECK_I(result, "H5TS_rdlock_wrunlock");
/* Give the writers a chance to make progress */
H5TS_thread_yield();
} while (last_val > 0);
}
return ret_value;
}
/*
**********************************************************************
* tts_rwlock
**********************************************************************
*/
void
tts_rwlock(const void H5_ATTR_UNUSED *params)
{
H5TS_thread_t threads[NUM_THREADS];
H5TS_pool_t *pool = NULL;
H5TS_rwlock_t lock;
atomic_counter_t counter;
herr_t result;
/* Sanity checks on bad input */
result = H5TS_rwlock_init(NULL);
VERIFY(result, FAIL, "H5TS_rwlock_init");
result = H5TS_rwlock_rdlock(NULL);
VERIFY(result, FAIL, "H5TS_rwlock_rdlock");
result = H5TS_rwlock_rdunlock(NULL);
VERIFY(result, FAIL, "H5TS_rwlock_rdunlock");
result = H5TS_rwlock_wrlock(NULL);
VERIFY(result, FAIL, "H5TS_rwlock_wrlock");
result = H5TS_rwlock_wrunlock(NULL);
VERIFY(result, FAIL, "H5TS_rwlock_wrunlock");
result = H5TS_rwlock_destroy(NULL);
VERIFY(result, FAIL, "H5TS_rwlock_destroy");
/* Create & destroy lock */
result = H5TS_rwlock_init(&lock);
CHECK_I(result, "H5TS_rwlock_init");
result = H5TS_rwlock_destroy(&lock);
CHECK_I(result, "H5TS_rwlock_destroy");
/* Read lock & unlock */
result = H5TS_rwlock_init(&lock);
CHECK_I(result, "H5TS_rwlock_init");
result = H5TS_rwlock_rdlock(&lock);
CHECK_I(result, "H5TS_rwlock_rdlock");
result = H5TS_rwlock_rdunlock(&lock);
CHECK_I(result, "H5TS_rwlock_rdunlock");
result = H5TS_rwlock_destroy(&lock);
CHECK_I(result, "H5TS_rwlock_destroy");
/* Write lock & unlock */
result = H5TS_rwlock_init(&lock);
CHECK_I(result, "H5TS_rwlock_init");
result = H5TS_rwlock_wrlock(&lock);
CHECK_I(result, "H5TS_rwlock_wrlock");
result = H5TS_rwlock_wrunlock(&lock);
CHECK_I(result, "H5TS_rwlock_wrunlock");
result = H5TS_rwlock_destroy(&lock);
CHECK_I(result, "H5TS_rwlock_destroy");
/* Hold read lock w/many threads */
result = H5TS_rwlock_init(&counter.lock);
CHECK_I(result, "H5TS_rwlock_init");
result = H5TS_barrier_init(&counter.barrier, NUM_THREADS);
CHECK_I(result, "H5TS_barrier_init");
for (unsigned u = 0; u < NUM_THREADS; u++) {
result = H5TS_thread_create(&threads[u], many_read, &counter);
CHECK_I(result, "H5TS_thread_create");
}
for (unsigned u = 0; u < NUM_THREADS; u++) {
result = H5TS_thread_join(threads[u], NULL);
CHECK_I(result, "H5TS_thread_join");
}
result = H5TS_barrier_destroy(&counter.barrier);
CHECK_I(result, "H5TS_barrier_destroy");
result = H5TS_rwlock_destroy(&counter.lock);
CHECK_I(result, "H5TS_rwlock_destroy");
/* Increment counter w/many threads */
result = H5TS_rwlock_init(&counter.lock);
CHECK_I(result, "H5TS_rwlock_init");
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
counter.val = 0;
for (unsigned u = 0; u < NUM_THREADS; u++) {
result = H5TS_pool_add_task(pool, incr_task, &counter);
CHECK_I(result, "H5TS_pool_add_task");
}
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(counter.val, NUM_THREADS, "many incr");
result = H5TS_rwlock_destroy(&counter.lock);
CHECK_I(result, "H5TS_rwlock_destroy");
/* Increment & decrement counter w/many threads while reading */
result = H5TS_rwlock_init(&counter.lock);
CHECK_I(result, "H5TS_rwlock_init");
result = H5TS_barrier_init(&counter.barrier, NUM_THREADS);
CHECK_I(result, "H5TS_barrier_init");
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
counter.val = 0;
for (unsigned u = 0; u < NUM_WRITERS; u++) {
result = H5TS_pool_add_task(pool, count_up_and_down, &counter);
CHECK_I(result, "H5TS_pool_add_task");
}
for (unsigned u = 0; u < (NUM_THREADS - NUM_WRITERS); u++) {
result = H5TS_pool_add_task(pool, verify_counting, &counter);
CHECK_I(result, "H5TS_pool_add_task");
}
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(counter.val, 0, "count up & down");
result = H5TS_barrier_destroy(&counter.barrier);
CHECK_I(result, "H5TS_barrier_destroy");
result = H5TS_rwlock_destroy(&counter.lock);
CHECK_I(result, "H5TS_rwlock_destroy");
} /* end tts_rwlock() */
#endif /*H5_HAVE_THREADS*/