1 /* Self tests for parallel_for_each 2 3 Copyright (C) 2021-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 /* This file is divided in two parts: 21 - FOR_EACH-undefined, and 22 - FOR_EACH-defined. 23 The former includes the latter, more than once, with different values for 24 FOR_EACH. The FOR_EACH-defined part reads like a regular function. */ 25 #ifndef FOR_EACH 26 27 #include "defs.h" 28 #include "gdbsupport/selftest.h" 29 #include "gdbsupport/parallel-for.h" 30 31 #if CXX_STD_THREAD 32 33 #include "gdbsupport/thread-pool.h" 34 35 namespace selftests { 36 namespace parallel_for { 37 38 struct save_restore_n_threads 39 { 40 save_restore_n_threads () 41 : n_threads (gdb::thread_pool::g_thread_pool->thread_count ()) 42 { 43 } 44 45 ~save_restore_n_threads () 46 { 47 gdb::thread_pool::g_thread_pool->set_thread_count (n_threads); 48 } 49 50 int n_threads; 51 }; 52 53 /* Define test_par using TEST in the FOR_EACH-defined part. */ 54 #define TEST test_par 55 #define FOR_EACH gdb::parallel_for_each 56 #include "parallel-for-selftests.c" 57 #undef FOR_EACH 58 #undef TEST 59 60 /* Define test_seq using TEST in the FOR_EACH-defined part. */ 61 #define TEST test_seq 62 #define FOR_EACH gdb::sequential_for_each 63 #include "parallel-for-selftests.c" 64 #undef FOR_EACH 65 #undef TEST 66 67 static void 68 test (int n_threads) 69 { 70 test_par (n_threads); 71 test_seq (n_threads); 72 } 73 74 static void 75 test_n_threads () 76 { 77 test (0); 78 test (1); 79 test (3); 80 } 81 82 } 83 } 84 85 #endif /* CXX_STD_THREAD */ 86 87 void _initialize_parallel_for_selftests (); 88 void 89 _initialize_parallel_for_selftests () 90 { 91 #ifdef CXX_STD_THREAD 92 selftests::register_test ("parallel_for", 93 selftests::parallel_for::test_n_threads); 94 #endif /* CXX_STD_THREAD */ 95 } 96 97 #else /* FOR_EACH */ 98 99 static void 100 TEST (int n_threads) 101 { 102 save_restore_n_threads saver; 103 gdb::thread_pool::g_thread_pool->set_thread_count (n_threads); 104 105 #define NUMBER 10000 106 107 std::atomic<int> counter (0); 108 FOR_EACH (1, 0, NUMBER, 109 [&] (int start, int end) 110 { 111 counter += end - start; 112 }); 113 SELF_CHECK (counter == NUMBER); 114 115 counter = 0; 116 FOR_EACH (1, 0, 0, 117 [&] (int start, int end) 118 { 119 counter += end - start; 120 }); 121 SELF_CHECK (counter == 0); 122 123 auto task_size_max_ = [] (int iter) 124 { 125 return (size_t)SIZE_MAX; 126 }; 127 auto task_size_max = gdb::make_function_view (task_size_max_); 128 129 counter = 0; 130 FOR_EACH (1, 0, NUMBER, 131 [&] (int start, int end) 132 { 133 counter += end - start; 134 }, task_size_max); 135 SELF_CHECK (counter == NUMBER); 136 137 auto task_size_one_ = [] (int iter) 138 { 139 return (size_t)1; 140 }; 141 auto task_size_one = gdb::make_function_view (task_size_one_); 142 143 counter = 0; 144 FOR_EACH (1, 0, NUMBER, 145 [&] (int start, int end) 146 { 147 counter += end - start; 148 }, task_size_one); 149 SELF_CHECK (counter == NUMBER); 150 151 #undef NUMBER 152 153 /* Check that if there are fewer tasks than threads, then we won't 154 end up with a null result. */ 155 std::vector<std::unique_ptr<int>> intresults; 156 std::atomic<bool> any_empty_tasks (false); 157 158 FOR_EACH (1, 0, 1, 159 [&] (int start, int end) 160 { 161 if (start == end) 162 any_empty_tasks = true; 163 return std::unique_ptr<int> (new int (end - start)); 164 }); 165 SELF_CHECK (!any_empty_tasks); 166 SELF_CHECK (std::all_of (intresults.begin (), 167 intresults.end (), 168 [] (const std::unique_ptr<int> &entry) 169 { 170 return entry != nullptr; 171 })); 172 173 /* The same but using the task size parameter. */ 174 intresults.clear (); 175 any_empty_tasks = false; 176 FOR_EACH (1, 0, 1, 177 [&] (int start, int end) 178 { 179 if (start == end) 180 any_empty_tasks = true; 181 return std::unique_ptr<int> (new int (end - start)); 182 }, 183 task_size_one); 184 SELF_CHECK (!any_empty_tasks); 185 SELF_CHECK (std::all_of (intresults.begin (), 186 intresults.end (), 187 [] (const std::unique_ptr<int> &entry) 188 { 189 return entry != nullptr; 190 })); 191 } 192 193 #endif /* FOR_EACH */ 194