diff --git a/include/xo/alloc2/alloc/AAllocIterator.hpp b/include/xo/alloc2/alloc/AAllocIterator.hpp index 6ce08d1..d8f4c42 100644 --- a/include/xo/alloc2/alloc/AAllocIterator.hpp +++ b/include/xo/alloc2/alloc/AAllocIterator.hpp @@ -14,9 +14,6 @@ namespace xo { using Copaque = const void *; using Opaque = void *; - /** forward decl for obj **/ - struct ObjAllocIterator; - /** @class AAllocIterator * @brief Abstract facet for iterating over allocs * diff --git a/include/xo/alloc2/alloc/AAllocator.hpp b/include/xo/alloc2/alloc/AAllocator.hpp index d2725e9..57802de 100644 --- a/include/xo/alloc2/alloc/AAllocator.hpp +++ b/include/xo/alloc2/alloc/AAllocator.hpp @@ -16,6 +16,9 @@ namespace xo { using Copaque = const void *; using Opaque = void *; + // see DArena.hpp + struct DArena; + /** @class AAllocator * @brief Abstract facet for allocation * @@ -81,7 +84,21 @@ namespace xo { * * Non-const @p d because may stash error details **/ - virtual AllocInfo alloc_info(Opaque d, value_type mem) const noexcept = 0; + virtual AllocInfo alloc_info(Copaque d, value_type mem) const noexcept = 0; + /** Ideally we want to control allocation for iterator here. + * Awkward to describe to compiler since we don't have vt yet. + * OTOH iteration over allocs is a niche feature. + * Consider alternatives: + * - put begin/end in separate interface. e.g. extend AAllocator + * - layer of indirection: begin/end return iterator factory. + * Then allocator can be passed to iterator factory separately. + * Helps because factory can be static + * - abandon allocator support in this case. Instead will need to + * reinstate uvt (unique variant), use heap + * - just pass DArena& for alloc-iterator-allocation + **/ + //virtual facet::vt begin(Copaque d, DArena & ialloc) const noexcept; + // virtual obj end() const noexcept = 0; /** expand committed space in arena @p d * to size at least @p z diff --git a/include/xo/alloc2/alloc/IAllocator_Any.hpp b/include/xo/alloc2/alloc/IAllocator_Any.hpp index ee03fa4..9de4170 100644 --- a/include/xo/alloc2/alloc/IAllocator_Any.hpp +++ b/include/xo/alloc2/alloc/IAllocator_Any.hpp @@ -6,6 +6,8 @@ #pragma once #include "AAllocator.hpp" +#include "AllocIterator.hpp" +#include #include namespace xo { @@ -40,9 +42,11 @@ namespace xo { [[noreturn]] size_type allocated(Copaque) const noexcept override { _fatal(); } [[noreturn]] bool contains(Copaque, const void *) const noexcept override { _fatal(); } [[noreturn]] AllocError last_error(Copaque) const noexcept override { _fatal(); } + [[noreturn]] AllocInfo alloc_info(Copaque, value_type) const noexcept override { _fatal(); } + // defn in .cpp - problematic to require compiler know vt defn here + //[[noreturn]] facet::vt begin(Copaque, DArena &) const noexcept override; // { _fatal(); } // non-const methods - [[noreturn]] AllocInfo alloc_info(Opaque, value_type) const noexcept override { _fatal(); } [[noreturn]] bool expand(Opaque, std::size_t) const noexcept override { _fatal(); } [[noreturn]] value_type alloc(Opaque, std::size_t) const override { _fatal(); } [[noreturn]] value_type super_alloc(Opaque, std::size_t) const override { _fatal(); } diff --git a/include/xo/alloc2/alloc/IAllocator_Xfer.hpp b/include/xo/alloc2/alloc/IAllocator_Xfer.hpp index 65caf85..79f76c8 100644 --- a/include/xo/alloc2/alloc/IAllocator_Xfer.hpp +++ b/include/xo/alloc2/alloc/IAllocator_Xfer.hpp @@ -42,7 +42,7 @@ namespace xo { // non-const methods - AllocInfo alloc_info(Opaque d, value_type mem) const noexcept override { + AllocInfo alloc_info(Copaque d, value_type mem) const noexcept override { return I::alloc_info(_dcast(d), mem); } bool expand(Opaque d, diff --git a/include/xo/alloc2/alloc/RAllocIterator.hpp b/include/xo/alloc2/alloc/RAllocIterator.hpp index fd5f983..6c2dd5e 100644 --- a/include/xo/alloc2/alloc/RAllocIterator.hpp +++ b/include/xo/alloc2/alloc/RAllocIterator.hpp @@ -22,6 +22,10 @@ namespace xo { RAllocIterator(Object::DataPtr data) : Object{std::move(data)} {} int32_t _typeseq() const noexcept { return O::iface()->_typeseq(); } + AllocInfo deref() const noexcept { return O::iface()->deref(O::data()); } + cmpresult compare(const obj & other) const noexcept { + return O::iface()->compare(O::data(), other); } + void next() noexcept { O::iface()->next(O::data()); } static bool _valid; }; @@ -36,6 +40,21 @@ namespace xo { struct RoutingFor { using RoutingType = xo::mm::RAllocIterator; }; + + /* also provide comparison */ + template + inline bool operator==(obj lhs, + obj rhs) + { + return lhs.compare(rhs).is_equal(); + } + + template + inline bool operator!=(obj lhs, + obj rhs) + { + return !lhs.compare(rhs).is_equal(); + } } } /*namespace xo*/ diff --git a/include/xo/alloc2/alloc/RAllocator.hpp b/include/xo/alloc2/alloc/RAllocator.hpp index 340dc24..e2ba55d 100644 --- a/include/xo/alloc2/alloc/RAllocator.hpp +++ b/include/xo/alloc2/alloc/RAllocator.hpp @@ -6,6 +6,7 @@ #pragma once #include "AAllocator.hpp" +#include "AllocIterator.hpp" #include #include diff --git a/include/xo/alloc2/arena/IAllocator_DArena.hpp b/include/xo/alloc2/arena/IAllocator_DArena.hpp index 2a0d606..e340ff3 100644 --- a/include/xo/alloc2/arena/IAllocator_DArena.hpp +++ b/include/xo/alloc2/arena/IAllocator_DArena.hpp @@ -46,7 +46,7 @@ namespace xo { static AllocError last_error(const DArena &) noexcept; /** retrieve allocation bookkeeping info for @p mem from arena @p d **/ - static AllocInfo alloc_info(DArena &, value_type mem) noexcept; + static AllocInfo alloc_info(const DArena &, value_type mem) noexcept; /** expand committed space in arena @p d * to size at least @p z * In practice will round up to a multiple of @ref page_z_. diff --git a/include/xo/alloc2/cmpresult.hpp b/include/xo/alloc2/cmpresult.hpp index 2b67d0a..0cc445c 100644 --- a/include/xo/alloc2/cmpresult.hpp +++ b/include/xo/alloc2/cmpresult.hpp @@ -46,7 +46,12 @@ namespace xo { /** print to stream **/ void display(std::ostream & os) const; - bool is_equal() const { return (err_ == comparison::comparable) && (cmp_ == 0); } + bool is_lesser() const { + return (err_ == comparison::comparable) && (cmp_ < 0); + } + bool is_equal() const { + return (err_ == comparison::comparable) && (cmp_ == 0); + } /* -1 -> invalid (sentinel) * 0 -> comparable diff --git a/include/xo/alloc2/gc/DX1Collector.hpp b/include/xo/alloc2/gc/DX1Collector.hpp index a637c99..5d2f614 100644 --- a/include/xo/alloc2/gc/DX1Collector.hpp +++ b/include/xo/alloc2/gc/DX1Collector.hpp @@ -191,6 +191,9 @@ namespace xo { /** true iff original alloc has been replaced by a forwarding pointer **/ bool is_forwarding_header(header_type hdr) const noexcept; + /** Retreive bookkeeping info for allocation at @p mem. **/ + AllocInfo alloc_info(value_type mem) const noexcept; + // ----- allocation ----- /** simple allocation. new allocs always in gen0 to-space **/ @@ -211,8 +214,6 @@ namespace xo { /** expand gen0 committed size to at least @p z. **/ bool expand(size_type z) noexcept; - /** Retreive bookkeeping info for allocation at @p mem. **/ - AllocInfo alloc_info(value_type mem) noexcept; // ----- iteration ----- diff --git a/include/xo/alloc2/gc/DX1CollectorIterator.hpp b/include/xo/alloc2/gc/DX1CollectorIterator.hpp index abd0d48..b510d30 100644 --- a/include/xo/alloc2/gc/DX1CollectorIterator.hpp +++ b/include/xo/alloc2/gc/DX1CollectorIterator.hpp @@ -42,6 +42,11 @@ namespace xo { bool is_valid() const noexcept { return (gc_ != nullptr); } bool is_invalid() const noexcept { return !is_valid(); } + generation gen_ix() const { return gen_ix_; } + generation gen_hi() const { return gen_hi_; } + DArenaIterator arena_ix() const { return arena_ix_; } + DArenaIterator arena_hi() const { return arena_hi_; } + /** fetch contents at current iterator position **/ AllocInfo deref() const noexcept; /** compare two iterators. To be comparable, diff --git a/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp b/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp index c5d2d20..958770c 100644 --- a/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp +++ b/include/xo/alloc2/gc/IAllocator_DX1Collector.hpp @@ -49,6 +49,8 @@ namespace xo { static bool contains(const DX1Collector & d, const void * p) noexcept; /** report last error, if any, for collector @p d **/ static AllocError last_error(const DX1Collector &) noexcept; + /** fetch allocation bookkeeping info **/ + static AllocInfo alloc_info(const DX1Collector & d, value_type mem) noexcept; /** always alloc in gen0 to-space **/ static value_type alloc(DX1Collector & d, size_type z) noexcept; @@ -56,8 +58,6 @@ namespace xo { static value_type sub_alloc(DX1Collector & d, size_type z, bool complete) noexcept; /** expand gen0 spaces (both from-space and to-space) **/ static bool expand(DX1Collector & d, size_type z) noexcept; - /** fetch allocation bookkeeping info **/ - static AllocInfo alloc_info(DX1Collector & d, value_type mem) noexcept; /** reset to empty state; clears all generations **/ static void clear(DX1Collector & d); diff --git a/src/alloc2/DX1Collector.cpp b/src/alloc2/DX1Collector.cpp index 8b033a7..141ce61 100644 --- a/src/alloc2/DX1Collector.cpp +++ b/src/alloc2/DX1Collector.cpp @@ -10,6 +10,7 @@ #include "gc/generation.hpp" #include "gc/object_age.hpp" #include +#include #include #include @@ -207,10 +208,10 @@ namespace xo { } AllocInfo - DX1Collector::alloc_info(value_type mem) noexcept { + DX1Collector::alloc_info(value_type mem) const noexcept { for (role ri : role::all()) { for (generation gj{0}; gj < config_.n_generation_; ++gj) { - DArena * arena = this->get_space(ri, gj); + const DArena * arena = this->get_space(ri, gj); assert(arena); @@ -221,21 +222,29 @@ namespace xo { } // deliberately attempt on nursery to-space, to capture error info + return sentinel - return this->new_space()->alloc_info(mem); + return this->get_space(role::to_space(), generation{0})->alloc_info(mem); } DX1CollectorIterator DX1Collector::begin() const noexcept { + scope log(XO_DEBUG(false)); + + const DArena * arena + = get_space(role::to_space(), + generation{0}); + return DX1CollectorIterator(this, generation{0}, generation{config_.n_generation_}, - DArenaIterator(), - DArenaIterator()); + arena->begin(), + arena->end()); } DX1CollectorIterator DX1Collector::end() const noexcept { + scope log(XO_DEBUG(false)); + generation gen_hi = generation{config_.n_generation_}; /** valid iterator for end points to end of last DArena. diff --git a/src/alloc2/DX1CollectorIterator.cpp b/src/alloc2/DX1CollectorIterator.cpp index 799b38a..1c351b2 100644 --- a/src/alloc2/DX1CollectorIterator.cpp +++ b/src/alloc2/DX1CollectorIterator.cpp @@ -26,19 +26,39 @@ namespace xo { void DX1CollectorIterator::normalize() noexcept { + scope log(XO_DEBUG(false), + xtag("gen_ix", gen_ix_), + xtag("gen_hi", gen_hi_), + xtag("arena_ix.pos", arena_ix_.pos_), + xtag("arena_hi.pos", arena_hi_.pos_)); + /* normalize: find lowest generation with non-empty to-space */ + if (arena_ix_.pos_ == arena_hi_.pos_) { + log && log(xtag("action", "look-lub-nonempty-gen")); - for (; gen_ix_ < gen_hi_; ++gen_ix_) { - const DArena * arena - = gc_->get_space(role::to_space(), gen_ix_); + if (gen_ix_ < gen_hi_) + ++gen_ix_; - arena_ix_ = arena->begin(); - arena_hi_ = arena->end(); + for (; gen_ix_ < gen_hi_; ++gen_ix_) { + const DArena * arena + = gc_->get_space(role::to_space(), gen_ix_); - if (arena_ix_ != arena_hi_) { - // normalization achieved! - break; + assert(arena); + + arena_ix_ = arena->begin(); + arena_hi_ = arena->end(); + + if (arena_ix_ != arena_hi_) { + // normalization achieved! + break; + } } + + log && log(xtag("gen_ix", gen_ix_), + xtag("arena_ix.pos", arena_ix_.pos_), + xtag("arena_hi.pos", arena_hi_.pos_)); + } else { + log && log(xtag("action", "noop")); } } @@ -81,9 +101,23 @@ namespace xo { void DX1CollectorIterator::next() noexcept { + scope log(XO_DEBUG(false), + xtag("arena_ix.arena", arena_ix_.arena_), + xtag("arena_ix.pos", arena_ix_.pos_), + xtag("arena_hi.arena", arena_hi_.arena_), + xtag("arena_hi.pos", arena_hi_.pos_)); + if (arena_ix_ != arena_hi_) { ++arena_ix_; + + log && log(xtag("++arena_ix.pos", arena_ix_.pos_)); + this->normalize(); + + log && log(xtag("arena_ix.arena", arena_ix_.arena_), + xtag("arena_ix.pos", arena_ix_.pos_)); + } else { + log && log(xtag("action", "arena-at-end")); } } } /*namespace mm*/ diff --git a/src/alloc2/IAllocator_Any.cpp b/src/alloc2/IAllocator_Any.cpp index 1d12c6c..081a78e 100644 --- a/src/alloc2/IAllocator_Any.cpp +++ b/src/alloc2/IAllocator_Any.cpp @@ -27,6 +27,14 @@ namespace xo { std::terminate(); } +#ifdef NOPE + vt + IAllocator_Any::begin(Copaque, DArena &) const noexcept + { + _fatal(); + } +#endif + int32_t IAllocator_Any::s_typeseq = typeseq::id(); diff --git a/src/alloc2/IAllocator_DArena.cpp b/src/alloc2/IAllocator_DArena.cpp index d023749..a34a224 100644 --- a/src/alloc2/IAllocator_DArena.cpp +++ b/src/alloc2/IAllocator_DArena.cpp @@ -60,7 +60,7 @@ namespace xo { } AllocInfo - IAllocator_DArena::alloc_info(DArena & s, value_type mem) noexcept + IAllocator_DArena::alloc_info(const DArena & s, value_type mem) noexcept { return s.alloc_info(mem); } diff --git a/src/alloc2/IAllocator_DX1Collector.cpp b/src/alloc2/IAllocator_DX1Collector.cpp index 89b9c2d..ea8cf76 100644 --- a/src/alloc2/IAllocator_DX1Collector.cpp +++ b/src/alloc2/IAllocator_DX1Collector.cpp @@ -86,7 +86,7 @@ namespace xo { } AllocInfo - IAllocator_DX1Collector::alloc_info(DX1Collector & d, value_type mem) noexcept + IAllocator_DX1Collector::alloc_info(const DX1Collector & d, value_type mem) noexcept { return d.alloc_info(mem); } diff --git a/utest/DX1CollectorIterator.test.cpp b/utest/DX1CollectorIterator.test.cpp index 8ef6be5..b1c3241 100644 --- a/utest/DX1CollectorIterator.test.cpp +++ b/utest/DX1CollectorIterator.test.cpp @@ -10,6 +10,8 @@ #include "gc/IAllocIterator_DX1CollectorIterator.hpp" #include "arena/ArenaConfig.hpp" #include "padding.hpp" +#include +#include #include namespace xo { @@ -22,6 +24,10 @@ namespace xo { using xo::mm::CollectorConfig; using xo::mm::ArenaConfig; using xo::mm::AllocHeaderConfig; + using xo::mm::cmpresult; + using xo::mm::padding; + using xo::facet::with_facet; + using std::byte; namespace ut { TEST_CASE("IAllocIterator_Xfer_DX1CollectorIterator", "[alloc2]") @@ -31,7 +37,7 @@ namespace xo { REQUIRE(IAllocIterator_Xfer::_valid); } - TEST_CASE("DX1CollectorIterator", "[alloc2][gc][DX1Collector]") + TEST_CASE("DX1CollectorIterator-1", "[alloc2][gc][DX1Collector]") { ArenaConfig arena_cfg = { .name_ = "_test_unused", .size_ = 4*1024*1024, @@ -55,8 +61,89 @@ namespace xo { REQUIRE(ix.is_valid()); REQUIRE(end_ix.is_valid()); - REQUIRE(ix == end_ix); + + /* verify obj 'fat pointer' packaging */ + obj ix_vt{&ix}; + obj end_ix_vt{&end_ix}; + + REQUIRE(ix_vt.iface()); + REQUIRE(ix_vt.data()); + REQUIRE(end_ix_vt.iface()); + REQUIRE(end_ix_vt.data()); + + cmpresult cmp = ix_vt.compare(end_ix_vt); + + REQUIRE(cmp.is_equal()); + REQUIRE(ix_vt == end_ix_vt); + } + + TEST_CASE("DX1CollectorIterator-2", "[alloc2][gc][DX1Collector]") + { + scope log(XO_DEBUG(false)); + + ArenaConfig arena_cfg = { .name_ = "_test_unused", + .size_ = 4*1024*1024, + .store_header_flag_ = true, + .header_ = AllocHeaderConfig(0 /*guard_z*/, + 0xfd /*guard_byte*/, + 0 /*tseq_bits*/, + 0 /*age_bits*/, + 16 /*size_bits*/), }; + CollectorConfig cfg = { .arena_config_ = arena_cfg, + .n_generation_ = 2, + .gc_trigger_v_ = {{64*1024, 1024*1024, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0}} }; + + DX1Collector gc = DX1Collector{cfg}; + + size_t z = 13; + byte * mem = gc.alloc(z); + + REQUIRE(mem != nullptr); + + log && log("should have iterators separated by one alloc"); + + auto ix = gc.begin(); + auto end_ix = gc.end(); + + REQUIRE(ix.is_valid()); + REQUIRE(end_ix.is_valid()); + REQUIRE(ix != end_ix); + + /* verify obj 'fat pointer' packaging */ + auto ix_vt = with_facet::mkobj(&ix); + auto end_ix_vt = with_facet::mkobj(&end_ix); + + REQUIRE(ix_vt.iface()); + REQUIRE(ix_vt.data()); + REQUIRE(end_ix_vt.iface()); + REQUIRE(end_ix_vt.data()); + + cmpresult cmp = ix_vt.compare(end_ix_vt); + + REQUIRE(cmp.is_lesser()); + REQUIRE(ix_vt != end_ix_vt); + + /* we only did one alloc, should be able + * to visit it + */ + auto info = ix_vt.deref(); + + REQUIRE(info.is_valid()); + REQUIRE(info.payload().first == mem); + REQUIRE(info.size() == padding::with_padding(z)); + + ix_vt.next(); + + log && log(xtag("ix.gen", ix.gen_ix()), + xtag("ix.arena_ix.arena", ix.arena_ix().arena_)); + log && log(xtag("end_ix.gen", end_ix.gen_ix()), + xtag("end_ix.arena_ix.arena", end_ix.arena_ix().arena_)); + + REQUIRE(ix_vt == end_ix_vt); } } /*namespace ut*/ } /*namespace xo*/