/* * Copyright (C) 2020-present ScyllaDB */ /* * SPDX-License-Identifier: AGPL-3.0-or-later */ #pragma once #include #include #include #include #include #include #include "utils/collection-concepts.hh" using namespace seastar; namespace utils { // Similar to std::merge but it does not stall. Must run inside a seastar // thread. It merges items from list2 into list1. Items from list2 can only be copied. template requires LessComparable void merge_to_gently(std::list& list1, const std::list& list2, Compare comp) { auto first1 = list1.begin(); auto first2 = list2.begin(); auto last1 = list1.end(); auto last2 = list2.end(); while (first2 != last2) { seastar::thread::maybe_yield(); if (first1 == last1) { // Copy remaining items of list2 into list1 list1.insert(last1, *first2); ++first2; continue; } if (comp(*first2, *first1)) { list1.insert(first1, *first2); ++first2; } else { ++first1; } } } // The clear_gently functions are meant for // gently destroying the contents of containers. // The containers can be re-used after clear_gently // or may be destroyed. But unlike e.g. std::vector::clear(), // clear_gently will not necessarily keep the object allocation. template concept HasClearGentlyMethod = requires (T x) { { x.clear_gently() } -> std::same_as>; }; template concept SmartPointer = requires (T x) { { *x } -> std::same_as; }; template concept SharedPointer = SmartPointer && requires (T x) { { x.use_count() } -> std::convertible_to; }; template concept StringLike = requires (T x) { std::is_same_v>; }; template concept Iterable = requires (T x) { { x.empty() } -> std::same_as; { x.begin() } -> std::same_as; { x.end() } -> std::same_as; }; template concept Sequence = Iterable && requires (T x, size_t n) { { x.back() } -> std::same_as; { x.pop_back() } -> std::same_as; }; template concept TriviallyClearableSequence = Sequence && std::is_trivially_destructible_v && !HasClearGentlyMethod && requires (T s) { { s.clear() } -> std::same_as; }; template concept Container = Iterable && requires (T x, typename T::iterator it) { { x.erase(it) } -> std::same_as; }; template concept MapLike = Container && requires (T x) { std::is_same_v>; }; template future<> clear_gently(T& o) noexcept; template future<> clear_gently(foreign_ptr& o) noexcept; template future<> clear_gently(T& o) noexcept; template future<> clear_gently(T& o) noexcept; template future<> clear_gently(std::array&a) noexcept; template requires (StringLike || TriviallyClearableSequence) future<> clear_gently(T& s) noexcept; template requires (!StringLike && !TriviallyClearableSequence) future<> clear_gently(T& v) noexcept; template future<> clear_gently(T& c) noexcept; template requires (!StringLike && !Sequence && !MapLike) future<> clear_gently(T& c) noexcept; namespace internal { template concept HasClearGentlyImpl = requires (T x) { { clear_gently(x) } -> std::same_as>; }; template requires HasClearGentlyImpl future<> clear_gently(T& x) noexcept { return utils::clear_gently(x); } // This default implementation of clear_gently // is required to "terminate" recursive clear_gently calls // at trivial objects template future<> clear_gently(T&) noexcept { return make_ready_future<>(); } } // namespace internal template future<> clear_gently(T& o) noexcept { return futurize_invoke(std::bind(&T::clear_gently, &o)); } template future<> clear_gently(foreign_ptr& o) noexcept { return smp::submit_to(o.get_owner_shard(), [&o] { return internal::clear_gently(*o); }); } template future<> clear_gently(T& o) noexcept { if (o.use_count() == 1) { return internal::clear_gently(*o); } return make_ready_future<>(); } template future<> clear_gently(T& o) noexcept { return internal::clear_gently(*o); } template future<> clear_gently(std::array& a) noexcept { return do_for_each(a, [] (T& o) { return internal::clear_gently(o); }); } // Trivially destructible elements can be safely cleared in bulk template requires (StringLike || TriviallyClearableSequence) future<> clear_gently(T& s) noexcept { // Note: clear() is pointless in this case since it keeps the allocation // and since the values are trivially destructible it achieves nothing. // `s = {}` will free the vector/string allocation. s = {}; return make_ready_future<>(); } // Clear the elements gently and destroy them one-by-one // in reverse order, to avoid copying. template requires (!StringLike && !TriviallyClearableSequence) future<> clear_gently(T& v) noexcept { return do_until([&v] { return v.empty(); }, [&v] { return internal::clear_gently(v.back()).then([&v] { v.pop_back(); }); }); return make_ready_future<>(); } template future<> clear_gently(T& c) noexcept { return do_until([&c] { return c.empty(); }, [&c] { auto it = c.begin(); return internal::clear_gently(it->second).then([&c, it = std::move(it)] () mutable { c.erase(it); }); }); } template requires (!StringLike && !Sequence && !MapLike) future<> clear_gently(T& c) noexcept { return do_until([&c] { return c.empty(); }, [&c] { auto it = c.begin(); return internal::clear_gently(*it).then([&c, it = std::move(it)] () mutable { c.erase(it); }); }); } }