From 31414f54c640bbd4d9aa720b33b9ac6fe87ea13e Mon Sep 17 00:00:00 2001 From: Lakshmi Narayanan Sreethar Date: Thu, 13 Jun 2024 20:18:51 +0530 Subject: [PATCH] utils/stall_free: introduce reserve_gently Add reserve_gently() that can reserve memory without stalling by repeatedly calling reserve_partial() method of the passed container. Update the comments of existing reserve_partial() methods to mention this newly introduced reserve_gently() wrapper. Also, add test to verify the functionality. Signed-off-by: Lakshmi Narayanan Sreethar --- test/boost/stall_free_test.cc | 14 ++++++++++++++ utils/chunked_vector.hh | 4 ++++ utils/lsa/chunked_managed_vector.hh | 4 ++++ utils/stall_free.hh | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/test/boost/stall_free_test.cc b/test/boost/stall_free_test.cc index c273e9b12f..8b98fcbd4f 100644 --- a/test/boost/stall_free_test.cc +++ b/test/boost/stall_free_test.cc @@ -6,6 +6,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +#include + #include "test/lib/scylla_test_case.hh" #include "utils/stall_free.hh" #include "utils/small_vector.hh" @@ -530,3 +532,15 @@ SEASTAR_THREAD_TEST_CASE(test_clear_gently_vector_of_optimized_optionals) { utils::clear_gently(v).get(); BOOST_REQUIRE_EQUAL(cleared_gently, 1); } + +SEASTAR_THREAD_TEST_CASE(test_reserve_gently_with_chunked_vector) { + auto rand = std::default_random_engine(); + auto size_dist = std::uniform_int_distribution(1, 1 << 12); + + for (int i = 0; i < 100; ++i) { + utils::chunked_vector v; + const auto size = size_dist(rand); + utils::reserve_gently(v, size).get(); + BOOST_REQUIRE_EQUAL(v.capacity(), size); + } +} diff --git a/utils/chunked_vector.hh b/utils/chunked_vector.hh index c4906c3cae..06d3e9fa9d 100644 --- a/utils/chunked_vector.hh +++ b/utils/chunked_vector.hh @@ -191,6 +191,10 @@ public: /// /// Here, `do_until()` takes care of yielding between iterations when /// necessary. + /// + /// The recommended way to use this method is by calling utils::reserve_gently() from stall_free.hh + /// instead of looping with this method directly. utils::reserve_gently() will repeatedly call this + /// method to reserve the required quantity, yielding between calls when necessary. void reserve_partial(size_t n) { if (n > _capacity) { make_room(n, true); diff --git a/utils/lsa/chunked_managed_vector.hh b/utils/lsa/chunked_managed_vector.hh index 5aa3623075..b7fb9350f4 100644 --- a/utils/lsa/chunked_managed_vector.hh +++ b/utils/lsa/chunked_managed_vector.hh @@ -155,6 +155,10 @@ public: /// /// Here, `do_until()` takes care of yielding between iterations when /// necessary. + /// + /// The recommended way to use this method is by calling utils::reserve_gently() from stall_free.hh + /// instead of looping with this method directly. utils::reserve_gently() will repeatedly call this + /// method to reserve the required quantity, yielding between calls when necessary. void reserve_partial(size_t n) { if (n > _capacity) { return make_room(n, true); diff --git a/utils/stall_free.hh b/utils/stall_free.hh index 9f7162033e..04fb3fc655 100644 --- a/utils/stall_free.hh +++ b/utils/stall_free.hh @@ -271,5 +271,24 @@ future<> clear_gently(seastar::optimized_optional& opt) noexcept { } } +namespace internal { + +template +concept gently_reservable = requires(T x) { + { x.capacity() } -> std::same_as; + { x.reserve_partial(10) } -> std::same_as; + +}; + +} // namespace internal + +// reserve_gently gently reserves memory in containers which support partial reserve. +future<> reserve_gently(internal::gently_reservable auto& container, size_t size) { + return seastar::do_until([&container, size] { return container.capacity() == size; }, [&container, size]() { + container.reserve_partial(size); + return seastar::make_ready_future(); + }); +} + }