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 <lakshmi.sreethar@scylladb.com>
This commit is contained in:
Lakshmi Narayanan Sreethar
2024-06-13 20:18:51 +05:30
parent 3c921d5712
commit 31414f54c6
4 changed files with 41 additions and 0 deletions

View File

@@ -6,6 +6,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <random>
#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<unsigned>(1, 1 << 12);
for (int i = 0; i < 100; ++i) {
utils::chunked_vector<uint8_t, 512> v;
const auto size = size_dist(rand);
utils::reserve_gently(v, size).get();
BOOST_REQUIRE_EQUAL(v.capacity(), size);
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -271,5 +271,24 @@ future<> clear_gently(seastar::optimized_optional<T>& opt) noexcept {
}
}
namespace internal {
template <typename T>
concept gently_reservable = requires(T x) {
{ x.capacity() } -> std::same_as<size_t>;
{ x.reserve_partial(10) } -> std::same_as<void>;
};
} // 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();
});
}
}