refactor focusing on xo-alloc2/ xo-gc/ write-barrier

ability to inform allocator of gco->gco mutation, via AAllocator i/face.
This commit is contained in:
Roland Conybeare 2026-05-01 19:54:26 -04:00
commit b780d1da4c
17 changed files with 149 additions and 18 deletions

View file

@ -10,4 +10,6 @@
#include "alloc/IAllocator_Xfer.hpp" #include "alloc/IAllocator_Xfer.hpp"
#include "alloc/RAllocator.hpp" #include "alloc/RAllocator.hpp"
#include "alloc/RAllocator_aux.hpp"
/* end Allocator.hpp */ /* end Allocator.hpp */

View file

@ -0,0 +1,15 @@
/** @file Allocator_basic.hpp
*
* @author Roland Conybeare, Dec 2025
**/
#pragma once
#include "alloc/AAllocator.hpp"
#include "alloc/IAllocator_Any.hpp"
#include "alloc/IAllocator_Xfer.hpp"
#include "alloc/RAllocator.hpp"
//#include "alloc/RAllocator_aux.hpp"
/* end Allocator_basic.hpp */

View file

@ -20,8 +20,8 @@ namespace xo {
using Copaque = const void *; using Copaque = const void *;
using Opaque = void *; using Opaque = void *;
// see DArena.hpp class AGCObject; // see AGCObject.hpp
struct DArena; struct DArena; // see DArena.hpp
/** @class AAllocator /** @class AAllocator
* @brief Abstract facet for allocation * @brief Abstract facet for allocation
@ -34,6 +34,7 @@ namespace xo {
public: public:
/** @defgroup mm-allocator-type-traits allocator type traits **/ /** @defgroup mm-allocator-type-traits allocator type traits **/
///@{ ///@{
using AGCObject = xo::mm::AGCObject;
/** memory size report **/ /** memory size report **/
using MemorySizeInfo = xo::mm::MemorySizeInfo; using MemorySizeInfo = xo::mm::MemorySizeInfo;
/** type used for allocation amounts **/ /** type used for allocation amounts **/
@ -155,6 +156,28 @@ namespace xo {
virtual value_type alloc_copy(Opaque d, value_type src) const = 0; virtual value_type alloc_copy(Opaque d, value_type src) const = 0;
/** reset allocator @p d to empty state. **/ /** reset allocator @p d to empty state. **/
virtual void clear(Opaque d) const = 0; virtual void clear(Opaque d) const = 0;
/** assign helper for allocator that may require a write barrier
* (for example Collector). Using obj<AGCObject> here causes
* include cycle, use spelled out form instead;
*
* For:
* obj<AGCObject> lhs_ = rhs
* with allocator d, that owns some allocaiton p, would use
* mm.barrier_assign_aux(d, p,
* lhs_.iface(), lhs_.opaque_data_addr(),
* rhs.iface(), rhs.opaque_data());
* when lhs has known data type:
* DRepr * lhs_ = rhs.data();
* use
* mm.barrier_assign_aux(d, p,
* nullptr, lhs_.opaque_data_addr(),
* rhs.iface(), rhs.opaque_data());
* barrier needs to be able to construct complete fop for rhs
* to administrate write barrier.
**/
virtual void barrier_assign_aux(Opaque d, void * parent,
AGCObject * lhs_iface, void ** lhs_data,
AGCObject * rhs_iface, void * rhs_data) const = 0;
///@} ///@}
}; /*AAllocator*/ }; /*AAllocator*/

View file

