From 37d08b8c67cc3218653305b64bbbcd3eeb63ae24 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 4 Dec 2025 17:33:40 -0500 Subject: [PATCH] xo-ordinaltree: add _gc_assign_member feature to gc-aware allocs --- include/xo/allocutil/IObject.hpp | 18 +++++++ include/xo/allocutil/gc_allocator_traits.hpp | 53 ++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/include/xo/allocutil/IObject.hpp b/include/xo/allocutil/IObject.hpp index c6f9611..086d413 100644 --- a/include/xo/allocutil/IObject.hpp +++ b/include/xo/allocutil/IObject.hpp @@ -21,6 +21,24 @@ namespace xo { public: /** impl inheriting this class must provide gc hooks **/ static constexpr bool _requires_gc_hooks = true; + /** impl inheriting this class must use write barriers + * (so that GC allocator can remember cross-generational pointers) + **/ + static constexpr bool _requires_write_barrier = true; + + /** GC write barrier: + * assign value @p rhs to member @p *lhs of @p parent. + * Identifiy and remember cross-generational pointers. + **/ + template + void _gc_assign_member(T ** lhs, + T * rhs, + Allocator & alloc) + { + static_assert(std::is_convertible_v); + + alloc.mm_->assign_member(this, reinterpret_cast(lhs), rhs); + } /** true iff this object represents a forwarding pointer. * Forwarding pointers are exclusively created by the garbage collector; diff --git a/include/xo/allocutil/gc_allocator_traits.hpp b/include/xo/allocutil/gc_allocator_traits.hpp index 80184b0..6ddda37 100644 --- a/include/xo/allocutil/gc_allocator_traits.hpp +++ b/include/xo/allocutil/gc_allocator_traits.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace xo { namespace gc { @@ -15,7 +16,18 @@ namespace xo { * Introduces additional i/face methods * for garbage-collector-enabled allocators * - * allocator A can identify itself as a copying collector: + * Use Cases: + * 1. drop-in replacement for std::allocator_traits + * with non-gc-aware allocators. + * 2. allows a gc-aware template class to activate + * gc support when used with a collecting allocator + * (i.e. xo::gc::allocator) + * 3. allows a gc-aware template class T to fallback + * to ordinary allocator-aware behavior for non-gc + * allocators, such as std::allocator, + * but also pool allocators etc. + * + * An allocator A can identify itself as a copying collector: * * 1. provide A::object_interface * per-object header interface: tells garbage collector @@ -33,7 +45,24 @@ namespace xo { * - xo::gc::GC has a collection API and also provides * garbage collection * - * GC-allocated objects must: + * GC object model + * 2a. A GC-allocated object is an object that GC manages + * atomically. All memory associated with a GC-allocated + * object has the same lifetime. + * 2b. A GC-allocation is 1:1 with a GC-allocated object + * 2c. A GC-allocated object may have internal pointers. + * These are pointer interior to the same original + * allocation. It's the responsibility of the object to update these + * (if/when GC moves said object) via GC hooks. + * 2d. A GC-allocated object may have external pointers + * to other GC-allocated objects. Managing these is split + * between GC and object itself. GC takes responsibility + * for moving the destination objects. + * Object is responsible for telling GC about such pointers + * and changes to their values + * (e.g. IObject::_forward_children()) + * + * GC object implementation: gc objects must: * 2a. inherit A::object_interface * 2b. implement A::object_interface::_shallow_size() * 2c. implement A::object_interface::_shallow_copy(alloc) @@ -41,6 +70,22 @@ namespace xo { * in multiple inheritance scenarios * 2e. implement A::object_interface::_offset_destination(src) * + * 3. write barrier support: + * A generational GC needs to track changes that create or modify + * inter-generational pointers. + * + * GC-aware classes could write: + * MyClass::update_pointer_state(IObject *new_value, gc::IAlloc *gc) { + * if constexpr (GcObjectInterface::_requires_write_barrier) { + * gc->assign_member(this, &some_member_, new_value); + * } else { + * this->some_member_ = new_value; + * } + * } + * + * but simpler: + * GcObjectInterface::_gc_assign_member(this, &some_member_, new_value, alloc_); + * * Design Notes: * - virtual-method choice requires vtable pointer per object; * but zero *marginal* space cost for types that would have @@ -68,7 +113,7 @@ namespace xo { // opt-in: A provides nested type 'has_incremental_collector_interface': // struct A { - // using is_incremental_collector = std::true_type; + // using has_incremental_collector = std::true_type; // }; template struct has_incremental_gc_interface> : @@ -110,6 +155,8 @@ namespace xo { *lhs = rhs; } + virtual bool _is_forwarded() const { return false; } + virtual std::size_t _shallow_size() const { assert(false); return 0; } }; // specialization when A provides gc_object_interface