xo-gc: refactor: demote GCObjectVisitor to GCObjectStore

No longer needed by DX1Collector
Also retires utest/MockCollector
This commit is contained in:
Roland Conybeare 2026-04-12 14:54:38 -04:00
commit 9e74e35c68
21 changed files with 242 additions and 331 deletions

View file

@ -27,9 +27,9 @@ xo_add_genfacetimpl(
# note: manual target; generated code committed to git
xo_add_genfacetimpl(
TARGET xo-gc-facetimpl-gcobjectvisitor-x1collector
TARGET xo-gc-facetimpl-gcobjectvisitor-gcobjectstorevisitor
FACET_PKG xo_alloc2
INPUT idl/IGCObjectVisitor_DX1Collector.json5
INPUT idl/IGCObjectVisitor_DGCObjectStoreVisitor.json5
)
# ----------------------------------------------------------------

View file

@ -0,0 +1,28 @@
{
mode: "implementation",
output_cpp_dir: "src/gc/facet",
output_hpp_dir: "include/xo/gc",
output_impl_subdir: "detail",
includes: [
// "<xo/alloc2/GCObject.hpp>",
// "<xo/alloc2/Allocator.hpp>"
],
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 DGCObjectStoreVisitor",
using_doxygen: true,
repr: "DGCObjectStoreVisitor",
doc: [
"Implement AGCObjectVisitor for DGCObjectStoreVisitor.",
"Visit a gc-aware object. Either evacuate+forward (for gc cycle),",
"or check consistency (for verify_ok)",
],
}

View file

@ -0,0 +1,49 @@
/** @file DGCObjectStoreVisitor.hpp
*
* @author Roland Conybeare, Apr 2026
**/
#pragma once
#include <xo/alloc2/GCObjectVisitor.hpp>
#include <xo/alloc2/VisitReason.hpp>
#include <xo/alloc2/Generation.hpp>
#include <xo/alloc2/role.hpp>
#include <xo/arena/AllocInfo.hpp>
#include <xo/facet/obj.hpp>
namespace xo {
namespace mm {
class GCObjectStore; // see GCObjectStore.hpp
class AGCObject; // see AGCObject.hpp
/** @brief visitor shim for GCObjectStore
*
* For a GC cycle, remembers which generations
* are being collected
**/
class DGCObjectStoreVisitor {
public:
DGCObjectStoreVisitor(GCObjectStore * gcos, Generation upto);
template <typename AFacet = AGCObjectVisitor>
obj<AFacet,DGCObjectStoreVisitor> ref() { return obj<AFacet,DGCObjectStoreVisitor>(this); }
Generation generation_of(Role r, const void * addr) const noexcept;
AllocInfo alloc_info(void * mem) const noexcept;
void visit_child(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data);
std::byte * alloc_copy(void * src) noexcept;
private:
/** object storage **/
GCObjectStore * p_gco_store_ = nullptr;
/** collecting generations up to this bound **/
Generation upto_;
};
} /*namespace mm*/
} /*namespace xo*/
/* end DGCObjectVisitor.hpp */

View file

@ -134,7 +134,7 @@ namespace xo {
* to call AGCObject visitor method (forward_children()) on each
* object stored here.
**/
void verify_ok(obj<AGCObjectVisitor> gc) noexcept;
void verify_ok() noexcept;
/** Register object type with this collector.
* Provides shallow copy and pointer forwarding for instances of this
@ -156,8 +156,7 @@ namespace xo {
*
* Require: runstate_.is_running()
**/
void * deep_move_root(obj<AGCObjectVisitor> gc,
const AGCObject * root_iface,
void * deep_move_root(const AGCObject * root_iface,
void ** root_data,
Generation upto);
@ -170,15 +169,16 @@ namespace xo {
void * from_src,
Generation upto);
#ifdef NOT_YET
/** Target for GCObjectVisitor facet
* During gc phase (@p reason is 'forward')
* 1. evacuate object at @p *lhs_data to to-space.
* 2. replace @p *lhs_data with forwarding pointer
* to new location.
**/
void visit_child(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data);
#endif
void visit_child_aux(VisitReason reason,
AGCObject * lhs_iface,
void ** lhs_data,
Generation upto);
/** Evacuate object at @p *lhs_data to to-space, during collection phase
* acting on generations g in [0 ,.., upto).

View file

@ -0,0 +1,11 @@
/** @file GCObjectStoreVisitor.hpp
*
* @author Roland Conybeare, Apr 2026
**/
#pragma once
#include "DGCObjectStoreVisitor.hpp"
#include "detail/IGCObjectVisitor_DGCObjectStoreVisitor.hpp"
/* end GCObjectStoreVisitor.hpp */

View file

@ -8,6 +8,5 @@
#include "DX1Collector.hpp"
#include "detail/ICollector_DX1Collector.hpp"
#include "detail/IAllocator_DX1Collector.hpp"
#include "detail/IGCObjectVisitor_DX1Collector.hpp"
/* end X1Collector.hpp */

View file

@ -1,64 +1,64 @@
/** @file IGCObjectVisitor_DX1Collector.hpp
/** @file IGCObjectVisitor_DGCObjectStoreVisitor.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IGCObjectVisitor_DX1Collector.json5]
* --input [idl/IGCObjectVisitor_DGCObjectStoreVisitor.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_repr.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObjectVisitor_DX1Collector.json5]
* [idl/IGCObjectVisitor_DGCObjectStoreVisitor.json5]
**/
#pragma once
#include "GCObjectVisitor.hpp"
#include "DX1Collector.hpp"
#include "DGCObjectStoreVisitor.hpp"
namespace xo { namespace mm { class IGCObjectVisitor_DX1Collector; } }
namespace xo { namespace mm { class IGCObjectVisitor_DGCObjectStoreVisitor; } }
namespace xo {
namespace facet {
template <>
struct FacetImplementation<xo::mm::AGCObjectVisitor,
xo::mm::DX1Collector>
xo::mm::DGCObjectStoreVisitor>
{
using ImplType = xo::mm::IGCObjectVisitor_Xfer
<xo::mm::DX1Collector,
xo::mm::IGCObjectVisitor_DX1Collector>;
<xo::mm::DGCObjectStoreVisitor,
xo::mm::IGCObjectVisitor_DGCObjectStoreVisitor>;
};
}
}
namespace xo {
namespace mm {
/** @class IGCObjectVisitor_DX1Collector
/** @class IGCObjectVisitor_DGCObjectStoreVisitor
**/
class IGCObjectVisitor_DX1Collector {
class IGCObjectVisitor_DGCObjectStoreVisitor {
public:
/** @defgroup mm-gcobjectvisitor-dx1collector-type-traits **/
/** @defgroup mm-gcobjectvisitor-dgcobjectstorevisitor-type-traits **/
///@{
using Copaque = xo::mm::AGCObjectVisitor::Copaque;
using Opaque = xo::mm::AGCObjectVisitor::Opaque;
///@}
/** @defgroup mm-gcobjectvisitor-dx1collector-methods **/
/** @defgroup mm-gcobjectvisitor-dgcobjectstorevisitor-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 DX1Collector & self, void * addr);
static AllocInfo alloc_info(const DGCObjectStoreVisitor & 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 DX1Collector & self, Role r, const void * addr) noexcept;
static Generation generation_of(const DGCObjectStoreVisitor & 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(DX1Collector & self, std::byte * src);
static void * alloc_copy(DGCObjectStoreVisitor & self, std::byte * src);
/** visit child of a gc-aware object. May update child in-place! **/
static void visit_child(DX1Collector & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept;
static void visit_child(DGCObjectStoreVisitor & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept;
///@}
};

View file

@ -12,10 +12,12 @@ set(SELF_SRCS
X1CollectorConfig.cpp
DX1Collector.cpp
facet/ICollector_DX1Collector.cpp
facet/IGCObjectVisitor_DX1Collector.cpp
DX1CollectorIterator.cpp
DGCObjectStoreVisitor.cpp
facet/IGCObjectVisitor_DGCObjectStoreVisitor.cpp
GCObjectStoreConfig.cpp
GCObjectStore.cpp

View file

@ -1,27 +1,34 @@
/** @file DMockCollector.cpp
/** @file DGCObjectStoreVisitor.cpp
*
* @author Roland Conybeare, Apr 2026
**/
#include "MockCollector.hpp"
#include "GCObjectStoreVisitor.hpp"
#include "GCObjectStore.hpp"
namespace xo {
namespace mm {
DGCObjectStoreVisitor::DGCObjectStoreVisitor(GCObjectStore * gcos,
Generation upto)
: p_gco_store_{gcos}, upto_{upto}
{}
Generation
DMockCollector::generation_of(Role r, const void * addr) const noexcept
DGCObjectStoreVisitor::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
DGCObjectStoreVisitor::alloc_info(void * mem) const noexcept
{
return p_gco_store_->alloc_info((std::byte *)mem);
}
void
DMockCollector::visit_child(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data)
DGCObjectStoreVisitor::visit_child(VisitReason reason,
AGCObject * lhs_iface, void ** lhs_data)
{
switch (reason.code()) {
case VisitReason::code::forward:
@ -37,11 +44,11 @@ namespace xo {
}
std::byte *
DMockCollector::alloc_copy(void * src) noexcept {
DGCObjectStoreVisitor::alloc_copy(void * src) noexcept {
return p_gco_store_->new_space()->alloc_copy((std::byte *)src);
}
} /*namespace mm*/
} /*namespace xo*/
/* end DMockCollector.cpp */
/* end DGCObjectVisitor.cpp */

View file

@ -4,6 +4,7 @@
**/
#include "X1Collector.hpp"
#include "GCObjectStoreVisitor.hpp"
#include <xo/gc/DX1CollectorIterator.hpp>
#include <xo/object2/Dictionary.hpp>
@ -13,7 +14,6 @@
#include <xo/stringtable2/String.hpp>
#include <xo/alloc2/GCObject.hpp>
#include <xo/alloc2/GCObjectVisitor.hpp>
#include <xo/alloc2/Allocator.hpp>
#include <xo/alloc2/Arena.hpp>
#include "object_age.hpp"
@ -383,7 +383,8 @@ namespace xo {
// Add run state so DX1Collector can recognize forward_inplace()
// calls made for the purpose of checking child pointers.
auto self = this->ref<AGCObjectVisitor>();
DGCObjectStoreVisitor visitor(&gco_store_,
Generation{0} /*not used for verify*/);
GCRunState saved_runstate = runstate_;
{
@ -404,8 +405,7 @@ namespace xo {
// - X1Collector::forward_inplace() -> _verify_aux()
//
gco.visit_gco_children(VisitReason::verify(), self);
gco.visit_gco_children(VisitReason::verify(), visitor.ref());
}
X1VerifyStats post = verify_stats_;
@ -417,7 +417,7 @@ namespace xo {
}
// 3. scan to-space for each generation
gco_store_.verify_ok(this->ref<AGCObjectVisitor>());
gco_store_.verify_ok();
// 4. scan mutation logs
mlog_store_.verify_ok(&gco_store_,
@ -483,7 +483,7 @@ namespace xo {
//auto t0 = std::chrono::steady_clock::now();
log && log("memory");
auto visitor = [&log](const MemorySizeInfo & info) {
auto resource_visitor = [&log](const MemorySizeInfo & info) {
log && log(xtag("resource", info.resource_name_),
xtag("used", info.used_),
xtag("alloc", info.allocated_),
@ -492,7 +492,7 @@ namespace xo {
xtag("lo", info.lo_),
xtag("hi", info.hi_));
};
this->visit_pools(visitor);
this->visit_pools(resource_visitor);
if (config_.sanitize_flag_) {
log && log("step 0a : verify");
@ -500,6 +500,8 @@ namespace xo {
}
DGCObjectStoreVisitor gco_visitor(&gco_store_, upto);
log && log("step 0b : update run state");
this->runstate_ = GCRunState::gc_upto(upto);
@ -519,7 +521,7 @@ namespace xo {
log && log("step 2b : [STUB] copy pinned");
log && log("step 3 : [STUB] forward mutation log");
mlog_store_.forward_mutation_log(this->ref<AGCObjectVisitor>(), upto);
mlog_store_.forward_mutation_log(gco_visitor.ref(), upto);
log && log("step 4a : [STUB] run destructors");
log && log("step 4b : [STUB] keep reachable weak pointers");
@ -576,8 +578,7 @@ namespace xo {
xtag("slot.root()", slot.root()),
xtag("slot.root()->data_", slot.root()->data_));
void * root_to = gco_store_.deep_move_root(this->ref<AGCObjectVisitor>(),
slot.root()->iface(),
void * root_to = gco_store_.deep_move_root(slot.root()->iface(),
(void **)&(slot.root()->data_), upto);
slot.root()->reset_opaque(root_to);
@ -594,24 +595,9 @@ namespace xo {
// MAYBE: adapter distinct from DX1Collector that supports GCObjectVisitor facet,
// calls DX1Collector::_verify_aux()
switch (reason.code()) {
case VisitReason::code::forward:
{
Generation upto = runstate_.gc_upto();
// called during collection phase
gco_store_.forward_inplace_aux
(this->ref<AGCObjectVisitor>(), lhs_iface, lhs_data, upto);
break;
}
case VisitReason::code::verify:
// called during verify_ok
gco_store_.verify_aux(lhs_iface, *lhs_data);
break;
default:
// should be unreachable
assert(false);
}
gco_store_.visit_child_aux(reason, lhs_iface, lhs_data, upto);
}
auto

View file

@ -4,6 +4,7 @@
**/
#include "GCObjectStore.hpp"
#include "GCObjectStoreVisitor.hpp"
#include "X1VerifyStats.hpp"
#include <xo/object2/Dictionary.hpp>
@ -475,35 +476,33 @@ namespace xo {
return (g < upto);
}
#ifdef NOT_YET
void
GCObjectStore::visit_child(VisitReason reason,
GCObjectStore::visit_child_aux(VisitReason reason,
AGCObject * lhs_iface,
void ** lhs_data)
void ** lhs_data,
Generation upto)
{
// MAYBE: adapter distinct from DX1Collector that supports GCObjectVisitor facet,
// calls DX1Collector::_verify_aux()
switch (reason.code()) {
case VisitReason::code::forward:
{
Generation upto = runstate_.gc_upto();
DGCObjectStoreVisitor gcos_visitor(this, upto);
auto gcos_visitor_obj
= obj<AGCObjectVisitor,DGCObjectStoreVisitor>(&gcos_visitor);
// called during collection phase
this->forward_inplace_aux
(this->ref<AGCObjectVisitor>(), lhs_iface, lhs_data, upto);
(gcos_visitor_obj, lhs_iface, lhs_data, upto);
break;
}
case VisitReason::code::verify:
// called during verify_ok
gco_store_.verify_aux(lhs_iface, *lhs_data);
this->verify_aux(lhs_iface, *lhs_data);
break;
default:
// should be unreachable
assert(false);
}
}
#endif
void
GCObjectStore::forward_inplace_aux(obj<AGCObjectVisitor> gc,
@ -768,8 +767,11 @@ namespace xo {
}
void
GCObjectStore::verify_ok(obj<AGCObjectVisitor> gc) noexcept
GCObjectStore::verify_ok() noexcept
{
Generation unused_gen;
DGCObjectStoreVisitor visitor{this, unused_gen};
for (Generation g(0); g < config_.n_generation_; ++g) {
const DArena * space = this->get_space(Role::to_space(), g);
@ -789,7 +791,7 @@ namespace xo {
// assembled fop for gc-aware object
obj<AGCObject> gco(iface, const_cast<void *>(data));
gco.visit_gco_children(VisitReason::verify(), gc);
gco.visit_gco_children(VisitReason::verify(), visitor.ref());
} else {
++(p_verify_stats_->n_no_iface_);
continue;
@ -830,8 +832,7 @@ namespace xo {
}
void *
GCObjectStore::deep_move_root(obj<AGCObjectVisitor> gc,
const AGCObject * root_iface,
GCObjectStore::deep_move_root(const AGCObject * root_iface,
void ** root_data,
Generation upto)
{
@ -849,8 +850,10 @@ namespace xo {
bool src_in_from_space = this->contains(Role::from_space(), *root_data);
DGCObjectStoreVisitor visitor(this, upto);
if (src_in_from_space) {
*root_data = this->_deep_move_gc_owned(gc, *root_data, upto);
*root_data = this->_deep_move_gc_owned(visitor.ref(), *root_data, upto);
} else {
// we aren't moving from_src, it's not gc-owned.
// However we are moving all its gc-owned children
@ -860,7 +863,7 @@ namespace xo {
auto root = obj<AGCObject>(root_iface, *root_data);
root.visit_gco_children(VisitReason::forward(), gc);
root.visit_gco_children(VisitReason::forward(), visitor.ref());
// For each generation g:
// traverse objects newer than gray_lo_v[g], to make sure children
@ -868,7 +871,7 @@ namespace xo {
// Remember that forwarding may promote objects to older generation,
// so need multiple passes
//
this->_forward_children_until_fixpoint(gc, upto, gray_lo_v);
this->_forward_children_until_fixpoint(visitor.ref(), upto, gray_lo_v);
// reminder: *root_data preserved

View file

@ -5,6 +5,7 @@
#include "SetupGc.hpp"
#include "X1Collector.hpp"
#include "GCObjectStoreVisitor.hpp"
#include <xo/facet/FacetRegistry.hpp>
#include <xo/indentlog/scope.hpp>
@ -24,9 +25,11 @@ namespace xo {
FacetRegistry::register_impl<AAllocator, DX1Collector>();
FacetRegistry::register_impl<ACollector, DX1Collector>();
FacetRegistry::register_impl<AGCObjectVisitor, DX1Collector>();
FacetRegistry::register_impl<AGCObjectVisitor, DGCObjectStoreVisitor>();
log && log(xtag("DX1Collector.tseq", typeseq::id<DX1Collector>()));
log && log(xtag("DGCObjectStoreVisitor.tseq", typeseq::id<DGCObjectStoreVisitor>()));
log && log(xtag("ACollector.tseq", typeseq::id<ACollector>()));
log && log(xtag("AGCObjectVisitor.tseq", typeseq::id<AGCObjectVisitor>()));

View file

@ -0,0 +1,44 @@
/** @file IGCObjectVisitor_DGCObjectStoreVisitor.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IGCObjectVisitor_DGCObjectStoreVisitor.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObjectVisitor_DGCObjectStoreVisitor.json5]
**/
#include "detail/IGCObjectVisitor_DGCObjectStoreVisitor.hpp"
namespace xo {
namespace mm {
auto
IGCObjectVisitor_DGCObjectStoreVisitor::alloc_info(const DGCObjectStoreVisitor & self, void * addr) -> AllocInfo
{
return self.alloc_info(addr);
}
auto
IGCObjectVisitor_DGCObjectStoreVisitor::generation_of(const DGCObjectStoreVisitor & self, Role r, const void * addr) noexcept -> Generation
{
return self.generation_of(r, addr);
}
auto
IGCObjectVisitor_DGCObjectStoreVisitor::alloc_copy(DGCObjectStoreVisitor & self, std::byte * src) -> void *
{
return self.alloc_copy(src);
}
auto
IGCObjectVisitor_DGCObjectStoreVisitor::visit_child(DGCObjectStoreVisitor & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept -> void
{
self.visit_child(reason, iface, pp_data);
}
} /*namespace mm*/
} /*namespace xo*/
/* end IGCObjectVisitor_DGCObjectStoreVisitor.cpp */

View file

@ -1,44 +0,0 @@
/** @file IGCObjectVisitor_DX1Collector.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IGCObjectVisitor_DX1Collector.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObjectVisitor_DX1Collector.json5]
**/
#include "detail/IGCObjectVisitor_DX1Collector.hpp"
namespace xo {
namespace mm {
auto
IGCObjectVisitor_DX1Collector::alloc_info(const DX1Collector & self, void * addr) -> AllocInfo
{
return self.alloc_info(addr);
}
auto
IGCObjectVisitor_DX1Collector::generation_of(const DX1Collector & self, Role r, const void * addr) noexcept -> Generation
{
return self.generation_of(r, addr);
}
auto
IGCObjectVisitor_DX1Collector::alloc_copy(DX1Collector & self, std::byte * src) -> void *
{
return self.alloc_copy(src);
}
auto
IGCObjectVisitor_DX1Collector::visit_child(DX1Collector & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept -> void
{
self.visit_child(reason, iface, pp_data);
}
} /*namespace mm*/
} /*namespace xo*/
/* end IGCObjectVisitor_DX1Collector.cpp */

View file

@ -10,20 +10,10 @@ set(UTEST_SRCS
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)

View file

@ -1,40 +0,0 @@
/** @file DMockCollector.hpp
*
* @author Roland Conybeare, Apr 2026
**/
#pragma once
#include <xo/gc/GCObjectStore.hpp>
#include <xo/arena/AllocInfo.hpp>
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 <typename AFacet>
obj<AFacet,DMockCollector> ref() { return obj<AFacet,DMockCollector>(this); }
Generation generation_of(Role r, const void * addr) const noexcept;
AllocInfo alloc_info(void * mem) const noexcept;
void visit_child(VisitReason reason, 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 */

View file

@ -5,8 +5,6 @@
#include <xo/gc/GCObjectStore.hpp>
#include <xo/gc/X1VerifyStats.hpp>
#include "MockCollector.hpp"
#include <xo/object2/ListOps.hpp>
#include <xo/object2/List.hpp>
#include <xo/object2/Integer.hpp>
@ -27,7 +25,6 @@ namespace ut {
using xo::scm::DList;
using xo::scm::DInteger;
using xo::scm::DBoolean;
using xo::mm::DMockCollector;
using xo::mm::GCObjectStoreConfig;
using xo::mm::GCObjectStore;
using xo::mm::X1VerifyStats;
@ -395,7 +392,6 @@ namespace ut {
const GCObjectStore & gcos)
{
Generation g0{0};
//Generation g1{1};
Generation gn{tc.n_gen_};
@ -413,6 +409,13 @@ namespace ut {
}
}
/** Generate two copies of a random object graph for test case @p tc.
* Store first graph in @p *p_x1_v, allocating
* entirely from @p p_gcos new-space.
* Store second graph in @p *p_x2_v, allocating
* entirely from @p p_arena2.
* Use random number generator @p_rgen
**/
void
gcos_construct_ab_object_graphs(const Testcase & tc,
GCObjectStore * p_gcos,
@ -444,24 +447,26 @@ namespace ut {
// typeseq::id<DBoolean>()));
}
/** Invoke built-in consistency verification for @p *p_gcos.
**/
void
gcos_verify_consistency(obj<AGCObjectVisitor> mock_gc_visitor,
GCObjectStore * p_gcos,
const X1VerifyStats & verify_stats)
gcos_verify_consistency(GCObjectStore * p_gcos)
{
// traverses stored objects, updates counters
// in verify_stats (= gco.p_verify_stats_, via ctor)
//
p_gcos->verify_ok(mock_gc_visitor);
p_gcos->verify_ok();
INFO(tostr(xtag("n_gc_root", verify_stats.n_gc_root_),
xtag("n_ext", verify_stats.n_ext_),
xtag("n_from", verify_stats.n_from_),
xtag("n_to", verify_stats.n_to_),
xtag("n_fwd", verify_stats.n_fwd_),
xtag("n_no_iface", verify_stats.n_no_iface_)));
X1VerifyStats * verify_stats = p_gcos->verify_stats();
REQUIRE(verify_stats.is_ok());
INFO(tostr(xtag("n_gc_root", verify_stats->n_gc_root_),
xtag("n_ext", verify_stats->n_ext_),
xtag("n_from", verify_stats->n_from_),
xtag("n_to", verify_stats->n_to_),
xtag("n_fwd", verify_stats->n_fwd_),
xtag("n_no_iface", verify_stats->n_no_iface_)));
REQUIRE(verify_stats->is_ok());
}
void
@ -635,7 +640,7 @@ namespace ut {
void
gcos_move_roots_and_verify(const Testcase & tc,
GCObjectStore * p_gcos,
obj<AGCObjectVisitor> mock_gc_visitor,
Generation upto,
const std::vector<Recd> & x1_v,
const std::vector<Recd> & x2_v,
bool debug_flag)
@ -690,9 +695,9 @@ namespace ut {
obj<AGCObject> x1_gco = x1.gco_;
// modifies x1.gco_ in place
auto x1p_data = p_gcos->deep_move_root(mock_gc_visitor,
x1p_iface, (void **)&(x1.gco_.data_),
g1);
auto x1p_data
= p_gcos->deep_move_root(x1p_iface, (void **)&(x1.gco_.data_), upto);
REQUIRE(x1p_data);
REQUIRE(x1p_data == x1.gco_.data_);
@ -713,8 +718,7 @@ namespace ut {
// but will fail since type isn't registered
auto x1p_data
= p_gcos->deep_move_root(mock_gc_visitor,
x1.gco_.iface(),
= p_gcos->deep_move_root(x1.gco_.iface(),
(void **)&(x1.gco_.data_),
g1);
@ -812,10 +816,6 @@ namespace ut {
Generation g1{1};
Generation gn{tc.n_gen_};
// scaffold mock collector doing incremental collection
DMockCollector mock_gc(&gcos, g1);
auto mock_gc_visitor = mock_gc.ref<AGCObjectVisitor>();
REQUIRE(gcos.is_type_installed(typeseq::id<DList>()) == false);
REQUIRE(gcos.is_type_installed(typeseq::id<DBoolean>()) == false);
@ -836,9 +836,7 @@ namespace ut {
log1 && log1("verify before any gcos side effects");
gcos_verify_consistency(mock_gc_visitor,
&gcos,
verify_stats);
gcos_verify_consistency(&gcos);
// someday: print the graph. Need a cycle-detecting printer
@ -851,7 +849,7 @@ namespace ut {
gcos_verify_gen0_fromspace_only_allocated(tc, gcos, x1_v);
gcos_move_roots_and_verify(tc, &gcos, mock_gc_visitor, x1_v, x2_v, tc.debug_flag_);
gcos_move_roots_and_verify(tc, &gcos, g1, x1_v, x2_v, tc.debug_flag_);
// Things to test:
// - deep_move_interior() // used from MutationLogStore
@ -870,7 +868,7 @@ namespace ut {
// traverses stored objects, updates counters
// in verify_stats (= gco.p_verify_stats_, via ctor)
//
gcos.verify_ok(mock_gc_visitor);
gcos.verify_ok();
INFO(tostr(xtag("n_gc_root", verify_stats.n_gc_root_),
xtag("n_ext", verify_stats.n_ext_),

View file

@ -1,44 +0,0 @@
/** @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, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept -> void
{
self.visit_child(reason, iface, pp_data);
}
} /*namespace mm*/
} /*namespace xo*/
/* end IGCObjectVisitor_DMockCollector.cpp */

View file

@ -1,68 +0,0 @@
/** @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<xo::mm::AGCObjectVisitor,
xo::mm::DMockCollector>
{
using ImplType = xo::mm::IGCObjectVisitor_Xfer
<xo::mm::DMockCollector,
xo::mm::IGCObjectVisitor_DMockCollector>;
};
}
}
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, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept;
///@}
};
} /*namespace mm*/
} /*namespace xo*/
/* end */

View file

@ -1,13 +0,0 @@
/** @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 */

View file

@ -4,7 +4,7 @@
**/
#include "init_gc_utest.hpp"
#include "MockCollector.hpp"
//#include "MockCollector.hpp"
#include <xo/gc/init_gc.hpp>
#include <xo/facet/FacetRegistry.hpp>
#include <xo/indentlog/scope.hpp>
@ -19,9 +19,9 @@ namespace xo {
{
scope log(XO_DEBUG(false));
FacetRegistry::register_impl<AGCObjectVisitor, DMockCollector>();
//FacetRegistry::register_impl<AGCObjectVisitor, DMockCollector>();
log && log(xtag("DMockCollector.tseq", typeseq::id<DMockCollector>()));
//log && log(xtag("DMockCollector.tseq", typeseq::id<DMockCollector>()));
return true;
}