@ -60,6 +60,10 @@ namespace xo {
[[noreturn]] value_type sub_alloc(Opaque, std::size_t, bool) const override { _fatal(); } [[noreturn]] value_type sub_alloc(Opaque, std::size_t, bool) const override { _fatal(); }
[[noreturn]] value_type alloc_copy(Opaque, value_type) const override { _fatal(); } [[noreturn]] value_type alloc_copy(Opaque, value_type) const override { _fatal(); }
[[noreturn]] void clear(Opaque) const override { _fatal(); } [[noreturn]] void clear(Opaque) const override { _fatal(); }
[[noreturn]] void barrier_assign_aux(Opaque,
void *,
AGCObject *, void **,
AGCObject *, void *) const override { _fatal(); }
private: private:
[[noreturn]] static void _fatal(); [[noreturn]] static void _fatal();

View file

@ -78,6 +78,10 @@ namespace xo {
value_type alloc_copy(Opaque d, value_type alloc_copy(Opaque d,
value_type src) const override { return I::alloc_copy(_dcast(d), src); } value_type src) const override { return I::alloc_copy(_dcast(d), src); }
void clear(Opaque d) const override { return I::clear(_dcast(d)); } void clear(Opaque d) const override { return I::clear(_dcast(d)); }
void barrier_assign_aux(Opaque d,
void * parent,
AGCObject * lhs_iface, void ** lhs_data,
AGCObject * rhs_iface, void * rhs_data) const override { I::barrier_assign_aux(_dcast(d), parent, lhs_iface, lhs_data, rhs_iface, rhs_data); }
///@} ///@}
private: private:

View file

@ -5,7 +5,7 @@
#pragma once #pragma once
#include "AAllocator.hpp" #include "Allocator_basic.hpp" // omits RAllocator_aux
#include "AllocIterator.hpp" #include "AllocIterator.hpp"
#include <xo/facet/RRouter.hpp> #include <xo/facet/RRouter.hpp>
#include <string> #include <string>
@ -66,13 +66,30 @@ namespace xo {
AllocInfo alloc_info(value_type mem) const noexcept { return O::iface()->alloc_info(O::data(), mem); } AllocInfo alloc_info(value_type mem) const noexcept { return O::iface()->alloc_info(O::data(), mem); }
range_type alloc_range(DArena & mm) const noexcept { return O::iface()->alloc_range(O::data(), mm); } range_type alloc_range(DArena & mm) const noexcept { return O::iface()->alloc_range(O::data(), mm); }
bool expand(size_type z) { return O::iface()->expand(O::data(), z); }
value_type alloc(typeseq t, size_type z) noexcept { return O::iface()->alloc(O::data(), t, z); } value_type alloc(typeseq t, size_type z) noexcept { return O::iface()->alloc(O::data(), t, z); }
value_type super_alloc(typeseq t, size_type z) noexcept { return O::iface()->super_alloc(O::data(), t, z); } value_type super_alloc(typeseq t, size_type z) noexcept { return O::iface()->super_alloc(O::data(), t, z); }
value_type sub_alloc(size_type z, value_type sub_alloc(size_type z,
bool complete_flag) noexcept { return O::iface()->sub_alloc(O::data(), bool complete_flag) noexcept { return O::iface()->sub_alloc(O::data(),
z, complete_flag); } z, complete_flag); }
value_type alloc_copy(value_type src) noexcept { return O::iface()->alloc_copy(O::data(), src); } value_type alloc_copy(value_type src) noexcept { return O::iface()->alloc_copy(O::data(), src); }
bool expand(size_type z) { return O::iface()->expand(O::data(), z); } void clear() { O::iface()->clear(O::data()); }
void barrier_assign_aux(void * parent,
AGCObject * lhs_iface, void ** lhs_data,
AGCObject * rhs_iface, void * rhs_data) noexcept { O::iface()->barrier_assign_aux(O::data(), parent,
lhs_iface, lhs_data,
rhs_iface, rhs_data); }
void barrier_assign(void * parent,
obj<AGCObject> * p_lhs,
obj<AGCObject> rhs) noexcept;
#ifdef NOT_YET
this->barrier_assign_aux(parent,
p_lhs->iface(),
p_lhs->opaque_data_addr(),
rhs.iface(),
rhs.opaque_data());
#endif
static bool _valid; static bool _valid;
}; };

View file

@ -0,0 +1,34 @@
/** @file RAllocator_aux.hpp
*
* Out-of-line definitions for RAllocator template methods
* that depend on RGCObject (avoiding #include cycle in RAllocator.hpp)
*
* Would aspire to Include via user_hpp_includes in Allocator.json5,
* if/when that exists
*
* @author Roland Conybeare
**/
#pragma once
#include "RAllocator.hpp"
#include "GCObject.hpp"
namespace xo {
namespace mm {
template <typename Object>
void
RAllocator<Object>::barrier_assign(void * parent,
obj<AGCObject> * p_lhs,
obj<AGCObject> rhs) noexcept
{
(void)parent;
(void)p_lhs;
(void)rhs;
}
} /*namespace mm*/
} /*namespace xo*/
/* end RAllocator_aux.hpp */

View file

@ -75,6 +75,11 @@ namespace xo {
/** allocate copy of @p src in arena @p d. **/ /** allocate copy of @p src in arena @p d. **/
static value_type alloc_copy(DArena & d, value_type src); static value_type alloc_copy(DArena & d, value_type src);
static void clear(DArena &); static void clear(DArena &);
/** perform assignment {*lhs_iface, *lhs_data} = {*rhs_iface, rhs_data} **/
static void barrier_assign_aux(DArena &,
void * parent,
AGCObject * lhs_iface, void ** lhs_data,
AGCObject * rhs_iface, void * rhs_data);
static void destruct_data(DArena &); static void destruct_data(DArena &);
}; };

