hdf5/test/ttsafe_atomic.c
Quincey Koziol 9fd88560d5
Refactor threading and other concurrency support (#4469)
Complete overhaul of the concurrency-related aspects of the library (threading, atomics, locking, etc.), adding private routines in the H5TS package to allow internal algorithms to use all of these capabilities.

Adds many new features & components in the H5TS package that are equivalent to common concurrency data structures and capabilities: "regular" and recursive mutices, condition variables, semaphores, thread barriers, 'once' support, thread pools, atomic variables, thread-local keys, and spawning & joining internal threads.

Now supports C11, pthreads, and Windows threading for all H5TS capabilities, except the recursive readers/writers lock, which is not supported on Windows (because Windows threads don't provide a callback on thread-local variable deletion).

The "global" API lock is switched to use a recursive mutex from the H5TS package, instead of its own variant.

API context code (H5CX package) and error stacks (H5E package) now use the common thread-local info, instead of their own variants.

Subfiling code is switched from using Mercury threading features to the new internal H5TS features.

Removes the mercury threading code.

Adds a configure option (--enable-threads / HDF5_ENABLE_THREADS), enabled by default, to control whether threading is enabled within the library.
2024-07-31 12:34:43 -05:00

180 lines
5.7 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://support.hdfgroup.org/ftp/HDF5/releases. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/********************************************************************
*
* Test the correctness of the thread pool routines
*
********************************************************************/
#include "ttsafe.h"
#ifdef H5_HAVE_THREADS
#define NUM_THREADS 16
static H5TS_atomic_int_t counter_g;
static H5TS_THREAD_RETURN_TYPE
noop_task(void *_counter)
{
H5TS_atomic_int_t *counter = (H5TS_atomic_int_t *)_counter;
H5TS_thread_ret_t ret_value = 0;
VERIFY(H5TS_atomic_load_int(counter), 0, "noop_task");
return ret_value;
}
static H5TS_THREAD_RETURN_TYPE
incr_task(void *_counter)
{
H5TS_atomic_int_t *counter = (H5TS_atomic_int_t *)_counter;
H5TS_thread_ret_t ret_value = 0;
H5TS_atomic_fetch_add_int(counter, 1);
return ret_value;
}
static H5TS_THREAD_RETURN_TYPE
decr_task(void *_counter)
{
H5TS_atomic_int_t *counter = (H5TS_atomic_int_t *)_counter;
H5TS_thread_ret_t ret_value = 0;
H5TS_atomic_fetch_sub_int(counter, 1);
return ret_value;
}
/*
**********************************************************************
* tts_atomics
*
**********************************************************************
*/
void
tts_atomics(void)
{
H5TS_pool_t *pool = NULL;
herr_t result;
/* Initialize the counter */
H5TS_atomic_init_int(&counter_g, 0);
/* Sanity checks on bad input */
result = H5TS_pool_create(NULL, NUM_THREADS);
VERIFY(result, FAIL, "H5TS_pool_create");
result = H5TS_pool_create(&pool, 0);
VERIFY(result, FAIL, "H5TS_pool_create");
result = H5TS_pool_add_task(NULL, noop_task, NULL);
VERIFY(result, FAIL, "H5TS_pool_add_task");
result = H5TS_pool_add_task(pool, NULL, NULL);
VERIFY(result, FAIL, "H5TS_pool_add_task");
result = H5TS_pool_destroy(NULL);
VERIFY(result, FAIL, "H5TS_pool_destroy");
/* Create & destroy empty pool */
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
/* Create pool, add single 'noop' task, destroy pool */
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
result = H5TS_pool_add_task(pool, noop_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(H5TS_atomic_load_int(&counter_g), 0, "noop");
/* Create pool, add single 'incr' task, destroy pool */
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
result = H5TS_pool_add_task(pool, incr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(H5TS_atomic_load_int(&counter_g), 1, "single incr");
/* Create pool, add pair of 'incr' & 'decr' tasks, destroy pool */
H5TS_atomic_store_int(&counter_g, 10);
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
result = H5TS_pool_add_task(pool, incr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
result = H5TS_pool_add_task(pool, decr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(H5TS_atomic_load_int(&counter_g), 10, "incr + decr");
/* Create pool, add many 'incr' & 'decr' tasks, destroy pool */
H5TS_atomic_store_int(&counter_g, 3);
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
for (unsigned u = 0; u < 200; u++) {
result = H5TS_pool_add_task(pool, incr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
}
for (unsigned u = 0; u < 100; u++) {
result = H5TS_pool_add_task(pool, decr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
}
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(H5TS_atomic_load_int(&counter_g), 103, "200 incr + 100 decr");
/* Create pool, add *lots* of 'incr' & 'decr' tasks, destroy pool */
H5TS_atomic_store_int(&counter_g, 5);
result = H5TS_pool_create(&pool, NUM_THREADS);
CHECK_I(result, "H5TS_pool_create");
for (unsigned u = 0; u < 2 * 1000 * 1000; u++) {
result = H5TS_pool_add_task(pool, incr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
}
for (unsigned u = 0; u < 1 * 1000 * 1000; u++) {
result = H5TS_pool_add_task(pool, decr_task, &counter_g);
CHECK_I(result, "H5TS_pool_add_task");
}
result = H5TS_pool_destroy(pool);
CHECK_I(result, "H5TS_pool_destroy");
VERIFY(H5TS_atomic_load_int(&counter_g), 5 + (1000 * 1000), "2,000,000 incr + 1,000,000 decr");
/* Destroy the atomic counter */
H5TS_atomic_destroy_int(&counter_g);
} /* end tts_atomics() */
#endif /* H5_HAVE_THREADS */