xo-gc stack: refactor: introduce GCObjectVisitor facet

Plan using to properly level GCObjectStore and MutationLogStore
below Collector.

[WIP] not used yet
This commit is contained in:
Roland Conybeare 2026-04-05 18:07:14 -04:00
commit 57688a826a
16 changed files with 632 additions and 7 deletions

View file

@ -35,6 +35,15 @@ xo_add_genfacet(
# ----------------------------------------------------------------
# note: manual target; generated code committed to git
xo_add_genfacet(
TARGET xo-alloc2-facet-gcobjectvisitor
FACET GCObjectVisitor
INPUT idl/GCObjectVisitor.json5
)
# ----------------------------------------------------------------
# note: manual target; generated code committed to git
xo_add_genfacet(
TARGET xo-alloc2-facet-resourcevisitor

View file

@ -0,0 +1,75 @@
{
mode: "facet",
output_cpp_dir: "src/alloc2/facet",
output_hpp_dir: "include/xo/alloc2",
output_impl_subdir: "gc",
includes: [
// "<xo/alloc2/Allocator.hpp>",
// "<xo/alloc2/Collector.hpp>",
// "<cstdint>",
// "<cstddef>",
],
// extra includes in GCObject.hpp, if any
user_hpp_includes: [
// "\"gc/RCollector_aux.hpp\"",
],
namespace1: "xo",
namespace2: "mm",
pretext: [
"// see GCObject.hpp, also in xo-alloc2/",
"namespace xo { namespace mm { class AGCObject; }}",
],
facet: "GCObjectVisitor",
detail_subdir: "gc",
brief: "gc-aware object visitor",
using_doxygen: true,
doc: [
"Visit a gc-aware object. Visitor can traverse and update child pointers in-place."
],
types: [
// using size_type = std::size_t
// {
// name: "size_type",
// doc: ["type for an amount of memory"],
// definition: "std::size_t",
// },
// {
// name: "AAllocator",
// doc: ["fomo allocator type"],
// definition: "xo::mm::AAllocator",
// },
// {
// name: "ACollector",
// doc: ["fomo collector type"],
// definition: "xo::mm::ACollector",
// },
],
const_methods: [
// size_type shallow_size() const noexcept
// {
// name: "shallow_size",
// doc: ["memory consumption for this instance"],
// return_type: "size_type",
// args: [],
// const: true,
// noexcept: true,
// attributes: [],
// },
],
nonconst_methods: [
// void visit_child(AGCObject * iface, void ** pp_data) noexcept;
{
name: "visit_child",
doc: ["visit child of a gc-aware object. May update child in-place!"],
return_type: "void",
args:[
{type: "AGCObject *", name: "iface"},
{type: "void **", name: "pp_data"},
],
const: true, // technical const. I/face not modified
noexcept: true,
attributes: [],
},
],
router_facet_explicit_content: []
}

View file

@ -0,0 +1,22 @@
/** @file GCObjectVisitor.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/GCObjectVisitor.json5]
* 2. jinja2 template for facet .hpp file:
* [facet.hpp.j2]
* 3. idl for facet methods
* [idl/GCObjectVisitor.json5]
**/
#pragma once
#include "gc/AGCObjectVisitor.hpp"
#include "gc/IGCObjectVisitor_Any.hpp"
#include "gc/IGCObjectVisitor_Xfer.hpp"
#include "gc/RGCObjectVisitor.hpp"
/* end GCObjectVisitor.hpp */

View file

@ -0,0 +1,79 @@
/** @file AGCObjectVisitor.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/GCObjectVisitor.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [abstract_facet.hpp.j2]
* 3. idl for facet methods
* [idl/GCObjectVisitor.json5]
**/
#pragma once
// includes (via {facet_includes})
#include <xo/facet/obj.hpp>
#include <xo/facet/facet_implementation.hpp>
#include <xo/facet/typeseq.hpp>
// see GCObject.hpp, also in xo-alloc2/
namespace xo { namespace mm { class AGCObject; }}
namespace xo {
namespace mm {
using Copaque = const void *;
using Opaque = void *;
/**
Visit a gc-aware object. Visitor can traverse and update child pointers in-place.
**/
class AGCObjectVisitor {
public:
/** @defgroup mm-gcobjectvisitor-type-traits **/
///@{
// types
/** integer identifying a type **/
using typeseq = xo::facet::typeseq;
using Copaque = const void *;
using Opaque = void *;
///@}
/** @defgroup mm-gcobjectvisitor-methods **/
///@{
// const methods
/** An uninitialized AGCObjectVisitor instance will have zero vtable pointer (per {linux,osx} abi).
* Use case for this is narrow. We go to some lengths to avoid null vtable pointers. For example
* obj<AFacet> will have non-null vtable (via IFacet_Any) with all methods terminating.
**/
bool _has_null_vptr() const noexcept { return *reinterpret_cast<const void * const *>(this) == nullptr; }
/** RTTI: unique id# for actual runtime data representation **/
virtual typeseq _typeseq() const noexcept = 0;
/** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/
virtual void _drop(Opaque d) const noexcept = 0;
// nonconst methods
/** visit child of a gc-aware object. May update child in-place! **/
virtual void visit_child(Opaque data, AGCObject * iface, void ** pp_data) const noexcept = 0;
///@}
}; /*AGCObjectVisitor*/
/** Implementation IGCObjectVisitor_DRepr of AGCObjectVisitor for state DRepr
* should provide a specialization:
*
* template <>
* struct xo::facet::FacetImplementation<AGCObjectVisitor, DRepr> {
* using Impltype = IGCObjectVisitor_DRepr;
* };
*
* then IGCObjectVisitor_ImplType<DRepr> --> IGCObjectVisitor_DRepr
**/
template <typename DRepr>
using IGCObjectVisitor_ImplType = xo::facet::FacetImplType<AGCObjectVisitor, DRepr>;
} /*namespace mm*/
} /*namespace xo*/
/* AGCObjectVisitor.hpp */

View file

@ -0,0 +1,88 @@
/** @file IGCObjectVisitor_Any.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/GCObjectVisitor.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/GCObjectVisitor.json5]
**/
#pragma once
#include "AGCObjectVisitor.hpp"
#include <xo/facet/obj.hpp>
namespace xo { namespace mm { class IGCObjectVisitor_Any; } }
namespace xo {
namespace facet {
template <>
struct FacetImplementation<xo::mm::AGCObjectVisitor,
DVariantPlaceholder>
{
using ImplType = xo::mm::IGCObjectVisitor_Any;
};
}
}
namespace xo {
namespace mm {
/** @class IGCObjectVisitor_Any
* @brief AGCObjectVisitor implementation for empty variant instance
**/
class IGCObjectVisitor_Any : public AGCObjectVisitor {
public:
/** @defgroup mm-gcobjectvisitor-any-type-traits **/
///@{
/** integer identifying a type **/
using typeseq = xo::facet::typeseq;
///@}
/** @defgroup mm-gcobjectvisitor-any-methods **/
///@{
const AGCObjectVisitor * iface() const { return std::launder(this); }
// from AGCObjectVisitor
// builtin methods
typeseq _typeseq() const noexcept override { return s_typeseq; }
[[noreturn]] void _drop(Opaque) const noexcept override { _fatal(); }
// const methods
// nonconst methods
[[noreturn]] void visit_child(Opaque, AGCObject *, void **) const noexcept override;
///@}
private:
/** @defgraoup mm-gcobjectvisitor-any-private-methods **/
///@{
[[noreturn]] static void _fatal();
///@}
public:
/** @defgroup mm-gcobjectvisitor-any-member-vars **/
///@{
static typeseq s_typeseq;
static bool _valid;
///@}
};
} /*namespace mm */
} /*namespace xo */
/* IGCObjectVisitor_Any.hpp */

View file

@ -0,0 +1,82 @@
/** @file IGCObjectVisitor_Xfer.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/GCObjectVisitor.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/GCObjectVisitor.json5]
**/
#pragma once
namespace xo {
namespace mm {
/** @class IGCObjectVisitor_Xfer
**/
template <typename DRepr, typename IGCObjectVisitor_DRepr>
class IGCObjectVisitor_Xfer : public AGCObjectVisitor {
public:
/** @defgroup mm-gcobjectvisitor-xfer-type-traits **/
///@{
/** actual implementation (not generated; often delegates to DRepr) **/
using Impl = IGCObjectVisitor_DRepr;
/** integer identifying a type **/
using typeseq = AGCObjectVisitor::typeseq;
///@}
/** @defgroup mm-gcobjectvisitor-xfer-methods **/
///@{
static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; }
static DRepr & _dcast(Opaque d) { return *(DRepr *)d; }
// from AGCObjectVisitor
// builtin methods
typeseq _typeseq() const noexcept override { return s_typeseq; }
void _drop(Opaque d) const noexcept override { _dcast(d).~DRepr(); }
// const methods
// non-const methods
void visit_child(Opaque data, AGCObject * iface, void ** pp_data) const noexcept override {
return I::visit_child(_dcast(data), iface, pp_data);
}
///@}
private:
using I = Impl;
public:
/** @defgroup mm-gcobjectvisitor-xfer-member-vars **/
///@{
/** typeseq for template parameter DRepr **/
static typeseq s_typeseq;
/** true iff satisfies facet implementation **/
static bool _valid;
///@}
};
template <typename DRepr, typename IGCObjectVisitor_DRepr>
xo::facet::typeseq
IGCObjectVisitor_Xfer<DRepr, IGCObjectVisitor_DRepr>::s_typeseq
= xo::facet::typeseq::id<DRepr>();
template <typename DRepr, typename IGCObjectVisitor_DRepr>
bool
IGCObjectVisitor_Xfer<DRepr, IGCObjectVisitor_DRepr>::_valid
= xo::facet::valid_facet_implementation<AGCObjectVisitor,
IGCObjectVisitor_Xfer>();
} /*namespace mm */
} /*namespace xo*/
/* end IGCObjectVisitor_Xfer.hpp */

View file

@ -0,0 +1,84 @@
/** @file RGCObjectVisitor.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/GCObjectVisitor.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/GCObjectVisitor.json5]
**/
#pragma once
#include "AGCObjectVisitor.hpp"
namespace xo {
namespace mm {
/** @class RGCObjectVisitor
**/
template <typename Object>
class RGCObjectVisitor : public Object {
private:
using O = Object;
public:
/** @defgroup mm-gcobjectvisitor-router-type-traits **/
///@{
using ObjectType = Object;
using DataPtr = Object::DataPtr;
using typeseq = xo::reflect::typeseq;
///@}
/** @defgroup mm-gcobjectvisitor-router-ctors **/
///@{
RGCObjectVisitor() {}
RGCObjectVisitor(Object::DataPtr data) : Object{std::move(data)} {}
RGCObjectVisitor(const AGCObjectVisitor * iface, void * data)
requires std::is_same_v<typename Object::DataType, xo::facet::DVariantPlaceholder>
: Object(iface, data) {}
///@}
/** @defgroup mm-gcobjectvisitor-router-methods **/
///@{
// explicit injected content
// builtin methods
typeseq _typeseq() const noexcept { return O::iface()->_typeseq(); }
void _drop() const noexcept { O::iface()->_drop(O::data()); }
// const methods
// non-const methods (still const in router!)
void visit_child(AGCObject * iface, void ** pp_data) noexcept {
return O::iface()->visit_child(O::data(), iface, pp_data);
}
///@}
/** @defgroup mm-gcobjectvisitor-member-vars **/
///@{
static bool _valid;
///@}
};
template <typename Object>
bool
RGCObjectVisitor<Object>::_valid = xo::facet::valid_object_router<Object>();
} /*namespace mm*/
} /*namespace xo*/
namespace xo { namespace facet {
template <typename Object>
struct RoutingFor<xo::mm::AGCObjectVisitor, Object> {
using RoutingType = xo::mm::RGCObjectVisitor<Object>;
};
} }
/* end RGCObjectVisitor.hpp */

View file

@ -9,7 +9,9 @@ set(SELF_SRCS
CollectorTypeRegistry.cpp
facet/ICollector_Any.cpp
IGCObject_Any.cpp
facet/IGCObjectVisitor_Any.cpp
AAllocator.cpp
IAllocator_Any.cpp

View file

@ -0,0 +1,48 @@
/** @file IGCObjectVisitor_Any.cpp
*
**/
#include "gc/IGCObjectVisitor_Any.hpp"
#include <iostream>
#include <exception>
namespace xo {
namespace mm {
using xo::facet::DVariantPlaceholder;
using xo::facet::typeseq;
using xo::facet::valid_facet_implementation;
void
IGCObjectVisitor_Any::_fatal()
{
/* control here on uninitialized IAllocator_Any.
* Initialized instance will have specific implementation type
*/
std::cerr << "fatal"
<< ": attempt to call uninitialized"
<< " IGCObjectVisitor_Any method"
<< std::endl;
std::terminate();
}
typeseq
IGCObjectVisitor_Any::s_typeseq = typeseq::id<DVariantPlaceholder>();
bool
IGCObjectVisitor_Any::_valid
= valid_facet_implementation<AGCObjectVisitor, IGCObjectVisitor_Any>();
// nonconst methods
auto
IGCObjectVisitor_Any::visit_child(Opaque, AGCObject *, void **) const noexcept -> void
{
_fatal();
}
} /*namespace mm*/
} /*namespace xo*/
/* end IGCObjectVisitor_Any.cpp */

View file

@ -25,6 +25,13 @@ xo_add_genfacetimpl(
INPUT idl/ICollector_DX1Collector.json5
)
# note: manual target; generated code committed to git
xo_add_genfacetimpl(
TARGET xo-gc-facetimpl-gcobjectvisitor-x1collector
FACET_PKG xo_alloc2
INPUT idl/IGCObjectVisitor_DX1Collector.json5
)
# ----------------------------------------------------------------
xo_add_genfacet_all(xo-gc-genfacet-all)

View file

@ -0,0 +1,27 @@
{
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 DX1Collector",
using_doxygen: true,
repr: "DX1Collector",
doc: [
"Implement AGCObjectVisitor for DX1Collector.",
"Evacuate object pointer (migrate to to-space) during collection phase"
],
}

View file

@ -279,14 +279,10 @@ namespace xo {
**/
void forward_inplace(AGCObject * lhs_iface, void ** lhs_data);
#ifdef OBSOLETE
/** true iff {alloc_hdr, object_data} should move for
* currently-running collection.
*
* Require: runstate_.is_running()
/** Supports the GCObjectVisitor facet.
* Synonym for forward_inplace
**/
bool check_move_policy(header_type alloc_hdr, void * object_data) const noexcept;
#endif
void visit_child(AGCObject * lhs_iface, void ** lhs_data);
// ----- allocation -----

View file

@ -0,0 +1,58 @@
/** @file IGCObjectVisitor_DX1Collector.hpp
*
* 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_repr.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObjectVisitor_DX1Collector.json5]
**/
#pragma once
#include "GCObjectVisitor.hpp"
#include "DX1Collector.hpp"
namespace xo { namespace mm { class IGCObjectVisitor_DX1Collector; } }
namespace xo {
namespace facet {
template <>
struct FacetImplementation<xo::mm::AGCObjectVisitor,
xo::mm::DX1Collector>
{
using ImplType = xo::mm::IGCObjectVisitor_Xfer
<xo::mm::DX1Collector,
xo::mm::IGCObjectVisitor_DX1Collector>;
};
}
}
namespace xo {
namespace mm {
/** @class IGCObjectVisitor_DX1Collector
**/
class IGCObjectVisitor_DX1Collector {
public:
/** @defgroup mm-gcobjectvisitor-dx1collector-type-traits **/
///@{
using Copaque = xo::mm::AGCObjectVisitor::Copaque;
using Opaque = xo::mm::AGCObjectVisitor::Opaque;
///@}
/** @defgroup mm-gcobjectvisitor-dx1collector-methods **/
///@{
// const methods
// non-const methods
/** visit child of a gc-aware object. May update child in-place! **/
static void visit_child(DX1Collector & self, AGCObject * iface, void ** pp_data) noexcept;
///@}
};
} /*namespace mm*/
} /*namespace xo*/
/* end */

View file

@ -12,6 +12,7 @@ set(SELF_SRCS
X1CollectorConfig.cpp
DX1Collector.cpp
facet/ICollector_DX1Collector.cpp
facet/IGCObjectVisitor_DX1Collector.cpp
DX1CollectorIterator.cpp

View file

@ -587,6 +587,10 @@ namespace xo {
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()) {
@ -601,6 +605,22 @@ namespace xo {
}
}
void
DX1Collector::visit_child(AGCObject * lhs_iface,
void ** lhs_data)
{
if (runstate_.is_running()) {
// called during collection phase
this->forward_inplace(lhs_iface, lhs_data);
} else if (runstate_.is_verify()) {
// called during verify_ok
this->_verify_aux(lhs_iface, *lhs_data);
} else {
// should be unreachable
assert(false);
}
}
void
DX1Collector::_verify_aux(AGCObject * iface, void * data)
{

View file

@ -0,0 +1,27 @@
/** @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::visit_child(DX1Collector & self, AGCObject * iface, void ** pp_data) noexcept -> void
{
self.visit_child(iface, pp_data);
}
} /*namespace mm*/
} /*namespace xo*/
/* end IGCObjectVisitor_DX1Collector.cpp */