Line data Source code
1 1 : /** 2 : * @file test/test.c 3 : * 4 : * @brief Test framework source 5 : * 6 : * The source of the minimal test framework used in the code base tests 7 : * 8 : * SPDX-FileCopyrightText: 2008-2021 HPDCS Group <rootsim@googlegroups.com> 9 : * SPDX-License-Identifier: GPL-3.0-only 10 : */ 11 : #include <test.h> 12 : 13 : #include <arch/thread.h> 14 : 15 : #include <limits.h> 16 : #include <memory.h> 17 : #include <stdarg.h> 18 : #include <stdatomic.h> 19 : #include <stdbool.h> 20 : #include <stdlib.h> 21 : 22 : #ifndef ROOTSIM_TEST_NAME 23 0 : #define ROOTSIM_TEST_NAME "rs_test" 24 : #endif 25 : 26 : #ifdef ROOTSIM_MPI 27 1 : __attribute__((weak)) nid_t nid; 28 1 : __attribute__((weak)) nid_t n_nodes = 1; 29 : #endif 30 1 : __attribute__((weak)) lp_id_t n_lps; 31 1 : __attribute__((weak)) rid_t n_threads; 32 1 : __attribute__((weak)) __thread rid_t rid; 33 1 : __attribute__((weak)) int log_level; 34 : 35 0 : static char **test_argv; 36 : 37 : __attribute__((weak)) 38 1 : void _log_log(int level, const char *file, unsigned line, const char *fmt, ...) 39 : { 40 : (void) level; 41 : (void) file; 42 : (void) line; 43 : (void) fmt; 44 : } 45 : 46 1 : int main(int argc, char **argv); 47 : 48 : /** 49 : * @brief Initializes ISO C compliant argc and argv from the test configuration 50 : * @param argc_p a pointer to a variable which will hold the computed argc value 51 : * @param argv_p a pointer to a variable which will hold the computer argv value 52 : * @return 0 in case of success, -1 in case of failure 53 : */ 54 1 : static int init_arguments(int *argc_p, char ***argv_p) 55 : { 56 : int argc = 0; 57 : if (test_config.test_arguments) { 58 : while (test_config.test_arguments[argc]) { 59 : ++argc; 60 : } 61 : } 62 : ++argc; 63 : 64 : char **argv = malloc(sizeof(*argv) * (argc + 1)); 65 : if(argv == NULL) 66 : return -1; 67 : 68 : argv[0] = ROOTSIM_TEST_NAME; 69 : 70 : if (test_config.test_arguments) { 71 : memcpy(&argv[1], test_config.test_arguments, 72 : sizeof(*argv) * argc); 73 : } else { 74 : argv[1] = NULL; 75 : } 76 : 77 : *argc_p = argc; 78 : *argv_p = argv; 79 : return 0; 80 : } 81 : 82 : /** 83 : * @brief The exit handler, to exit cleanly even in case of errors 84 : */ 85 1 : static void test_atexit(void) 86 : { 87 : free(test_argv); 88 : } 89 : 90 : /** 91 : * @brief The test wrapper which allows to intervene before the actual main() 92 : */ 93 : __attribute__((constructor)) 94 1 : void main_wrapper(void) 95 : { 96 : int test_argc = 0; 97 : 98 : puts("Starting " ROOTSIM_TEST_NAME " test"); 99 : 100 : n_threads = test_config.threads_count; 101 : 102 : atexit(test_atexit); 103 : 104 : if (init_arguments(&test_argc, &test_argv) == -1) 105 : exit(TEST_BAD_FAIL_EXIT_CODE); 106 : 107 : int test_ret = main(test_argc, test_argv); 108 : if (!test_ret) 109 : puts("Successfully run " ROOTSIM_TEST_NAME " test"); 110 : 111 : exit(test_ret); 112 : } 113 : 114 : /** 115 : * @brief Synchronizes threads on a barrier 116 : * @return true if this thread has been elected as leader, false otherwise 117 : * 118 : * This is a more battle tested although worse performing version of the thread 119 : * barrier. We can't rely on the pthread barrier because it's not portable. 120 : */ 121 1 : bool test_thread_barrier(void) 122 : { 123 : static atomic_uint b_in, b_out, b_cr; 124 : 125 : unsigned i; 126 : unsigned count = test_config.threads_count; 127 : unsigned max_in_before_reset = (UINT_MAX / 2) - (UINT_MAX / 2) % count; 128 : do { 129 : i = atomic_fetch_add_explicit( 130 : &b_in, 1U, memory_order_acq_rel) + 1; 131 : } while (__builtin_expect(i > max_in_before_reset, 0)); 132 : 133 : unsigned cr = atomic_load_explicit(&b_cr, memory_order_relaxed); 134 : 135 : bool leader = i == cr + count; 136 : if (leader) 137 : atomic_store_explicit(&b_cr, cr + count, memory_order_release); 138 : else 139 : while (i > cr) 140 : cr = atomic_load_explicit(&b_cr, memory_order_relaxed); 141 : 142 : atomic_thread_fence(memory_order_acquire); 143 : 144 : unsigned o = atomic_fetch_add_explicit(&b_out, 1, 145 : memory_order_release) + 1; 146 : if (__builtin_expect(o == max_in_before_reset, 0)) { 147 : atomic_thread_fence(memory_order_acquire); 148 : atomic_store_explicit(&b_cr, 0, memory_order_relaxed); 149 : atomic_store_explicit(&b_out, 0, memory_order_relaxed); 150 : atomic_store_explicit(&b_in, 0, memory_order_release); 151 : } 152 : return leader; 153 : }