xo-ordinaltree: add _gc_assign_member feature to gc-aware allocs

This commit is contained in:
Roland Conybeare 2025-12-04 17:33:40 -05:00
commit 37d08b8c67
2 changed files with 68 additions and 3 deletions

View file

@ -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 <typename T, typename Allocator>
void _gc_assign_member(T ** lhs,
T * rhs,
Allocator & alloc)
{
static_assert(std::is_convertible_v<decltype(*lhs), IObject*>);
alloc.mm_->assign_member(this, reinterpret_cast<IObject **>(lhs), rhs);
}
/** true iff this object represents a forwarding pointer.
* Forwarding pointers are exclusively created by the garbage collector;

View file

@ -7,6 +7,7 @@
#include <type_traits>
#include <memory>
#include <cassert>
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<Allocator>
* 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<xo::gc::GC>)
* 3. allows a gc-aware template class T to fallback
* to ordinary allocator-aware behavior for non-gc
* allocators, such as std::allocator<T>,
* 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 <typename A>
struct has_incremental_gc_interface<A, std::void_t<typename A::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