when building scylla with the standard library from GCC-14.2, shipped by
fedora 41, we have following build failure:
```
/home/kefu/.local/bin/clang++ -DDEBUG -DDEBUG_LSA_SANITIZER -DFMT_SHARED -DSANITIZE -DSCYLLA_BUILD_MODE=debug -DSCYLLA_ENABLE_ERROR_INJECTION -DSEASTAR_API_LEVEL=7 -DSEASTAR_DEBUG -DSEASTAR_DEBUG_PROMISE -DSEASTAR_DEBUG_SHARED_PTR -DSEASTAR_DEFAULT_ALLOCATOR -DSEASTAR_LOGGER_COMPILE_TIME_FMT -DSEASTAR_LOGGER_TYPE_STDOUT -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_SHUFFLE_TASK_QUEUE -DSEASTAR_SSTRING -DSEASTAR_TYPE_ERASE_MORE -DXXH_PRIVATE_API -DCMAKE_INTDIR=\"Debug\" -I/home/kefu/dev/scylladb -I/home/kefu/dev/scylladb/build/gen -I/home/kefu/dev/scylladb/seastar/include -I/home/kefu/dev/scylladb/build/seastar/gen/include -I/home/kefu/dev/scylladb/build/seastar/gen/src -isystem /home/kefu/dev/scylladb/abseil -g -Og -g -gz -std=gnu++23 -fvisibility=hidden -Wall -Werror -Wextra -Wno-error=deprecated-declarations -Wimplicit-fallthrough -Wno-c++11-narrowing -Wno-deprecated-copy -Wno-mismatched-tags -Wno-missing-field-initializers -Wno-overloaded-virtual -Wno-unsupported-friend -Wno-unused-parameter -ffile-prefix-map=/home/kefu/dev/scylladb/build=. -march=x86-64-v3 -mpclmul -Xclang -fexperimental-assignment-tracking=disabled -Werror=unused-result -fstack-clash-protection -fsanitize=address -fsanitize=undefined -MD -MT CMakeFiles/scylla-main.dir/Debug/init.cc.o -MF CMakeFiles/scylla-main.dir/Debug/init.cc.o.d -o CMakeFiles/scylla-main.dir/Debug/init.cc.o -c /home/kefu/dev/scylladb/init.cc
In file included from /home/kefu/dev/scylladb/init.cc:12:
In file included from /home/kefu/dev/scylladb/db/config.hh:20:
In file included from /home/kefu/dev/scylladb/locator/abstract_replication_strategy.hh:26:
/home/kefu/dev/scylladb/locator/tablets.hh:410:30: error: unexpected type name 'size_t': expected expression
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
/home/kefu/dev/scylladb/locator/tablets.hh:410:23: error: no member named 'irange' in namespace 'boost'
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ~~~~~~~^
/home/kefu/dev/scylladb/locator/tablets.hh:410:38: error: left operand of comma operator has no effect [-Werror,-Wunused-value]
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
3 errors generated.
[16/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/keys.cc.o
[17/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/counters.cc.o
[18/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/partition_slice_builder.cc.o
[19/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o
FAILED: CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o
/home/kefu/.local/bin/clang++ -DDEBUG -DDEBUG_LSA_SANITIZER -DFMT_SHARED -DSANITIZE -DSCYLLA_BUILD_MODE=debug -DSCYLLA_ENABLE_ERROR_INJECTION -DSEASTAR_API_LEVEL=7 -DSEASTAR_DEBUG -DSEASTAR_DEBUG_PROMISE -DSEASTAR_DEBUG_SHARED_PTR -DSEASTAR_DEFAULT_ALLOCATOR -DSEASTAR_LOGGER_COMPILE_TIME_FMT -DSEASTAR_LOGGER_TYPE_STDOUT -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_SHUFFLE_TASK_QUEUE -DSEASTAR_SSTRING -DSEASTAR_TYPE_ERASE_MORE -DXXH_PRIVATE_API -DCMAKE_INTDIR=\"Debug\" -I/home/kefu/dev/scylladb -I/home/kefu/dev/scylladb/build/gen -I/home/kefu/dev/scylladb/seastar/include -I/home/kefu/dev/scylladb/build/seastar/gen/include -I/home/kefu/dev/scylladb/build/seastar/gen/src -isystem /home/kefu/dev/scylladb/abseil -g -Og -g -gz -std=gnu++23 -fvisibility=hidden -Wall -Werror -Wextra -Wno-error=deprecated-declarations -Wimplicit-fallthrough -Wno-c++11-narrowing -Wno-deprecated-copy -Wno-mismatched-tags -Wno-missing-field-initializers -Wno-overloaded-virtual -Wno-unsupported-friend -Wno-unused-parameter -ffile-prefix-map=/home/kefu/dev/scylladb/build=. -march=x86-64-v3 -mpclmul -Xclang -fexperimental-assignment-tracking=disabled -Werror=unused-result -fstack-clash-protection -fsanitize=address -fsanitize=undefined -MD -MT CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o -MF CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o.d -o CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o -c /home/kefu/dev/scylladb/mutation_query.cc
In file included from /home/kefu/dev/scylladb/mutation_query.cc:12:
In file included from /home/kefu/dev/scylladb/schema/schema_registry.hh:17:
In file included from /home/kefu/dev/scylladb/replica/database.hh:11:
In file included from /home/kefu/dev/scylladb/locator/abstract_replication_strategy.hh:26:
/home/kefu/dev/scylladb/locator/tablets.hh:410:30: error: unexpected type name 'size_t': expected expression
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
/home/kefu/dev/scylladb/locator/tablets.hh:410:23: error: no member named 'irange' in namespace 'boost'
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ~~~~~~~^
/home/kefu/dev/scylladb/locator/tablets.hh:410:38: error: left operand of comma operator has no effect [-Werror,-Wunused-value]
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
In file included from /home/kefu/dev/scylladb/mutation_query.cc:12:
In file included from /home/kefu/dev/scylladb/schema/schema_registry.hh:17:
In file included from /home/kefu/dev/scylladb/replica/database.hh:37:
In file included from /home/kefu/dev/scylladb/db/snapshot-ctl.hh:20:
/home/kefu/dev/scylladb/tasks/task_manager.hh:403:54: error: no member named 'irange' in namespace 'boost'
403 | co_await coroutine::parallel_for_each(boost::irange(0u, smp::count), [&tm, id, &res, &func] (unsigned shard) -> future<> {
| ~~~~~~~^
4 errors generated.
```
so let's take the opportunity to switch from `boost::irange` to
`std::views::iota`.
in this change, we:
- switch from boost::irange to std::views::iota for better standard library compatibility
- retain boost::irange where step parameter is used, as std::views::iota doesn't support it
- this change partially modernizes our range usage while maintaining
- existing functionality
Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
Closes scylladb/scylladb#20924
370 lines
11 KiB
C++
370 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2021-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include "test/lib/scylla_test_case.hh"
|
|
|
|
#include <deque>
|
|
#include <random>
|
|
#include <ranges>
|
|
#include <algorithm>
|
|
|
|
#include "utils/lsa/chunked_managed_vector.hh"
|
|
#include "utils/managed_ref.hh"
|
|
#include "test/lib/log.hh"
|
|
|
|
#include <boost/range/algorithm/equal.hpp>
|
|
#include <boost/range/algorithm/reverse.hpp>
|
|
|
|
using namespace logalloc;
|
|
|
|
using disk_array = lsa::chunked_managed_vector<uint64_t>;
|
|
|
|
using deque = std::deque<int>;
|
|
|
|
SEASTAR_TEST_CASE(test_random_walk) {
|
|
region region;
|
|
allocating_section as;
|
|
with_allocator(region.allocator(), [&] {
|
|
auto rand = std::default_random_engine();
|
|
auto op_gen = std::uniform_int_distribution<unsigned>(0, 9);
|
|
auto nr_dist = std::geometric_distribution<size_t>(0.7);
|
|
deque d;
|
|
disk_array c;
|
|
for (auto i = 0; i != 1000000; ++i) {
|
|
if (i % 10000 == 0) {
|
|
region.full_compaction();
|
|
}
|
|
as(region, [&] {
|
|
auto op = op_gen(rand);
|
|
switch (op) {
|
|
case 0: {
|
|
auto n = rand();
|
|
c.push_back(n);
|
|
d.push_back(n);
|
|
break;
|
|
}
|
|
case 1: {
|
|
auto nr_pushes = nr_dist(rand);
|
|
for (auto i : std::views::iota(size_t(0), nr_pushes)) {
|
|
(void)i;
|
|
auto n = rand();
|
|
c.push_back(n);
|
|
d.push_back(n);
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
if (!d.empty()) {
|
|
auto n = d.back();
|
|
auto m = c.back();
|
|
BOOST_REQUIRE_EQUAL(n, m);
|
|
c.pop_back();
|
|
d.pop_back();
|
|
}
|
|
break;
|
|
}
|
|
case 3: {
|
|
c.reserve(nr_dist(rand));
|
|
break;
|
|
}
|
|
case 4: {
|
|
std::ranges::sort(c);
|
|
std::ranges::sort(d);
|
|
break;
|
|
}
|
|
case 5: {
|
|
if (!d.empty()) {
|
|
auto u = std::uniform_int_distribution<size_t>(0, d.size() - 1);
|
|
auto idx = u(rand);
|
|
auto m = c[idx];
|
|
auto n = c[idx];
|
|
BOOST_REQUIRE_EQUAL(m, n);
|
|
}
|
|
break;
|
|
}
|
|
case 6: {
|
|
c.clear();
|
|
d.clear();
|
|
break;
|
|
}
|
|
case 7: {
|
|
boost::reverse(c);
|
|
boost::reverse(d);
|
|
break;
|
|
}
|
|
case 8: {
|
|
c.clear();
|
|
d.clear();
|
|
break;
|
|
}
|
|
case 9: {
|
|
auto nr = nr_dist(rand);
|
|
c.resize(nr);
|
|
d.resize(nr);
|
|
break;
|
|
}
|
|
default:
|
|
abort();
|
|
}
|
|
});
|
|
BOOST_REQUIRE_EQUAL(c.size(), d.size());
|
|
BOOST_REQUIRE(boost::equal(c, d));
|
|
}
|
|
});
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
class exception_safety_checker {
|
|
uint64_t _live_objects = 0;
|
|
uint64_t _countdown = std::numeric_limits<uint64_t>::max();
|
|
public:
|
|
bool ok() const {
|
|
return !_live_objects;
|
|
}
|
|
void set_countdown(unsigned x) {
|
|
_countdown = x;
|
|
}
|
|
void add_live_object() {
|
|
if (!_countdown--) { // auto-clears
|
|
throw "ouch";
|
|
}
|
|
++_live_objects;
|
|
}
|
|
void del_live_object() {
|
|
--_live_objects;
|
|
}
|
|
};
|
|
|
|
class exception_safe_class {
|
|
exception_safety_checker* _esc;
|
|
public:
|
|
explicit exception_safe_class(exception_safety_checker& esc) : _esc(&esc) {
|
|
_esc->add_live_object();
|
|
}
|
|
exception_safe_class(const exception_safe_class& x) : _esc(x._esc) {
|
|
_esc->add_live_object();
|
|
}
|
|
exception_safe_class(exception_safe_class&&) = default;
|
|
~exception_safe_class() {
|
|
_esc->del_live_object();
|
|
}
|
|
exception_safe_class& operator=(const exception_safe_class& x) {
|
|
if (this != &x) {
|
|
auto tmp = x;
|
|
this->~exception_safe_class();
|
|
*this = std::move(tmp);
|
|
}
|
|
return *this;
|
|
}
|
|
exception_safe_class& operator=(exception_safe_class&&) = default;
|
|
};
|
|
|
|
SEASTAR_TEST_CASE(tests_constructor_exception_safety) {
|
|
region region;
|
|
allocating_section as;
|
|
with_allocator(region.allocator(), [&] {
|
|
as(region, [&] {
|
|
auto checker = exception_safety_checker();
|
|
auto v = std::vector<exception_safe_class>(100, exception_safe_class(checker));
|
|
checker.set_countdown(5);
|
|
try {
|
|
auto u = lsa::chunked_managed_vector<exception_safe_class>(v.begin(), v.end());
|
|
BOOST_REQUIRE(false);
|
|
} catch (...) {
|
|
v.clear();
|
|
BOOST_REQUIRE(checker.ok());
|
|
}
|
|
});
|
|
});
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(tests_reserve_partial) {
|
|
region region;
|
|
allocating_section as;
|
|
with_allocator(region.allocator(), [&] {
|
|
as(region, [&] {
|
|
auto rand = std::default_random_engine();
|
|
// use twice the max_chunk_capacity() as upper limit to test if
|
|
// reserve_partial() can reserve capacity across multiple chunks.
|
|
auto max_test_size = lsa::chunked_managed_vector<uint8_t>::max_chunk_capacity() * 2;
|
|
auto size_dist = std::uniform_int_distribution<unsigned>(1, max_test_size);
|
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
lsa::chunked_managed_vector<uint8_t> v;
|
|
const auto size = size_dist(rand);
|
|
while (v.capacity() != size) {
|
|
v.reserve_partial(size);
|
|
}
|
|
BOOST_REQUIRE_EQUAL(v.capacity(), size);
|
|
}
|
|
});
|
|
});
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(test_clear_and_release) {
|
|
region region;
|
|
allocating_section as;
|
|
|
|
with_allocator(region.allocator(), [&] {
|
|
lsa::chunked_managed_vector<managed_ref<uint64_t>> v;
|
|
|
|
for (uint64_t i = 1; i < 4000; ++i) {
|
|
as(region, [&] {
|
|
v.emplace_back(make_managed<uint64_t>(i));
|
|
});
|
|
}
|
|
|
|
v.clear_and_release();
|
|
});
|
|
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(test_chunk_reserve) {
|
|
region region;
|
|
allocating_section as;
|
|
|
|
for (auto conf :
|
|
{ // std::make_pair(reserve size, push count)
|
|
std::make_pair(0u, 4000u),
|
|
std::make_pair(100u, 4000u),
|
|
std::make_pair(200u, 4000u),
|
|
std::make_pair(1000u, 4000u),
|
|
std::make_pair(2000u, 4000u),
|
|
std::make_pair(3000u, 4000u),
|
|
std::make_pair(5000u, 4000u),
|
|
std::make_pair(500u, 8000u),
|
|
std::make_pair(1000u, 8000u),
|
|
std::make_pair(2000u, 8000u),
|
|
std::make_pair(8000u, 500u),
|
|
})
|
|
{
|
|
with_allocator(region.allocator(), [&] {
|
|
auto [reserve_size, push_count] = conf;
|
|
testlog.info("Testing reserve({}), {}x emplace_back()", reserve_size, push_count);
|
|
lsa::chunked_managed_vector<managed_ref<uint64_t>> v;
|
|
v.reserve(reserve_size);
|
|
uint64_t seed = rand();
|
|
for (uint64_t i = 0; i < push_count; ++i) {
|
|
as(region, [&] {
|
|
v.emplace_back(make_managed<uint64_t>(seed + i));
|
|
BOOST_REQUIRE(**v.begin() == seed);
|
|
});
|
|
}
|
|
auto v_it = v.begin();
|
|
for (uint64_t i = 0; i < push_count; ++i) {
|
|
BOOST_REQUIRE(**v_it++ == seed + i);
|
|
}
|
|
v.clear_and_release();
|
|
});
|
|
}
|
|
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(test_correctness_when_crossing_chunk_boundary) {
|
|
region region;
|
|
allocating_section as;
|
|
with_allocator(region.allocator(), [&] {
|
|
as(region, [&] {
|
|
size_t max_chunk_size = lsa::chunked_managed_vector<int>::max_chunk_capacity();
|
|
|
|
lsa::chunked_managed_vector<size_t> v;
|
|
for (size_t i = 0; i < (max_chunk_size + 1); i++) {
|
|
v.push_back(i);
|
|
}
|
|
BOOST_REQUIRE(v.back() == max_chunk_size);
|
|
BOOST_REQUIRE(v.at(max_chunk_size) == max_chunk_size);
|
|
v.pop_back();
|
|
BOOST_REQUIRE(v.back() == max_chunk_size - 1);
|
|
BOOST_REQUIRE(v.at(max_chunk_size - 1) == max_chunk_size - 1);
|
|
});
|
|
});
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
// Tests the case of make_room() invoked with last_chunk_capacity_deficit but _size not in
|
|
// the last reserved chunk.
|
|
SEASTAR_TEST_CASE(test_shrinking_and_expansion_involving_chunk_boundary) {
|
|
region region;
|
|
allocating_section as;
|
|
|
|
with_allocator(region.allocator(), [&] {
|
|
lsa::chunked_managed_vector<managed_ref<uint64_t>> v;
|
|
|
|
// Fill two chunks
|
|
v.reserve(2000);
|
|
for (uint64_t i = 0; i < 2000; ++i) {
|
|
as(region, [&] {
|
|
v.emplace_back(make_managed<uint64_t>(i));
|
|
});
|
|
}
|
|
|
|
// Make the last chunk smaller than max size to trigger the last_chunk_capacity_deficit path in make_room()
|
|
v.shrink_to_fit();
|
|
|
|
// Leave the last chunk reserved but empty
|
|
for (uint64_t i = 0; i < 1000; ++i) {
|
|
v.pop_back();
|
|
}
|
|
|
|
// Try to reserve more than the currently reserved capacity and trigger last_chunk_capacity_deficit path
|
|
// with _size not in the last chunk. Should not sigsegv.
|
|
v.reserve(8000);
|
|
|
|
for (uint64_t i = 0; i < 2000; ++i) {
|
|
as(region, [&] {
|
|
v.emplace_back(make_managed<uint64_t>(i));
|
|
});
|
|
}
|
|
|
|
v.clear_and_release();
|
|
});
|
|
|
|
return make_ready_future<>();
|
|
}
|
|
|
|
struct push_back_item {
|
|
std::unique_ptr<int> p;
|
|
push_back_item() = default;
|
|
push_back_item(int v) : p(std::make_unique<int>(v)) {}
|
|
push_back_item(const push_back_item& x) : push_back_item(x.value() + 1) {}
|
|
push_back_item(push_back_item&& x) noexcept : p(std::exchange(x.p, nullptr)) {}
|
|
|
|
int value() const noexcept { return *p; }
|
|
};
|
|
|
|
template <class VectorType>
|
|
static void do_test_push_back_using_existing_element(std::function<void (VectorType&, const managed_ref<push_back_item>&)> do_push_back) {
|
|
region region;
|
|
allocating_section as;
|
|
|
|
with_allocator(region.allocator(), [&] {
|
|
VectorType v;
|
|
as(region, [&] {
|
|
v.push_back(make_managed<push_back_item>(0));
|
|
for (int i = 0; i < 1000; i++) {
|
|
do_push_back(v, v.back());
|
|
}
|
|
});
|
|
for (int i = 0; i < 1000; i++) {
|
|
BOOST_REQUIRE_EQUAL(v[i]->value(), i);
|
|
}
|
|
});
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(test_push_back_using_existing_element) {
|
|
using chunked_managed_vector_type = lsa::chunked_managed_vector<managed_ref<push_back_item>>;
|
|
do_test_push_back_using_existing_element<chunked_managed_vector_type>([] (chunked_managed_vector_type& v, const managed_ref<push_back_item>& x) { v.push_back(make_managed<push_back_item>(*x)); });
|
|
do_test_push_back_using_existing_element<chunked_managed_vector_type>([] (chunked_managed_vector_type& v, const managed_ref<push_back_item>& x) { v.emplace_back(make_managed<push_back_item>(*x)); });
|
|
return make_ready_future<>();
|
|
}
|