View file

@ -14,7 +14,7 @@
#pragma once #pragma once
// includes (via {facet_includes}) // includes (via {facet_includes})
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator_basic.hpp>
#include <xo/alloc2/Generation.hpp> #include <xo/alloc2/Generation.hpp>
#include <xo/alloc2/role.hpp> #include <xo/alloc2/role.hpp>
#include <xo/facet/obj.hpp> #include <xo/facet/obj.hpp>

View file

@ -14,7 +14,7 @@
#pragma once #pragma once
// includes (via {facet_includes}) // includes (via {facet_includes})
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator_basic.hpp>
#include <xo/alloc2/Collector.hpp> #include <xo/alloc2/Collector.hpp>
#include <xo/alloc2/GCObjectVisitor.hpp> #include <xo/alloc2/GCObjectVisitor.hpp>
#include <cstdint> #include <cstdint>

View file

@ -13,7 +13,7 @@
#pragma once #pragma once
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator_basic.hpp>
#include <xo/alloc2/Generation.hpp> #include <xo/alloc2/Generation.hpp>
#include <xo/alloc2/role.hpp> #include <xo/alloc2/role.hpp>

View file

@ -13,7 +13,7 @@
#pragma once #pragma once
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator_basic.hpp>
#include <xo/alloc2/Collector.hpp> #include <xo/alloc2/Collector.hpp>
#include <xo/alloc2/GCObjectVisitor.hpp> #include <xo/alloc2/GCObjectVisitor.hpp>
#include <cstdint> #include <cstdint>

View file

