diff --git a/xo-alloc2/include/xo/alloc2/ArenaIterator.hpp b/xo-alloc2/include/xo/alloc2/ArenaIterator.hpp new file mode 100644 index 00000000..02b49e85 --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/ArenaIterator.hpp @@ -0,0 +1,15 @@ +/** @file ArenaIterator.hpp + * + * @author Roland Conybeare, May 2026 + **/ + +#pragma once + +// reminder: we can't put this AAllocIterator support in xo-arena +// because xo-arena is a dependency of xo-facet, which we're relying +// on here + +#include +#include "arena/IAllocIterator_DArenaIterator.hpp" + +/* end ArenaIterator.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp b/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp index 0512cd57..5a3ad8fd 100644 --- a/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp +++ b/xo-alloc2/include/xo/alloc2/arena/IAllocator_DArena.hpp @@ -72,8 +72,6 @@ namespace xo { * @p complete_flag to true. **/ static value_type sub_alloc(DArena &, size_type z, bool complete_flag); - /** allocate copy of @p src in arena @p d. **/ - static value_type alloc_copy(DArena & d, value_type src); static void clear(DArena &); /** perform assignment {*lhs_iface, *lhs_data} = {*rhs_iface, rhs_data} **/ static void barrier_assign_aux(DArena &, diff --git a/xo-alloc2/include/xo/alloc2/role.hpp b/xo-alloc2/include/xo/alloc2/role.hpp index 9b068a1d..cb8b9137 100644 --- a/xo-alloc2/include/xo/alloc2/role.hpp +++ b/xo-alloc2/include/xo/alloc2/role.hpp @@ -30,6 +30,8 @@ namespace xo { operator value_type() const { return role_; } + Role next() const { return Role(role_ + 1); } + value_type role_ = 0; }; } /*namespace mm*/ diff --git a/xo-alloc2/src/alloc2/IAllocator_Any.cpp b/xo-alloc2/src/alloc2/IAllocator_Any.cpp index f158e47c..813973c8 100644 --- a/xo-alloc2/src/alloc2/IAllocator_Any.cpp +++ b/xo-alloc2/src/alloc2/IAllocator_Any.cpp @@ -15,7 +15,8 @@ namespace xo { namespace mm { void - IAllocator_Any::_fatal() { + IAllocator_Any::_fatal() + { /* control here on uninitialized IAllocator_Any. * Initialized instance will have specific implementation type * e.g. IAllocator_Xfer diff --git a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp index 4d9a0a43..3f205a94 100644 --- a/xo-alloc2/src/alloc2/IAllocator_DArena.cpp +++ b/xo-alloc2/src/alloc2/IAllocator_DArena.cpp @@ -141,13 +141,6 @@ namespace xo { return s.sub_alloc(req_z, complete_flag); } - std::byte * - IAllocator_DArena::alloc_copy(DArena &s, - value_type src) - { - return s.alloc_copy(src); - } - void IAllocator_DArena::clear(DArena & s) { diff --git a/xo-alloc2/src/alloc2/SetupAlloc2.cpp b/xo-alloc2/src/alloc2/SetupAlloc2.cpp index caf8d573..da14c0ff 100644 --- a/xo-alloc2/src/alloc2/SetupAlloc2.cpp +++ b/xo-alloc2/src/alloc2/SetupAlloc2.cpp @@ -5,6 +5,7 @@ #include "SetupAlloc2.hpp" #include +#include #include #include @@ -21,8 +22,13 @@ namespace xo { scope log(XO_DEBUG(true)); FacetRegistry::register_impl(); + FacetRegistry::register_impl(); log && log(xtag("DArena.tseq", typeseq::id())); + log && log(xtag("DArenaIterator.tseq", typeseq::id())); + + log && log(xtag("AAllocator.tseq", typeseq::id())); + log && log(xtag("AAllocIterator.tseq", typeseq::id())); return true; } diff --git a/xo-alloc2/utest/CMakeLists.txt b/xo-alloc2/utest/CMakeLists.txt index 9f9ece5f..af2d3194 100644 --- a/xo-alloc2/utest/CMakeLists.txt +++ b/xo-alloc2/utest/CMakeLists.txt @@ -10,6 +10,7 @@ set(UTEST_SRCS # Collector.test.cpp # DX1CollectorIterator.test.cpp Generation.test.cpp + Role.test.cpp VisitReason.test.cpp dp.test.cpp random_allocs.cpp diff --git a/xo-alloc2/utest/Generation.test.cpp b/xo-alloc2/utest/Generation.test.cpp index 43d64389..e62359ea 100644 --- a/xo-alloc2/utest/Generation.test.cpp +++ b/xo-alloc2/utest/Generation.test.cpp @@ -39,4 +39,4 @@ namespace xo { } /*namespace ut*/ } /*namespace xo*/ -/* end Generation.hpp */ +/* end Generation.test.cpp */ diff --git a/xo-alloc2/utest/Role.test.cpp b/xo-alloc2/utest/Role.test.cpp new file mode 100644 index 00000000..d1de6fe4 --- /dev/null +++ b/xo-alloc2/utest/Role.test.cpp @@ -0,0 +1,46 @@ +/** @file Role.test.cpp + * + * @author Roland Conybeare, May 2026 + **/ + +#include "role.hpp" +#include + +namespace xo { + using xo::mm::Role; + + namespace ut { + + TEST_CASE("Role-1", "[Role]") + { + /* 1. there are two distinct valid roles, 'to' and 'from', + * 2. valid roles fall in interval [begin, end) + */ + + REQUIRE(Role::to_space() == Role::to_space()); + REQUIRE(Role::from_space() == Role::from_space()); + REQUIRE(Role::to_space() != Role::from_space()); + + Role x0 = Role::begin(); + { + bool ok = (x0 == Role::to_space() || x0 == Role::from_space()); + REQUIRE(ok); + } + REQUIRE(x0 != Role::end()); + + Role x1 = x0.next(); + REQUIRE(x1 != x0); + { + bool ok = (x1 == Role::to_space() || x1 == Role::from_space()); + REQUIRE(ok); + } + REQUIRE(x1 != Role::end()); + + Role x2 = x1.next(); + REQUIRE(x2 == Role::end()); + } + + } +} /*namespace xo*/ + +/* end Role.test.cpp */ diff --git a/xo-alloc2/utest/arena.test.cpp b/xo-alloc2/utest/arena.test.cpp index 0beb66d8..0a1c3e90 100644 --- a/xo-alloc2/utest/arena.test.cpp +++ b/xo-alloc2/utest/arena.test.cpp @@ -20,6 +20,7 @@ namespace xo { using xo::mm::AllocHeaderConfig; using xo::mm::ArenaConfig; using xo::mm::AllocHeader; + using xo::mm::AllocInfo; using xo::mm::padding; using xo::mm::error; using xo::facet::with_facet; @@ -223,6 +224,39 @@ namespace xo { REQUIRE(a1o.allocated() <= a1o.committed()); REQUIRE(a1o.allocated() + a1o.available() == a1o.committed()); REQUIRE(a1o.committed() <= a1o.reserved()); + + auto committed0_z = a1o.committed(); + + // also test alloc info + AllocInfo m0_info = a1o.alloc_info(m0); + { + REQUIRE(m0_info.size() >= z0); + + // {tseq, age}: feature disabled must be zero + REQUIRE(m0_info.tseq() == 0); + REQUIRE(m0_info.age() == 0); + + REQUIRE(m0_info.payload().first == m0); + REQUIRE(m0_info.payload().second >= m0 + z0); + + REQUIRE(m0_info.guard_z() == cfg.header_.guard_z_); + REQUIRE((uint32_t)m0_info.guard_byte() == (uint32_t)cfg.header_.guard_byte_); + } + + a1o.clear(); + + // allocated size got reset + REQUIRE(a1o.allocated() == 0); + // committed size unchanged + REQUIRE(a1o.committed() == committed0_z); + REQUIRE(a1o.last_error().error_ == error::ok); + REQUIRE(a1o.last_error().error_seq_ == 0); + + // allocator no longer contains m0 (now points to unallocated but committed memory + // (not exposed via AAllocator! + // REQUIRE(a1o.contains_allocated(m0) == false); + + REQUIRE(a1o.contains(m0)); } TEST_CASE("allocator-alloc-3", "[alloc2][Allocator]") diff --git a/xo-alloc2/utest/random_allocs.cpp b/xo-alloc2/utest/random_allocs.cpp index 2325140d..470ee840 100644 --- a/xo-alloc2/utest/random_allocs.cpp +++ b/xo-alloc2/utest/random_allocs.cpp @@ -39,6 +39,7 @@ namespace utest { bool AllocUtil::random_allocs(uint32_t n_alloc, + uint32_t max_alloc_z, bool catch_flag, xoshiro256ss * p_rgen, obj mm) @@ -64,6 +65,8 @@ namespace utest { double si = ngen(*p_rgen); double zi = ::pow(2.0, si); std::size_t z = ::ceil(zi); + if (z > max_alloc_z) + z = max_alloc_z; bool ok_flag = true; diff --git a/xo-alloc2/utest/random_allocs.hpp b/xo-alloc2/utest/random_allocs.hpp index ab0afc4e..5c726e40 100644 --- a/xo-alloc2/utest/random_allocs.hpp +++ b/xo-alloc2/utest/random_allocs.hpp @@ -36,6 +36,7 @@ namespace utest { * verify allocator behavior **/ static bool random_allocs(std::uint32_t n_alloc, + std::uint32_t max_alloc_z, bool catch_flag, xo::rng::xoshiro256ss * p_rgen, xo::facet::obj alloc); diff --git a/xo-arena/include/xo/arena/AllocInfo.hpp b/xo-arena/include/xo/arena/AllocInfo.hpp index 599e0b78..16878b9b 100644 --- a/xo-arena/include/xo/arena/AllocInfo.hpp +++ b/xo-arena/include/xo/arena/AllocInfo.hpp @@ -78,7 +78,7 @@ namespace xo { /** Number of guard bytes **/ size_type guard_z() const noexcept { return p_config_->guard_z_; } /** Value (fixed test pattern) of guard byte **/ - char guard_byte() const noexcept { return p_config_->guard_byte_; } + uint8_t guard_byte() const noexcept { return p_config_->guard_byte_; } ///@} diff --git a/xo-arena/include/xo/arena/DArena.hpp b/xo-arena/include/xo/arena/DArena.hpp index 13850d75..f485b3de 100644 --- a/xo-arena/include/xo/arena/DArena.hpp +++ b/xo-arena/include/xo/arena/DArena.hpp @@ -128,7 +128,7 @@ namespace xo { **/ bool contains(const void * addr) const noexcept { return (lo_ <= addr) && (addr < hi_); } - /** Truee iff address @p addr is owned by this arena and in allocated regions **/ + /** True iff address @p addr is owned by this arena and in allocated regions **/ bool contains_allocated(const void * addr) const noexcept { return (lo_ <= addr) && (addr < free_); } /** true if arena is mapped i.e. has a reserved address range **/ diff --git a/xo-gc/include/xo/gc/X1CollectorIterator.hpp b/xo-gc/include/xo/gc/X1CollectorIterator.hpp new file mode 100644 index 00000000..07a97dfe --- /dev/null +++ b/xo-gc/include/xo/gc/X1CollectorIterator.hpp @@ -0,0 +1,11 @@ +/** @file X1CollectorIterator.hpp + * + * @author Roland Conybeare, May 2026 + **/ + +#pragma once + +#include "DX1CollectorIterator.hpp" +#include "detail/IAllocIterator_DX1CollectorIterator.hpp" + +/* end X1CollectorIterator.hpp */ diff --git a/xo-gc/src/gc/setup_gc.cpp b/xo-gc/src/gc/setup_gc.cpp index 23beed55..7927390b 100644 --- a/xo-gc/src/gc/setup_gc.cpp +++ b/xo-gc/src/gc/setup_gc.cpp @@ -5,6 +5,7 @@ #include "setup_gc.hpp" #include "X1Collector.hpp" +#include "X1CollectorIterator.hpp" #include "GCObjectStoreVisitor.hpp" #include #include @@ -25,10 +26,12 @@ namespace xo { FacetRegistry::register_impl(); FacetRegistry::register_impl(); + FacetRegistry::register_impl(); FacetRegistry::register_impl(); log && log(xtag("DX1Collector.tseq", typeseq::id())); + log && log(xtag("DX1CollectorIterator.tseq", typeseq::id())); log && log(xtag("DGCObjectStoreVisitor.tseq", typeseq::id())); log && log(xtag("ACollector.tseq", typeseq::id())); diff --git a/xo-gc/utest/Collector.test.cpp b/xo-gc/utest/Collector.test.cpp index 9141b576..eff2eda1 100644 --- a/xo-gc/utest/Collector.test.cpp +++ b/xo-gc/utest/Collector.test.cpp @@ -39,6 +39,7 @@ namespace xo { using xo::mm::AllocHeader; using xo::mm::Generation; using xo::mm::c_max_generation; + using xo::facet::DVariantPlaceholder; using xo::facet::with_facet; using xo::reflect::typeseq; using xo::scope; @@ -60,7 +61,7 @@ namespace xo { REQUIRE(!gc1); REQUIRE(gc1.iface() != nullptr); REQUIRE(gc1.data() == nullptr); - REQUIRE(gc1._typeseq() == typeseq::id()); + REQUIRE(gc1._typeseq() == typeseq::id()); } TEST_CASE("DX1Collector-1", "[alloc2][gc][DX1Collector]") @@ -187,6 +188,15 @@ namespace xo { { scope log(XO_DEBUG(false), "DX1Collector alloc test"); + constexpr uint32_t c_n_alloc = 25; + constexpr uint32_t c_reserved_z = 4*1024*1024; + constexpr uint32_t c_max_alloc = c_reserved_z / c_n_alloc; + // allowance for per-alloc overhead + constexpr uint32_t c_max_alloc_payload = c_max_alloc - 32; + + static_assert(c_max_alloc > 0); + static_assert(c_max_alloc_payload < c_max_alloc); + ArenaConfig arena_cfg = { .name_ = "_test_unused", .size_ = 4*1024*1024, .store_header_flag_ = true, @@ -226,22 +236,31 @@ namespace xo { auto rng = rng::xoshiro256ss(seed); bool catch_flag = false; - REQUIRE(utest::AllocUtil::random_allocs(25, catch_flag, &rng, x1alloc)); + REQUIRE(utest::AllocUtil::random_allocs(c_n_alloc, c_max_alloc_payload, + catch_flag, &rng, x1alloc)); } TEST_CASE("collector-x1-alloc2", "[alloc2][gc]") { - scope log(XO_DEBUG(false), + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), "DX1Collector alloc test2"); - ArenaConfig arena_cfg = { .name_ = "_test_unused", - .size_ = 4*1024*1024, - .store_header_flag_ = true, - .header_ = AllocHeaderConfig(8 /*guard_z*/, - 0xfd /*guard-byte*/, - 0 /*tseq-bits*/, - 0 /*age-bits*/, - 16 /*size-bits*/), + constexpr uint32_t c_n_alloc = 25; + constexpr uint32_t c_reserved_z = 4*1024*1024; + constexpr uint32_t c_max_alloc = c_reserved_z / c_n_alloc; + // allowance for per-alloc overhead + constexpr uint32_t c_max_alloc_payload = c_max_alloc - 32; + + ArenaConfig arena_cfg = { + .name_ = "_test_unused", + .size_ = c_reserved_z, + .store_header_flag_ = true, + .header_ = AllocHeaderConfig(8 /*guard_z*/, + 0xfd /*guard-byte*/, + 0 /*tseq-bits*/, + 0 /*age-bits*/, + 16 /*size-bits*/), }; /* collector with one generation collapses to a non-generational copying collector */ @@ -277,7 +296,8 @@ namespace xo { // these are not gc-aware objects. // just testing ability to work as a low-level allocator - REQUIRE(utest::AllocUtil::random_allocs(25, false, &rng, x1alloc)); + REQUIRE(utest::AllocUtil::random_allocs(c_n_alloc, c_max_alloc_payload, + c_debug_flag, &rng, x1alloc)); } namespace { diff --git a/xo-gc/utest/DX1CollectorIterator.test.cpp b/xo-gc/utest/DX1CollectorIterator.test.cpp index e61c6a99..53d11ce0 100644 --- a/xo-gc/utest/DX1CollectorIterator.test.cpp +++ b/xo-gc/utest/DX1CollectorIterator.test.cpp @@ -179,8 +179,8 @@ namespace xo { obj ix = range.begin(); obj end_ix = range.end(); - REQUIRE(ix._typeseq() == typeseq::id()); - REQUIRE(end_ix._typeseq() == typeseq::id()); + REQUIRE(ix._typeseq() == typeseq::id()); + REQUIRE(end_ix._typeseq() == typeseq::id()); REQUIRE(ix.iface()); REQUIRE(ix.data()); REQUIRE(end_ix.iface()); diff --git a/xo-gc/utest/random_allocs.cpp b/xo-gc/utest/random_allocs.cpp index 2325140d..ff0e663e 100644 --- a/xo-gc/utest/random_allocs.cpp +++ b/xo-gc/utest/random_allocs.cpp @@ -39,6 +39,7 @@ namespace utest { bool AllocUtil::random_allocs(uint32_t n_alloc, + uint32_t max_alloc_z, bool catch_flag, xoshiro256ss * p_rgen, obj mm) @@ -64,6 +65,8 @@ namespace utest { double si = ngen(*p_rgen); double zi = ::pow(2.0, si); std::size_t z = ::ceil(zi); + if (z > max_alloc_z) + z = max_alloc_z; bool ok_flag = true; @@ -132,7 +135,7 @@ namespace utest { for (const byte * p = info.guard_lo().first; p != info.guard_lo().second; ++p) { - REQUIRE_ORFAIL(ok_flag, catch_flag, (char)*p == info.guard_byte()); + REQUIRE_ORFAIL(ok_flag, catch_flag, (uint8_t)*p == info.guard_byte()); } REQUIRE_ORFAIL(ok_flag, catch_flag, @@ -146,7 +149,7 @@ namespace utest { for (const byte * p = info.guard_hi().first; p != info.guard_hi().second; ++p) { - REQUIRE_ORFAIL(ok_flag, catch_flag, (char)*p == info.guard_byte()); + REQUIRE_ORFAIL(ok_flag, catch_flag, (uint8_t)*p == info.guard_byte()); } diff --git a/xo-gc/utest/random_allocs.hpp b/xo-gc/utest/random_allocs.hpp index ab0afc4e..6428a3f0 100644 --- a/xo-gc/utest/random_allocs.hpp +++ b/xo-gc/utest/random_allocs.hpp @@ -34,8 +34,12 @@ namespace utest { /** generate a random sequence of allocations. * verify allocator behavior + * + * Will not alloc more than n_alloc * max_alloc_z bytes + * (not counting allocator overhead) **/ static bool random_allocs(std::uint32_t n_alloc, + std::uint32_t max_alloc_z, bool catch_flag, xo::rng::xoshiro256ss * p_rgen, xo::facet::obj alloc);