Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 09:29:08

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Utilities/detail/WorkerPool.hpp"
0012 
0013 #include <algorithm>
0014 #include <atomic>
0015 #include <iterator>
0016 #include <stdexcept>
0017 #include <thread>
0018 #include <vector>
0019 
0020 using namespace Acts;
0021 
0022 namespace ActsTests {
0023 
0024 BOOST_AUTO_TEST_SUITE(UtilitiesSuite)
0025 
0026 BOOST_AUTO_TEST_CASE(WorkerPoolVisitsEveryIndexOnce) {
0027   for (std::size_t nThreads : {0u, 1u, 2u, 4u, 7u}) {
0028     detail::WorkerPool pool(nThreads);
0029     BOOST_CHECK_EQUAL(pool.size(), std::max<std::size_t>(nThreads, 1));
0030     for (std::size_t n : {0u, 1u, 2u, 13u, 1000u}) {
0031       for (std::size_t grainSize : {0u, 1u, 3u, 5000u}) {
0032         std::vector<std::atomic<int>> visits(n);
0033         pool.parallelFor(
0034             n, [&](std::size_t i) { visits[i].fetch_add(1); }, grainSize);
0035         for (const auto& v : visits) {
0036           BOOST_CHECK_EQUAL(v.load(), 1);
0037         }
0038       }
0039     }
0040   }
0041 }
0042 
0043 BOOST_AUTO_TEST_CASE(WorkerPoolUsesThreads) {
0044   detail::WorkerPool pool(4);
0045   std::vector<std::thread::id> ids(64);
0046   std::atomic<int> waiting{0};
0047   pool.parallelFor(
0048       ids.size(),
0049       [&](std::size_t i) {
0050         ids[i] = std::this_thread::get_id();
0051         // Block the first indices until every thread has claimed one, so
0052         // the loop cannot be drained by a single thread
0053         if (i < 4) {
0054           waiting.fetch_add(1);
0055           while (waiting.load() < 4) {
0056             std::this_thread::yield();
0057           }
0058         }
0059       },
0060       1);
0061   std::ranges::sort(ids);
0062   auto unique = std::ranges::unique(ids);
0063   BOOST_CHECK_EQUAL(std::distance(ids.begin(), unique.begin()), 4);
0064 }
0065 
0066 BOOST_AUTO_TEST_CASE(WorkerPoolRethrows) {
0067   detail::WorkerPool pool(4);
0068   std::atomic<int> calls{0};
0069   BOOST_CHECK_THROW(pool.parallelFor(1000,
0070                                      [&](std::size_t i) {
0071                                        calls.fetch_add(1);
0072                                        if (i == 17) {
0073                                          throw std::runtime_error("failure");
0074                                        }
0075                                      }),
0076                     std::runtime_error);
0077   BOOST_CHECK_GE(calls.load(), 1);
0078 
0079   // The pool is still usable afterwards
0080   std::atomic<std::size_t> sum{0};
0081   pool.parallelFor(100, [&](std::size_t i) { sum.fetch_add(i); });
0082   BOOST_CHECK_EQUAL(sum.load(), 4950u);
0083 }
0084 
0085 BOOST_AUTO_TEST_CASE(WorkerPoolNestedAndConcurrentCalls) {
0086   detail::WorkerPool pool(4);
0087 
0088   // A nested call from a loop body runs serially instead of dead-locking
0089   std::atomic<std::size_t> nested{0};
0090   pool.parallelFor(8, [&](std::size_t) {
0091     pool.parallelFor(10, [&](std::size_t) { nested.fetch_add(1); });
0092   });
0093   BOOST_CHECK_EQUAL(nested.load(), 80u);
0094 
0095   // Cross-pool nesting must not lose the outer pool's nesting guard, or
0096   // attempt to reacquire its call mutex from either pool's loop body.
0097   detail::WorkerPool other(2);
0098   nested.store(0);
0099   pool.parallelFor(8, [&](std::size_t) {
0100     other.parallelFor(2, [&](std::size_t) {
0101       pool.parallelFor(3, [&](std::size_t) { nested.fetch_add(1); });
0102     });
0103     pool.parallelFor(4, [&](std::size_t) { nested.fetch_add(1); });
0104   });
0105   BOOST_CHECK_EQUAL(nested.load(), 80u);
0106 
0107   // Calls from different threads are serialized
0108   std::atomic<std::size_t> total{0};
0109   std::vector<std::thread> callers;
0110   for (int c = 0; c < 3; ++c) {
0111     callers.emplace_back([&] {
0112       for (int r = 0; r < 20; ++r) {
0113         pool.parallelFor(100, [&](std::size_t) { total.fetch_add(1); });
0114       }
0115     });
0116   }
0117   for (auto& caller : callers) {
0118     caller.join();
0119   }
0120   BOOST_CHECK_EQUAL(total.load(), 3u * 20u * 100u);
0121 }
0122 
0123 BOOST_AUTO_TEST_SUITE_END()
0124 
0125 }  // namespace ActsTests