@ -52,7 +52,7 @@ public:
void * alloc_copy_for(const T * src) noexcept { void * alloc_copy_for(const T * src) noexcept {
return O::iface()->alloc_copy(O::data(), (std::byte *)const_cast<T *>(src)); return O::iface()->alloc_copy(O::data(), (std::byte *)const_cast<T *>(src));
} }
/** convenience template for move-constructible T (this is common) **/ /** convenience template for move-constructible T (this is common) **/
template <typename T> template <typename T>
T * std_move_for(T * src) noexcept { T * std_move_for(T * src) noexcept {
@ -62,28 +62,28 @@ public:
} }
return nullptr; return nullptr;
} }
/** forward faceted object pointer in place. Defined in GCObject.hpp to avoid #include cycle **/ /** forward faceted object pointer in place. Defined in GCObject.hpp to avoid #include cycle **/
template <typename DRepr> template <typename DRepr>
void forward_inplace(obj<AGCObject,DRepr> * p_obj); void forward_inplace(obj<AGCObject,DRepr> * p_obj);
/** another convenience template for forwarding. /** another convenience template for forwarding.
* Defined in RGCObject.hpp to avoid #include cycle. * Defined in RGCObject.hpp to avoid #include cycle.
**/ **/
template <typename DRepr> template <typename DRepr>
void forward_inplace(DRepr ** pp_repr); void forward_inplace(DRepr ** pp_repr);
/** convenience template where pointer requires pivot **/ /** convenience template where pointer requires pivot **/
template <typename AFacet, typename DRepr> template <typename AFacet, typename DRepr>
requires (!std::is_same_v<AFacet, AGCObject>) requires (!std::is_same_v<AFacet, AGCObject>)
void forward_pivot_inplace(obj<AFacet,DRepr> * p_obj); void forward_pivot_inplace(obj<AFacet,DRepr> * p_obj);
/** add root @p p_root **/ /** add root @p p_root **/
template<typename DRepr> template<typename DRepr>
void add_gc_root(obj<AGCObject, DRepr> * p_root) { void add_gc_root(obj<AGCObject, DRepr> * p_root) {
O::iface()->add_gc_root_poly(O::data(), (obj<AGCObject> *)p_root); O::iface()->add_gc_root_poly(O::data(), (obj<AGCObject> *)p_root);
} }
/** remove root @p p_root **/ /** remove root @p p_root **/
template <typename DRepr> template <typename DRepr>
void remove_gc_root(obj<AGCObject, DRepr> * p_root) { void remove_gc_root(obj<AGCObject, DRepr> * p_root) {

View file

@ -14,7 +14,7 @@
#pragma once #pragma once
// includes (via {facet_includes}) // includes (via {facet_includes})
#include "Allocator.hpp" #include "Allocator_basic.hpp"
#include <xo/facet/obj.hpp> #include <xo/facet/obj.hpp>
#include <xo/facet/facet_implementation.hpp> #include <xo/facet/facet_implementation.hpp>
#include <xo/facet/typeseq.hpp> #include <xo/facet/typeseq.hpp>

View file

@ -4,6 +4,7 @@
**/ **/
#include "AllocIterator.hpp" #include "AllocIterator.hpp"
#include "GCObject.hpp"
#include "arena/IAllocator_DArena.hpp" #include "arena/IAllocator_DArena.hpp"
#include "arena/IAllocIterator_DArenaIterator.hpp" // for alloc_range #include "arena/IAllocIterator_DArenaIterator.hpp" // for alloc_range
#include <xo/arena/DArenaIterator.hpp> #include <xo/arena/DArenaIterator.hpp>
@ -154,6 +155,31 @@ namespace xo {
//s.checkpoint_ = s.lo_; //s.checkpoint_ = s.lo_;
} }
void
IAllocator_DArena::barrier_assign_aux(DArena & s,
void * parent,
AGCObject * lhs_iface, void ** lhs_data,
AGCObject * rhs_iface, void * rhs_data)
{
(void)s;
(void)parent;
// usually would expect this to just forward to DArena.
// That's problematic in this case, because DArena is at lower level
// relative to obj<AAllocator,DArena>;
// recall that DArena is used in the implementation of xo-facet/
//
// In any case, for DArena no write barrier is applied.
// Instead just perform the fop assignment
// replacing vtable pointer here
if (lhs_iface) {
::memcpy((void *)lhs_iface, (void *)rhs_iface, sizeof(AGCObject));
}
*lhs_data = rhs_data;
}
void void
IAllocator_DArena::destruct_data(DArena & s) IAllocator_DArena::destruct_data(DArena & s)
{ {

View file

@ -5,7 +5,8 @@
#include "Allocator.hpp" #include "Allocator.hpp"
#include "AllocIterator.hpp" #include "AllocIterator.hpp"
#include "arena/IAllocator_DArena.hpp" #include "Arena.hpp"
//#include "arena/IAllocator_DArena.hpp"
#include "arena/IAllocIterator_DArenaIterator.hpp" #include "arena/IAllocIterator_DArenaIterator.hpp"
#include "padding.hpp" #include "padding.hpp"
#include <xo/indentlog/scope.hpp> #include <xo/indentlog/scope.hpp>

View file

@ -4,8 +4,8 @@
**/ **/
#include "xo/alloc2/Allocator.hpp" #include "xo/alloc2/Allocator.hpp"
#include "xo/alloc2/alloc/IAllocator_Xfer.hpp" #include "xo/alloc2/Arena.hpp"
#include "xo/alloc2/arena/IAllocator_DArena.hpp" //#include "xo/alloc2/arena/IAllocator_DArena.hpp"
#include "xo/arena/print.hpp" #include "xo/arena/print.hpp"
#include "xo/arena/padding.hpp" #include "xo/arena/padding.hpp"
#include <xo/facet/obj.hpp> #include <xo/facet/obj.hpp>