aboutsummaryrefslogtreecommitdiff
path: root/gdb/unittests/parallel-for-selftests.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/unittests/parallel-for-selftests.c')
-rw-r--r--gdb/unittests/parallel-for-selftests.c187
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 */
+}