From 0c2fa7910e6c4612b529aba11cc9fe5968a676cc Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Tue, 26 Nov 2024 11:38:39 -0600 Subject: [PATCH] Isolate threaded test stat handling --- test/testframe.c | 302 ++++++++++++++++++++++++----------------------- 1 file changed, 157 insertions(+), 145 deletions(-) diff --git a/test/testframe.c b/test/testframe.c index f0b6f9959e5..60594962473 100644 --- a/test/testframe.c +++ b/test/testframe.c @@ -64,6 +64,12 @@ static int (*TestPrivateParser)(int ac, char *av[]) = NULL; static int TestMaxNumThreads_g = -1; /* Max number of threads that can be spawned */ const char *test_path_prefix = NULL; +static void PerformThreadedTest(TestStruct Test); + +#ifdef H5_HAVE_MULTITHREAD +static void UpdateTestStats(TestThreadArgs *test_args); +#endif + /* * Setup a test function and add it to the list of tests. * It must have no parameters and returns void. @@ -350,11 +356,8 @@ PerformTests(void) { unsigned Loop; bool is_test_threaded = false; - bool mt_initialized = false; int old_num_errs = 0; - /* Silence compiler warnings */ - (void) mt_initialized; for (Loop = 0; Loop < Index; Loop++) { is_test_threaded = (Test[Loop].TestFrameworkFlags & ALLOW_MULTITHREAD) && (TEST_EXECUTION_THREADED); @@ -364,8 +367,7 @@ PerformTests(void) MESSAGE(2, ("Skipping -- %s (%s) \n", Test[Loop].Description, Test[Loop].Name)); } else { - MESSAGE(2, ("Testing %s -- %s (%s) \n", (is_test_threaded ? "(Threaded)" : ""), - Test[Loop].Description, Test[Loop].Name)); + MESSAGE(2, ("Testing -- %s (%s) \n", Test[Loop].Description, Test[Loop].Name)); MESSAGE(5, ("===============================================\n")); H5_ATOMIC_STORE(Test[Loop].NumErrors, num_errs_g); Test_parameters = Test[Loop].TestParameters; @@ -373,164 +375,165 @@ PerformTests(void) if (!is_test_threaded) { Test[Loop].Call(); - TestAlarmOff(); - H5_ATOMIC_STORE(Test[Loop].NumErrors, num_errs_g - old_num_errs); - MESSAGE(5, ("===============================================\n")); - MESSAGE(5, ("There were %d errors detected.\n\n", (int)H5_ATOMIC_LOAD(Test[Loop].NumErrors))); } else { -#ifndef H5_HAVE_MULTITHREAD - if (Test[Loop].TestFrameworkFlags & ALLOW_MULTITHREAD) { - MESSAGE(2, ("HDF5 was not built with multi-threaded support; Skipping test\n")); - TestAlarmOff(); - continue; - } -#else - pthread_t *threads; - TestThreadArgs *thread_args; - int ret = 0; - test_outcome_t final_results[H5_MAX_NUM_SUBTESTS]; - - memset(final_results, (int) TEST_UNINIT, H5_MAX_NUM_SUBTESTS * sizeof(test_outcome_t)); - - if ((threads = (pthread_t *)calloc((size_t) GetTestMaxNumThreads(), sizeof(pthread_t))) == NULL) { - fprintf(stderr, "Error allocating memory for threads\n"); - exit(EXIT_FAILURE); - } + PerformThreadedTest(Test[Loop]); + } - if ((thread_args = (TestThreadArgs *)calloc((size_t) GetTestMaxNumThreads(), sizeof(TestThreadArgs))) == NULL) { - fprintf(stderr, "Error allocating memory for thread arguments\n"); - exit(EXIT_FAILURE); - } + TestAlarmOff(); + H5_ATOMIC_STORE(Test[Loop].NumErrors, num_errs_g - old_num_errs); + MESSAGE(5, ("===============================================\n")); + MESSAGE(5, ("There were %d errors detected.\n\n", (int)H5_ATOMIC_LOAD(Test[Loop].NumErrors))); + } + } - if (!mt_initialized) { - if (H5_mt_test_global_setup() < 0) { - fprintf(stderr, "Error setting up global MT test info\n"); - exit(EXIT_FAILURE); - } + Test_parameters = NULL; /* clear it. */ - mt_initialized = true; - } + if (num_errs_g) + print_func("!!! %d Error(s) were detected !!!\n\n", (int)num_errs_g); + else + MESSAGE(VERBO_NONE, ("All tests were successful. \n\n")); +} - for (int i = 0; i < GetTestMaxNumThreads(); i++) { - thread_args[i].ThreadIndex = i; - thread_args[i].Call = Test[Loop].Call; - thread_args[i].num_tests = 0; +#ifdef H5_HAVE_MULTITHREAD - if ((thread_args[i].test_outcomes = calloc(H5_MAX_NUM_SUBTESTS, sizeof(test_outcome_t))) == NULL) { - fprintf(stderr, "Error allocating memory for thread outcomes\n"); - exit(EXIT_FAILURE); - } +static void +PerformThreadedTest(TestStruct threaded_test) { + pthread_t *threads; + TestThreadArgs *thread_args; + int ret = 0; + + if (H5_mt_test_global_setup() < 0) { + fprintf(stderr, "Error setting up global MT test info\n"); + exit(EXIT_FAILURE); + } - memset(thread_args[i].test_outcomes, (int) TEST_UNINIT, H5_MAX_NUM_SUBTESTS * sizeof(test_outcome_t)); + if ((threads = (pthread_t *)calloc((size_t) GetTestMaxNumThreads(), sizeof(pthread_t))) == NULL) { + fprintf(stderr, "Error allocating memory for threads\n"); + exit(EXIT_FAILURE); + } - if ((thread_args[i].test_descriptions = calloc(H5_MAX_NUM_SUBTESTS, sizeof(char*))) == NULL) { - fprintf(stderr, "Error allocating memory for thread test descriptions\n"); - exit(EXIT_FAILURE); - } + if ((thread_args = (TestThreadArgs *)calloc((size_t) GetTestMaxNumThreads(), sizeof(TestThreadArgs))) == NULL) { + fprintf(stderr, "Error allocating memory for thread arguments\n"); + exit(EXIT_FAILURE); + } - memset(thread_args[i].test_descriptions, 0, H5_MAX_NUM_SUBTESTS * sizeof(char*)); + for (int i = 0; i < GetTestMaxNumThreads(); i++) { + thread_args[i].ThreadIndex = i; + thread_args[i].Call = threaded_test.Call; + thread_args[i].num_tests = 0; - ret = pthread_create(&threads[i], NULL, ThreadTestWrapper, (void*) &thread_args[i]); + if ((thread_args[i].test_outcomes = calloc(H5_MAX_NUM_SUBTESTS, sizeof(test_outcome_t))) == NULL) { + fprintf(stderr, "Error allocating memory for thread outcomes\n"); + exit(EXIT_FAILURE); + } - if (ret != 0) { - fprintf(stderr, "Error creating thread %d\n", i); - exit(EXIT_FAILURE); - } - } + memset(thread_args[i].test_outcomes, (int) TEST_UNINIT, H5_MAX_NUM_SUBTESTS * sizeof(test_outcome_t)); - for (int i = 0; i < GetTestMaxNumThreads(); i++) { - ret = pthread_join(threads[i], NULL); + if ((thread_args[i].test_descriptions = calloc(H5_MAX_NUM_SUBTESTS, sizeof(char*))) == NULL) { + fprintf(stderr, "Error allocating memory for thread test descriptions\n"); + exit(EXIT_FAILURE); + } - if (ret != 0) { - fprintf(stderr, "Error joining thread %d\n", i); - exit(EXIT_FAILURE); - } + memset(thread_args[i].test_descriptions, 0, H5_MAX_NUM_SUBTESTS * sizeof(char*)); + + ret = pthread_create(&threads[i], NULL, ThreadTestWrapper, (void*) &thread_args[i]); - if (thread_args[i].num_tests == 0) { - fprintf(stderr, "Empty test found for thread %d\n", i); - exit(EXIT_FAILURE); - } - } - - /* Verify that each thread reported the same number of subtests */ - for (int i = 0; i < GetTestMaxNumThreads(); i++) { - if (thread_args[i].num_tests != thread_args[0].num_tests) { - fprintf(stderr, "Thread %d reported %ld subtests, but thread 0 reported %ld\n", i, thread_args[i].num_tests, thread_args[0].num_tests); - exit(EXIT_FAILURE); - } - } + if (ret != 0) { + fprintf(stderr, "Error creating thread %d\n", i); + exit(EXIT_FAILURE); + } + } - /* Aggregate results - priority order is invalid > fail > pass > skip */ - H5_ATOMIC_ADD(n_tests_run_g, thread_args[0].num_tests); - - for (size_t j = 0; j < thread_args[0].num_tests; j++) { - for (int i = 0; i < GetTestMaxNumThreads(); i++) - final_results[j] = ((final_results[j] > thread_args[i].test_outcomes[j]) ? final_results[j] : thread_args[i].test_outcomes[j]); - - /* Display subtest description, if result is from subtest */ - if (thread_args[0].test_descriptions[j] != NULL) - TESTING_2_DISPLAY(thread_args[0].test_descriptions[j]); - - switch (final_results[j]) { - case TEST_PASS: - PASSED_DISPLAY(); - H5_ATOMIC_ADD(n_tests_passed_g, 1); - break; - case TEST_FAIL: - H5_FAILED_DISPLAY(); - H5_ATOMIC_ADD(n_tests_failed_g, 1); - /* TBD - Neither multi-threaded nor single-threaded API tests increment the testframe error count. - * This would deal with the multi-threaded case, but the single-threaded case is trickier. */ - /* H5_ATOMIC_ADD(num_errs_g, 1); */ - break; - case TEST_SKIP: - SKIPPED_DISPLAY(); - H5_ATOMIC_ADD(n_tests_skipped_g, 1); - break; - case TEST_UNINIT: - ERROR_DISPLAY(); - exit(EXIT_FAILURE); - break; - case TEST_INVALID: - default: - ERROR_DISPLAY(); - exit(EXIT_FAILURE); - break; - } - } + for (int i = 0; i < GetTestMaxNumThreads(); i++) { + ret = pthread_join(threads[i], NULL); - for (int i = 0; i < GetTestMaxNumThreads(); i++) { - free(thread_args[i].test_outcomes); - free(thread_args[i].test_descriptions); - thread_args[i].test_outcomes = NULL; - thread_args[i].test_descriptions = NULL; - } + if (ret != 0) { + fprintf(stderr, "Error joining thread %d\n", i); + exit(EXIT_FAILURE); + } + } + + UpdateTestStats(thread_args); + + /* Clean up */ + for (int i = 0; i < GetTestMaxNumThreads(); i++) { + free(thread_args[i].test_outcomes); + thread_args[i].test_outcomes = NULL; + + free(thread_args[i].test_descriptions); + thread_args[i].test_descriptions = NULL; + } - free(threads); - free(thread_args); - - threads = NULL; - thread_args = NULL; + free(threads); + free(thread_args); + + threads = NULL; + thread_args = NULL; - TestAlarmOff(); + return; +} - H5_ATOMIC_STORE(Test[Loop].NumErrors, num_errs_g - old_num_errs); - MESSAGE(5, ("===============================================\n")); - MESSAGE(5, ("There were %d errors detected.\n\n", (int)H5_ATOMIC_LOAD(Test[Loop].NumErrors))); -#endif /* H5_HAVE_MULTITHREAD */ - } +static void +UpdateTestStats(TestThreadArgs *thread_args) { + test_outcome_t final_results[H5_MAX_NUM_SUBTESTS]; + memset(final_results, (int) TEST_UNINIT, H5_MAX_NUM_SUBTESTS * sizeof(test_outcome_t)); + + /* If test does not publish its results information to a threadlocal variable, + * do not track statistics */ + if (thread_args[0].num_tests == 0) { + return; + } + + /* Verify that each thread reported the same number of subtests */ + for (int i = 0; i < GetTestMaxNumThreads(); i++) { + if (thread_args[i].num_tests != thread_args[0].num_tests) { + fprintf(stderr, "Thread %d reported %ld subtests, but thread 0 reported %ld\n", i, thread_args[i].num_tests, thread_args[0].num_tests); + exit(EXIT_FAILURE); } } - Test_parameters = NULL; /* clear it. */ + /* Aggregate results - priority order is invalid > fail > pass > skip */ + H5_ATOMIC_ADD(n_tests_run_g, thread_args[0].num_tests); + + for (size_t j = 0; j < thread_args[0].num_tests; j++) { + for (int i = 0; i < GetTestMaxNumThreads(); i++) + final_results[j] = ((final_results[j] > thread_args[i].test_outcomes[j]) ? final_results[j] : thread_args[i].test_outcomes[j]); + + /* Display subtest description, if result is from subtest */ + if (thread_args[0].test_descriptions[j] != NULL) + TESTING_2_DISPLAY(thread_args[0].test_descriptions[j]); + + switch (final_results[j]) { + case TEST_PASS: + PASSED_DISPLAY(); + H5_ATOMIC_ADD(n_tests_passed_g, 1); + break; + case TEST_FAIL: + H5_FAILED_DISPLAY(); + H5_ATOMIC_ADD(n_tests_failed_g, 1); + /* TBD - Neither multi-threaded nor single-threaded API tests increment the testframe error count. + * This would deal with the multi-threaded case, but the single-threaded case is trickier. */ + /* H5_ATOMIC_ADD(num_errs_g, 1); */ + break; + case TEST_SKIP: + SKIPPED_DISPLAY(); + H5_ATOMIC_ADD(n_tests_skipped_g, 1); + break; + case TEST_UNINIT: + ERROR_DISPLAY(); + exit(EXIT_FAILURE); + break; + case TEST_INVALID: + default: + ERROR_DISPLAY(); + exit(EXIT_FAILURE); + break; + } + } - if (num_errs_g) - print_func("!!! %d Error(s) were detected !!!\n\n", (int)num_errs_g); - else - MESSAGE(VERBO_NONE, ("All tests were successful. \n\n")); + return; } -#ifdef H5_HAVE_MULTITHREAD /* * Set up and execute a test flagged for threaded * execution within a single thread. @@ -627,7 +630,15 @@ void H5_test_thread_info_key_destructor(void *value) { return; } -#endif +#else /* H5_HAVE_MULTITHREAD */ +static void +PerformThreadedTest(TestStruct threaded_test) { + (void) threaded_test; + MESSAGE(2, ("HDF5 was not built with multi-threaded support; Skipping test\n")); + return; +} + +#endif /* H5_HAVE_MULTITHREAD */ /* * Display test summary. */ @@ -928,11 +939,12 @@ SetTest(const char *testname, int action) /* Set up global variables used for API tests */ int H5_mt_test_global_setup(void) { - /* Set up pthread key */ - if (pthread_key_create(&test_thread_info_key_g, H5_test_thread_info_key_destructor) != 0) { - fprintf(stderr, "Error creating threadlocal key\n"); - goto error; - } + /* Set up pthread key if it doesn't exist */ + if (pthread_getspecific(test_thread_info_key_g) == NULL) + if (pthread_key_create(&test_thread_info_key_g, H5_test_thread_info_key_destructor) != 0) { + fprintf(stderr, "Error creating threadlocal key\n"); + goto error; + } return 0; error: @@ -990,4 +1002,4 @@ SetTestMaxNumThreads(int max_num_threads) { TestMaxNumThreads_g = max_num_threads; return; -} \ No newline at end of file +}