diff --git a/xo-alloc2/idl/Collector.json5 b/xo-alloc2/idl/Collector.json5 index 99904f88..3912ecdc 100644 --- a/xo-alloc2/idl/Collector.json5 +++ b/xo-alloc2/idl/Collector.json5 @@ -283,25 +283,26 @@ noexcept: false, attributes: [], }, - // void forward_inplace(AGCObject * lhs_iface, void ** lhs_data); - { - name: "forward_inplace", - doc: [ - "evacuate @p *lhs, that refers to state with interface @p lhs_iface,", - "to collector @p d's to-space. Replace *lhs_data with forwarding pointer", - "", - "Require: gc in progress", - "", - ], - return_type: "void", - args: [ - {type: "AGCObject *", name: "lhs_iface"}, - {type: "void **", name: "lhs_data"}, - ], - const: false, - noexcept: false, - attributes: [], - }, + // obsolete. use GCObjectVisitor.visit_child() +// // void forward_inplace(AGCObject * lhs_iface, void ** lhs_data); +// { +// name: "forward_inplace", +// doc: [ +// "evacuate @p *lhs, that refers to state with interface @p lhs_iface,", +// "to collector @p d's to-space. Replace *lhs_data with forwarding pointer", +// "", +// "Require: gc in progress", +// "", +// ], +// return_type: "void", +// args: [ +// {type: "AGCObject *", name: "lhs_iface"}, +// {type: "void **", name: "lhs_data"}, +// ], +// const: false, +// noexcept: false, +// attributes: [], +// }, ], router_facet_explicit_content: [ "/** convenience template for gc object copy **/", diff --git a/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp b/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp index 8d23384b..2765c6b2 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp @@ -123,12 +123,6 @@ Require: gc not in progress **/ Source must be owned by this collector. Increments object age **/ virtual void * alloc_copy(Opaque data, std::byte * src) = 0; - /** evacuate @p *lhs, that refers to state with interface @p lhs_iface, -to collector @p d's to-space. Replace *lhs_data with forwarding pointer - -Require: gc in progress - **/ - virtual void forward_inplace(Opaque data, AGCObject * lhs_iface, void ** lhs_data) = 0; ///@} }; /*ACollector*/ diff --git a/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp b/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp index 0a14751a..f4ab6bb0 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp @@ -76,7 +76,6 @@ namespace mm { [[noreturn]] void request_gc(Opaque, Generation) override; [[noreturn]] void assign_member(Opaque, void *, obj *, obj &) override; [[noreturn]] void * alloc_copy(Opaque, std::byte *) override; - [[noreturn]] void forward_inplace(Opaque, AGCObject *, void **) override; ///@} diff --git a/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp b/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp index 1a368235..50802f9d 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp @@ -93,9 +93,6 @@ namespace mm { void * alloc_copy(Opaque data, std::byte * src) override { return I::alloc_copy(_dcast(data), src); } - void forward_inplace(Opaque data, AGCObject * lhs_iface, void ** lhs_data) override { - return I::forward_inplace(_dcast(data), lhs_iface, lhs_data); - } ///@} diff --git a/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp b/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp index ac66a262..b7c54d44 100644 --- a/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp @@ -142,9 +142,6 @@ public: void * alloc_copy(std::byte * src) { return O::iface()->alloc_copy(O::data(), src); } - void forward_inplace(AGCObject * lhs_iface, void ** lhs_data) { - return O::iface()->forward_inplace(O::data(), lhs_iface, lhs_data); - } ///@} /** @defgroup mm-collector-member-vars **/ diff --git a/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp b/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp index 269d95bb..2743bfb4 100644 --- a/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp +++ b/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp @@ -71,12 +71,6 @@ ICollector_Any::alloc_copy(Opaque, std::byte *) -> void * _fatal(); } -auto -ICollector_Any::forward_inplace(Opaque, AGCObject *, void **) -> void -{ - _fatal(); -} - } /*namespace mm*/ } /*namespace xo*/ diff --git a/xo-gc/include/xo/gc/DX1Collector.hpp b/xo-gc/include/xo/gc/DX1Collector.hpp index aaa2b083..5eafa802 100644 --- a/xo-gc/include/xo/gc/DX1Collector.hpp +++ b/xo-gc/include/xo/gc/DX1Collector.hpp @@ -274,13 +274,18 @@ namespace xo { /** Execute gc immediately, for all generations < @p upto **/ void execute_gc(Generation upto) noexcept; +#ifdef OBSOLETE // replaced by visit_child() /** Evacuate object at @p *lhs_data to to-space. * Replace original with forwarding pointer to new location **/ void forward_inplace(AGCObject * lhs_iface, void ** lhs_data); +#endif - /** Supports the GCObjectVisitor facet. - * Synonym for forward_inplace + /** Supports GCObjectVisitor facet. + * During gc phase: + * 1. evacuate object at @p *lhs_data to to-space. + * 2. replace @p *lhs_data with forwarding pointer + * to new location. **/ void visit_child(AGCObject * lhs_iface, void ** lhs_data); diff --git a/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp b/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp index c81513f0..d9b43a5c 100644 --- a/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp +++ b/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp @@ -111,12 +111,6 @@ Require: gc not in progress **/ Source must be owned by this collector. Increments object age **/ static void * alloc_copy(DX1Collector & self, std::byte * src); - /** evacuate @p *lhs, that refers to state with interface @p lhs_iface, -to collector @p d's to-space. Replace *lhs_data with forwarding pointer - -Require: gc in progress - **/ - static void forward_inplace(DX1Collector & self, AGCObject * lhs_iface, void ** lhs_data); ///@} }; diff --git a/xo-gc/src/gc/DX1Collector.cpp b/xo-gc/src/gc/DX1Collector.cpp index 28636368..00bd9fb4 100644 --- a/xo-gc/src/gc/DX1Collector.cpp +++ b/xo-gc/src/gc/DX1Collector.cpp @@ -584,32 +584,13 @@ namespace xo { } } - void - DX1Collector::forward_inplace(AGCObject * lhs_iface, - void ** lhs_data) - { - // TODO: streamline once GCObject refactored so that - // forward_children takes GCObjectVisitor instead of Collector - // argument. - - Generation upto = runstate_.gc_upto(); - - if (runstate_.is_running()) { - // called during collection phase - gco_store_.forward_inplace_aux(this->ref(), lhs_iface, lhs_data, upto); - } else if (runstate_.is_verify()) { - // called during verify_ok - this->_verify_aux(lhs_iface, *lhs_data); - } else { - // should be unreachable - assert(false); - } - } - void DX1Collector::visit_child(AGCObject * lhs_iface, void ** lhs_data) { + // MAYBE: adapter distinct from DX1Collector that supports GCObjectVisitor facet, + // calls DX1Collector::_verify_aux() + if (runstate_.is_running()) { Generation upto = runstate_.gc_upto(); @@ -661,17 +642,17 @@ namespace xo { auto DX1Collector::super_alloc(typeseq t, size_type z) noexcept -> value_type { - return with_facet::mkobj(new_space()).super_alloc(t, z); + return with_facet::mkobj(this->new_space()).super_alloc(t, z); } auto DX1Collector::sub_alloc(size_type z, bool complete) noexcept -> value_type { - return with_facet::mkobj(new_space()).sub_alloc(z, complete); + return with_facet::mkobj(this->new_space()).sub_alloc(z, complete); } auto DX1Collector::alloc_copy(value_type src) noexcept -> value_type { - return with_facet::mkobj(new_space()).alloc_copy(src); + return with_facet::mkobj(this->new_space()).alloc_copy(src); } bool diff --git a/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp b/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp index a656ca6b..a20a9744 100644 --- a/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp +++ b/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp @@ -99,11 +99,6 @@ namespace xo { { return self.alloc_copy(src); } - auto - ICollector_DX1Collector::forward_inplace(DX1Collector & self, AGCObject * lhs_iface, void ** lhs_data) -> void - { - self.forward_inplace(lhs_iface, lhs_data); - } } /*namespace mm*/ } /*namespace xo*/ diff --git a/xo-gc/utest/CMakeLists.txt b/xo-gc/utest/CMakeLists.txt index 5ffcac82..599e1ea1 100644 --- a/xo-gc/utest/CMakeLists.txt +++ b/xo-gc/utest/CMakeLists.txt @@ -9,9 +9,21 @@ set(UTEST_SRCS DX1CollectorIterator.test.cpp GCObjectStore.test.cpp Object2.test.cpp + + DMockCollector.cpp + IGCObjectVisitor_DMockCollector.cpp + + init_gc_utest.cpp random_allocs.cpp ) +# mock collector for unit test +xo_add_genfacetimpl( + TARGET xo-gc-facetimpl-gcobjectvisitor-mockcollector + FACET_PKG xo_alloc2 + INPUT idl/IGCObjectVisitor_DMockCollector.json5 +) + if (ENABLE_TESTING) xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS}) xo_headeronly_dependency(${UTEST_EXE} randomgen) diff --git a/xo-gc/utest/DMockCollector.cpp b/xo-gc/utest/DMockCollector.cpp new file mode 100644 index 00000000..69f90508 --- /dev/null +++ b/xo-gc/utest/DMockCollector.cpp @@ -0,0 +1,37 @@ +/** @file DMockCollector.cpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#include "MockCollector.hpp" + +namespace xo { + namespace mm { + + Generation + DMockCollector::generation_of(Role r, const void * addr) const noexcept + { + return p_gco_store_->generation_of(r, addr); + } + + AllocInfo + DMockCollector::alloc_info(void * mem) const noexcept + { + return p_gco_store_->alloc_info((std::byte *)mem); + } + + void + DMockCollector::visit_child(AGCObject * lhs_iface, void ** lhs_data) + { + p_gco_store_->forward_inplace_aux(this->ref(), lhs_iface, lhs_data, upto_); + } + + std::byte * + DMockCollector::alloc_copy(void * src) noexcept { + return p_gco_store_->new_space()->alloc_copy((std::byte *)src); + } + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end DMockCollector.cpp */ diff --git a/xo-gc/utest/DMockCollector.hpp b/xo-gc/utest/DMockCollector.hpp new file mode 100644 index 00000000..ca1c0dfb --- /dev/null +++ b/xo-gc/utest/DMockCollector.hpp @@ -0,0 +1,40 @@ +/** @file DMockCollector.hpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#pragma once + +#include +#include + +namespace xo { + namespace mm { + + /** @brief Mock Collector + * + * Intended to help unit test a GCObjectSotre instance. + * Mock a Collector in collection phase for generations 0 <= g < @ref upto_. + **/ + class DMockCollector { + public: + explicit DMockCollector(GCObjectStore * gcos, Generation upto) : p_gco_store_{gcos}, upto_{upto} {} + + template + obj ref() { return obj(this); } + + Generation generation_of(Role r, const void * addr) const noexcept; + AllocInfo alloc_info(void * mem) const noexcept; + + void visit_child(AGCObject * lhs_iface, void ** lhs_data); + std::byte * alloc_copy(void * src) noexcept; + + private: + GCObjectStore * p_gco_store_ = nullptr; + Generation upto_; + }; + + } /*namespace mm*/ +} /*namespaace xo*/ + +/* end DMockCollector.hpp */ diff --git a/xo-gc/utest/IGCObjectVisitor_DMockCollector.cpp b/xo-gc/utest/IGCObjectVisitor_DMockCollector.cpp new file mode 100644 index 00000000..f4472874 --- /dev/null +++ b/xo-gc/utest/IGCObjectVisitor_DMockCollector.cpp @@ -0,0 +1,44 @@ +/** @file IGCObjectVisitor_DMockCollector.cpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/IGCObjectVisitor_DMockCollector.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/IGCObjectVisitor_DMockCollector.json5] +**/ + +#include "./IGCObjectVisitor_DMockCollector.hpp" + +namespace xo { + namespace mm { + auto + IGCObjectVisitor_DMockCollector::alloc_info(const DMockCollector & self, void * addr) -> AllocInfo + { + return self.alloc_info(addr); + } + + auto + IGCObjectVisitor_DMockCollector::generation_of(const DMockCollector & self, Role r, const void * addr) noexcept -> Generation + { + return self.generation_of(r, addr); + } + + auto + IGCObjectVisitor_DMockCollector::alloc_copy(DMockCollector & self, std::byte * src) -> void * + { + return self.alloc_copy(src); + } + auto + IGCObjectVisitor_DMockCollector::visit_child(DMockCollector & self, AGCObject * iface, void ** pp_data) noexcept -> void + { + self.visit_child(iface, pp_data); + } + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end IGCObjectVisitor_DMockCollector.cpp */ diff --git a/xo-gc/utest/IGCObjectVisitor_DMockCollector.hpp b/xo-gc/utest/IGCObjectVisitor_DMockCollector.hpp new file mode 100644 index 00000000..ac9db2bb --- /dev/null +++ b/xo-gc/utest/IGCObjectVisitor_DMockCollector.hpp @@ -0,0 +1,68 @@ +/** @file IGCObjectVisitor_DMockCollector.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/IGCObjectVisitor_DMockCollector.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_repr.hpp.j2] + * 3. idl for facet methods + * [idl/IGCObjectVisitor_DMockCollector.json5] + **/ + +#pragma once + +#include "GCObjectVisitor.hpp" +#include "DMockCollector.hpp" + +namespace xo { namespace mm { class IGCObjectVisitor_DMockCollector; } } + +namespace xo { + namespace facet { + template <> + struct FacetImplementation + { + using ImplType = xo::mm::IGCObjectVisitor_Xfer + ; + }; + } +} + +namespace xo { + namespace mm { + /** @class IGCObjectVisitor_DMockCollector + **/ + class IGCObjectVisitor_DMockCollector { + public: + /** @defgroup mm-gcobjectvisitor-dmockcollector-type-traits **/ + ///@{ + using Copaque = xo::mm::AGCObjectVisitor::Copaque; + using Opaque = xo::mm::AGCObjectVisitor::Opaque; + ///@} + /** @defgroup mm-gcobjectvisitor-dmockcollector-methods **/ + ///@{ + // const methods + /** allocation metadata for gc-aware data at address @p gco. +@p gco must be the result of a call to collector's alloc() function **/ + static AllocInfo alloc_info(const DMockCollector & self, void * addr); + /** generation to which pointer @p addr belongs, given role @p r; +sentinel if @p addr is not owned by collector **/ + static Generation generation_of(const DMockCollector & self, Role r, const void * addr) noexcept; + + // non-const methods + /** allocate copy of source object at address @p src. +Source must be owned by this collector. +Increments object age **/ + static void * alloc_copy(DMockCollector & self, std::byte * src); + /** visit child of a gc-aware object. May update child in-place! **/ + static void visit_child(DMockCollector & self, AGCObject * iface, void ** pp_data) noexcept; + ///@} + }; + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end */ \ No newline at end of file diff --git a/xo-gc/utest/MockCollector.hpp b/xo-gc/utest/MockCollector.hpp new file mode 100644 index 00000000..4e3f3d1b --- /dev/null +++ b/xo-gc/utest/MockCollector.hpp @@ -0,0 +1,13 @@ +/** @file MockCollector.hpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#pragma once + +#include "DMockCollector.hpp" +#include "IGCObjectVisitor_DMockCollector.hpp" +//#include "ICollector_DMockCollector.hpp" +//#include "IAllocator_DMockCollector.hpp" + +/* end MockCollector.hpp */ diff --git a/xo-gc/utest/gc_utest_main.cpp b/xo-gc/utest/gc_utest_main.cpp index cb1a31f3..5d4c4ea8 100644 --- a/xo-gc/utest/gc_utest_main.cpp +++ b/xo-gc/utest/gc_utest_main.cpp @@ -1,17 +1,19 @@ /* file gc_utest_main.cpp */ -#include +#include "init_gc_utest.hpp" #include #define CATCH_CONFIG_RUNNER #include -using xo::S_gc_tag; +using xo::S_gc_utest_tag; using xo::InitSubsys; using xo::InitEvidence; -// ensure xo-gc properly initialized when Subsystem::initialize_all() runs -static InitEvidence s_init = (InitSubsys::require()); +// ensure xo-gc/utest as mock subsystem +// properly initialized when Subsystem::initialize_all() runs +// +static InitEvidence s_init = (InitSubsys::require()); int main(int argc, char* argv[]) diff --git a/xo-gc/utest/idl/IGCObjectVisitor_DMockCollector.json5 b/xo-gc/utest/idl/IGCObjectVisitor_DMockCollector.json5 new file mode 100644 index 00000000..9d104bf7 --- /dev/null +++ b/xo-gc/utest/idl/IGCObjectVisitor_DMockCollector.json5 @@ -0,0 +1,27 @@ +{ + mode: "implementation", + output_cpp_dir: ".", + output_hpp_dir: "./", + output_impl_subdir: ".", + includes: [ +// "", +// "" + ], + local_types: [ +// { +// name: "typeseq", +// doc: ["identifies a c++ type"], +// definition: "xo::reflect::typeseq" +// }, + ], + namespace1: "xo", + namespace2: "mm", + facet_idl: "idl/GCObjectVisitor.json5", + brief: "provide AGCObjectVisitor interface for DMockCollector", + using_doxygen: true, + repr: "DMockCollector", + doc: [ + "Implement AGCObjectVisitor for DMockCollector.", + "Evacuate object pointer (migrate to to-space) during collection phase" + ], +} diff --git a/xo-gc/utest/init_gc_utest.cpp b/xo-gc/utest/init_gc_utest.cpp new file mode 100644 index 00000000..a88f6046 --- /dev/null +++ b/xo-gc/utest/init_gc_utest.cpp @@ -0,0 +1,48 @@ +/** @file init_gc_utest.cpp +* + * @author Roland Conybeare, Apr 2026 + **/ + +#include "init_gc_utest.hpp" +#include "MockCollector.hpp" +#include +#include +#include + +namespace xo { + using xo::mm::SetupGcUtest; + using xo::facet::FacetRegistry; + using xo::reflect::typeseq; + + bool + SetupGcUtest::register_facets() + { + scope log(XO_DEBUG(true)); + + FacetRegistry::register_impl(); + + log && log(xtag("DMockCollector.tseq", typeseq::id())); + + return true; + } + + void + InitSubsys::init() + { + SetupGcUtest::register_facets(); + } + + InitEvidence + InitSubsys::require() { + InitEvidence retval; + + /* recursive subsystem deps for xo-gc/utest */ + retval ^= InitSubsys::require(); + + /* xo-gc/utest/'s own initialization code */ + retval ^= Subsystem::provide("gc-utest", &init); + + return retval; + } + +} diff --git a/xo-gc/utest/init_gc_utest.hpp b/xo-gc/utest/init_gc_utest.hpp new file mode 100644 index 00000000..ccadf842 --- /dev/null +++ b/xo-gc/utest/init_gc_utest.hpp @@ -0,0 +1,34 @@ +/** @file init_gc_utest.hpp +* + * @author Roland Conybeare, Apr 2026 + **/ + +#pragma once + +#include + +namespace xo { + /* tag to represent xo-gc/utest/ as a mock subsystem within ordered initialization + * + * (so we can follow usual patterns for setting up facet tables) + */ + enum S_gc_utest_tag {}; + + template <> + struct InitSubsys { + static void init(); + static InitEvidence require(); + }; + + namespace mm { + + class SetupGcUtest { + public: + /** Register gc/utest (facet,impl) combinations with FacetRegistry **/ + static bool register_facets(); + }; + } /*namespace mm*/ + +} /*namespace xo*/ + +/* end init_gc_utest.hpp */