diff --git a/include/seastar/core/loop.hh b/include/seastar/core/loop.hh index 15f4693792..015614cf46 100644 --- a/include/seastar/core/loop.hh +++ b/include/seastar/core/loop.hh @@ -503,17 +503,20 @@ struct has_iterator_category : std::false_type {}; template struct has_iterator_category::iterator_category >> : std::true_type {}; -template +template inline size_t -iterator_range_estimate_vector_capacity(Iterator begin, Sentinel end, IteratorCategory) { - // May be linear time below random_access_iterator_tag, but still better than reallocation - if constexpr (std::is_base_of_v) { - return std::distance(begin, end); +iterator_range_estimate_vector_capacity(Iterator begin, Sentinel end) { + if constexpr (std::forward_iterator && + std::forward_iterator) { + return std::ranges::distance(begin, end); + } else if constexpr (std::random_access_iterator && + std::random_access_iterator) { + return std::ranges::distance(begin, end); + } else { + // For InputIterators we can't estimate needed capacity + return 0; } - - // For InputIterators we can't estimate needed capacity - return 0; } } // namespace internal @@ -577,12 +580,11 @@ parallel_for_each(Iterator begin, Sentinel end, Func&& func) noexcept { memory::scoped_critical_alloc_section _; if (!f.available() || f.failed()) { if (!s) { - using itraits = std::iterator_traits; size_t n{0U}; if constexpr (internal::has_iterator_category::value) { // We need if-constexpr here because there exist iterators for which std::iterator_traits // does not have 'iterator_category' as member type - n = (internal::iterator_range_estimate_vector_capacity(begin, end, typename itraits::iterator_category{}) + 1); + n = (internal::iterator_range_estimate_vector_capacity(begin, end) + 1); } s = new parallel_for_each_state(n); } diff --git a/include/seastar/core/when_all.hh b/include/seastar/core/when_all.hh index a05d816a81..c6caf60b96 100644 --- a/include/seastar/core/when_all.hh +++ b/include/seastar/core/when_all.hh @@ -286,9 +286,9 @@ complete_when_all(std::vector&& futures, typename std::vector::i }); } -template +template inline auto -do_when_all(FutureIterator begin, FutureIterator end) noexcept { +do_when_all(FutureIterator begin, Sentinel end) noexcept { using itraits = std::iterator_traits; auto make_values_vector = [] (size_t size) noexcept { memory::scoped_critical_alloc_section _; @@ -297,10 +297,10 @@ do_when_all(FutureIterator begin, FutureIterator end) noexcept { return ret; }; std::vector ret = - make_values_vector(iterator_range_estimate_vector_capacity(begin, end, typename itraits::iterator_category())); + make_values_vector(iterator_range_estimate_vector_capacity(begin, end)); // Important to invoke the *begin here, in case it's a function iterator, // so we launch all computation in parallel. - std::move(begin, end, std::back_inserter(ret)); + std::ranges::move(begin, end, std::back_inserter(ret)); return complete_when_all(std::move(ret), ret.begin()); } @@ -508,14 +508,14 @@ inline auto when_all_succeed(FutOrFuncs&&... fut_or_funcs) noexcept { /// \param end an \c InputIterator designating the end of the range of futures /// \return an \c std::vector<> of all the valus in the input SEASTAR_MODULE_EXPORT -template ::value_type> +template ::value_type> requires requires (FutureIterator i) { *i++; { i != i } -> std::convertible_to; requires is_future>::value; } inline auto -when_all_succeed(FutureIterator begin, FutureIterator end) noexcept { +when_all_succeed(FutureIterator begin, Sentinel end) noexcept { using itraits = std::iterator_traits; using result_transform = internal::extract_values_from_futures_vector; try { diff --git a/include/seastar/coroutine/parallel_for_each.hh b/include/seastar/coroutine/parallel_for_each.hh index 6c1e7ab746..d3394df52a 100644 --- a/include/seastar/coroutine/parallel_for_each.hh +++ b/include/seastar/coroutine/parallel_for_each.hh @@ -128,9 +128,8 @@ public: } else { memory::scoped_critical_alloc_section _; if (_futures.empty()) { - using itraits = std::iterator_traits; if constexpr (seastar::internal::has_iterator_category::value) { - auto n = seastar::internal::iterator_range_estimate_vector_capacity(it, end, typename itraits::iterator_category{}); + auto n = seastar::internal::iterator_range_estimate_vector_capacity(it, end); _futures.reserve(n); } } diff --git a/tests/unit/futures_test.cc b/tests/unit/futures_test.cc index 0af2d825c8..51e6a7bae4 100644 --- a/tests/unit/futures_test.cc +++ b/tests/unit/futures_test.cc @@ -538,17 +538,30 @@ SEASTAR_TEST_CASE(test_when_all_iterator_range) { template void test_iterator_range_estimate() { - using iter_traits = std::iterator_traits; Container container{1,2,3}; BOOST_REQUIRE_EQUAL(internal::iterator_range_estimate_vector_capacity( - container.begin(), container.end(), typename iter_traits::iterator_category{}), 3); + container.begin(), container.end()), 3); } BOOST_AUTO_TEST_CASE(test_iterator_range_estimate_vector_capacity) { test_iterator_range_estimate>(); test_iterator_range_estimate>(); test_iterator_range_estimate>(); + { + int n = 42; + auto seq = std::views::iota(0, n); + BOOST_REQUIRE_EQUAL(internal::iterator_range_estimate_vector_capacity( + seq.begin(), seq.end()), n); + } + { + // for ranges that generate elements on-the-fly, advancing an iterator + // might actually consume or transform the underlying sequence, in this + // case, the function under test returns 0. + auto seq = std::views::iota(1); + BOOST_REQUIRE_EQUAL(internal::iterator_range_estimate_vector_capacity( + seq.begin(), seq.end()), 0); + } } // helper function for when_any tests