diff options
Diffstat (limited to 'gdb/unittests/parallel-for-selftests.c')
-rw-r--r-- | gdb/unittests/parallel-for-selftests.c | 187 |
1 files changed, 106 insertions, 81 deletions
diff --git a/gdb/unittests/parallel-for-selftests.c b/gdb/unittests/parallel-for-selftests.c index 841d914..b69eed4 100644 --- a/gdb/unittests/parallel-for-selftests.c +++ b/gdb/unittests/parallel-for-selftests.c @@ -17,13 +17,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* This file is divided in two parts: - - FOR_EACH-undefined, and - - FOR_EACH-defined. - The former includes the latter, more than once, with different values for - FOR_EACH. The FOR_EACH-defined part reads like a regular function. */ -#ifndef FOR_EACH - #include "gdbsupport/selftest.h" #include "gdbsupport/parallel-for.h" @@ -49,97 +42,129 @@ struct save_restore_n_threads int n_threads; }; -/* Define test_par using TEST in the FOR_EACH-defined part. */ -#define TEST test_par -#define FOR_EACH gdb::parallel_for_each -#include "parallel-for-selftests.c" -#undef FOR_EACH -#undef TEST +using foreach_callback_t = gdb::function_view<void (iterator_range<int *> range)>; +using do_foreach_t = gdb::function_view<void (int *first, int *last, + foreach_callback_t)>; -/* Define test_seq using TEST in the FOR_EACH-defined part. */ -#define TEST test_seq -#define FOR_EACH gdb::sequential_for_each -#include "parallel-for-selftests.c" -#undef FOR_EACH -#undef TEST +/* Run one parallel-for-each test on the range [1, UPPER_BOUND) using the + parallel-for-each implementation DO_FOREACH. */ static void -test (int n_threads) +test_one (do_foreach_t do_foreach, int upper_bound) { - test_par (n_threads); - test_seq (n_threads); -} + std::vector<int> input; -static void -test_n_threads () -{ - test (0); - test (1); - test (3); -} + for (int i = 0; i < upper_bound; ++i) + input.emplace_back (i); -} -} + std::vector<int> output; + std::mutex mtx; -#endif /* CXX_STD_THREAD */ + /* The (unfortunate) reason why we don't use std::vector<int>::iterator as + the parallel-for-each iterator type is that std::atomic won't work with + that type when building with -D_GLIBCXX_DEBUG. */ + do_foreach (input.data (), input.data () + input.size (), + [&] (iterator_range<int *> range) + { + /* We shouldn't receive empty ranges. */ + SELF_CHECK (!range.empty ()); -void _initialize_parallel_for_selftests (); -void -_initialize_parallel_for_selftests () -{ -#ifdef CXX_STD_THREAD - selftests::register_test ("parallel_for", - selftests::parallel_for::test_n_threads); -#endif /* CXX_STD_THREAD */ + std::lock_guard lock (mtx); + + for (int i : range) + output.emplace_back (i * 2); + }); + + /* Verify that each item was processed exactly once. */ + SELF_CHECK (output.size () == upper_bound); + std::sort (output.begin (), output.end ()); + + for (int i = 0; i < output.size (); ++i) + SELF_CHECK (output[i] == i * 2); } -#else /* FOR_EACH */ +/* Run all tests on the parallel-for-each implementation DO_FOREACH. */ static void -TEST (int n_threads) +test_one_function (int n_threads, do_foreach_t do_foreach) { save_restore_n_threads saver; gdb::thread_pool::g_thread_pool->set_thread_count (n_threads); -#define NUMBER 10000 - - std::atomic<int> counter (0); - FOR_EACH (1, 0, NUMBER, - [&] (int start, int end) - { - counter += end - start; - }); - SELF_CHECK (counter == NUMBER); + /* Test with a few arbitrary number of items. */ + test_one (do_foreach, 0); + test_one (do_foreach, 1); + test_one (do_foreach, 1000); +} - counter = 0; - FOR_EACH (1, 0, 0, - [&] (int start, int end) +static void +test_parallel_for_each () +{ + struct test_worker + { + /* DUMMY is there to test passing multiple arguments to the worker + constructor. */ + test_worker (foreach_callback_t callback, int dummy) + : m_callback (callback) + { + } + + void operator() (iterator_range<int *> range) + { + return m_callback (range); + } + + private: + foreach_callback_t m_callback; + }; + + const std::vector<do_foreach_t> for_each_functions + { + /* Test gdb::parallel_for_each. */ + [] (int *start, int *end, foreach_callback_t callback) + { gdb::parallel_for_each<1, int *, test_worker> (start, end, callback, 0); }, + + /* Test gdb::parallel_for_each_async. */ + [] (int *start, int *end, foreach_callback_t callback) + { + bool done_flag = false; + std::condition_variable cv; + std::mutex mtx; + + gdb::parallel_for_each_async<1, int *, test_worker> (start, end, + [&mtx, &done_flag, &cv] () { - counter += end - start; - }); - SELF_CHECK (counter == 0); - -#undef NUMBER - - /* Check that if there are fewer tasks than threads, then we won't - end up with a null result. */ - std::vector<std::unique_ptr<int>> intresults; - std::atomic<bool> any_empty_tasks (false); - - FOR_EACH (1, 0, 1, - [&] (int start, int end) - { - if (start == end) - any_empty_tasks = true; - return std::make_unique<int> (end - start); - }); - SELF_CHECK (!any_empty_tasks); - SELF_CHECK (std::all_of (intresults.begin (), - intresults.end (), - [] (const std::unique_ptr<int> &entry) - { - return entry != nullptr; - })); + std::lock_guard<std::mutex> lock (mtx); + done_flag = true; + cv.notify_one(); + }, callback, 0); + + /* Wait for the async parallel-for to complete. */ + std::unique_lock<std::mutex> lock (mtx); + cv.wait (lock, [&done_flag] () { return done_flag; }); + }, + + /* Test gdb::sequential_for_each. */ + [] (int *start, int *end, foreach_callback_t callback) + { gdb::sequential_for_each<int *, test_worker> (start, end, callback, 0); }, + }; + + int default_thread_count = gdb::thread_pool::g_thread_pool->thread_count (); + + for (int n_threads : { 0, 1, 3, default_thread_count }) + for (const auto &for_each_function : for_each_functions) + test_one_function (n_threads, for_each_function); } -#endif /* FOR_EACH */ +} /* namespace parallel_for */ +} /* namespace selftests */ + +#endif /* CXX_STD_THREAD */ + +INIT_GDB_FILE (parallel_for_selftests) +{ +#ifdef CXX_STD_THREAD + selftests::register_test ("parallel_for", + selftests::parallel_for::test_parallel_for_each); +#endif /* CXX_STD_THREAD */ +} |