xo-alloc / xo-ordinaltree: work on dual-alloc-policy trees

This commit is contained in:
Roland Conybeare 2025-12-02 10:37:07 -05:00
commit c7816416c5
7 changed files with 65 additions and 19 deletions

View file

@ -318,7 +318,8 @@ namespace xo {
* @param rhs. new target for @p *lhs * @param rhs. new target for @p *lhs
**/ **/
virtual void assign_member(IObject * parent, IObject ** lhs, IObject* rhs) final override; virtual void assign_member(IObject * parent, IObject ** lhs, IObject* rhs) final override;
/** evacuate @p *lhs and replace with forwarding pointer **/
virtual void forward_inplace(IObject ** lhs) final override;
/** during GC check for source objects owned by GC. /** during GC check for source objects owned by GC.
* See Object::_shallow_move. * See Object::_shallow_move.
**/ **/

View file

@ -568,6 +568,12 @@ namespace xo {
} }
} }
void
GC::forward_inplace(IObject ** lhs)
{
Object::_forward_inplace(lhs, this);
}
bool bool
GC::check_owned(IObject * src) const GC::check_owned(IObject * src) const
{ {

View file

@ -24,7 +24,8 @@ namespace xo {
Object::mm = nullptr; Object::mm = nullptr;
IObject * IObject *
Object::_forward(IObject * src, gc::IAlloc * gc) Object::_forward(IObject * src,
gc::IAlloc * gc)
{ {
if (!src) if (!src)
return src; return src;

View file

@ -76,7 +76,7 @@ namespace xo {
std::byte * allocate(std::size_t n) { std::byte * allocate(std::size_t n) {
return this->alloc(n); return this->alloc(n);
} }
/** std::allocator locality hint. Not used for now **/ /** std::allocator locality hint. Not used for now **/
std::byte * allocate(std::size_t n, const void * hint) { std::byte * allocate(std::size_t n, const void * hint) {
(void)hint; (void)hint;
@ -147,6 +147,8 @@ namespace xo {
virtual void assign_member(IObject * /*parent*/, virtual void assign_member(IObject * /*parent*/,
IObject ** lhs, IObject ** lhs,
IObject * rhs) { *lhs = rhs; } IObject * rhs) { *lhs = rhs; }
/** if GC: evacuate @p *lhs and replace with forwarding pointer **/
virtual void forward_inplace(IObject ** lhs) { (void)lhs; }
/** allocate @p z bytes for copy of object at @p src. /** allocate @p z bytes for copy of object at @p src.
* Only used in @ref GC. Default implementation asserts and returns nullptr * Only used in @ref GC. Default implementation asserts and returns nullptr
**/ **/
@ -170,7 +172,7 @@ namespace xo {
using const_reference = const T &; using const_reference = const T &;
using size_type = IAlloc::size_type; using size_type = IAlloc::size_type;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using gc_object_interface = IAlloc::gc_object_interface; using gc_object_interface = IAlloc::gc_object_interface;
using has_incremental_gc_interface = IAlloc::has_incremental_gc_interface; using has_incremental_gc_interface = IAlloc::has_incremental_gc_interface;

View file

@ -19,6 +19,9 @@ namespace xo {
**/ **/
class IObject { class IObject {
public: public:
/** impl inheriting this class must provide gc hooks **/
static constexpr bool _requires_gc_hooks = true;
/** true iff this object represents a forwarding pointer. /** true iff this object represents a forwarding pointer.
* Forwarding pointers are exclusively created by the garbage collector; * Forwarding pointers are exclusively created by the garbage collector;
* forwarding pointers (and only forwarding pointers) return true here. * forwarding pointers (and only forwarding pointers) return true here.

View file

@ -32,7 +32,7 @@ namespace xo {
* provide garbage collection * provide garbage collection
* - xo::gc::GC has a collection API and also provides * - xo::gc::GC has a collection API and also provides
* garbage collection * garbage collection
* *
* GC-allocated objects must: * GC-allocated objects must:
* 2a. inherit A::object_interface * 2a. inherit A::object_interface
* 2b. implement A::object_interface::_shallow_size() * 2b. implement A::object_interface::_shallow_size()
@ -40,7 +40,7 @@ namespace xo {
* 2d. implement A::object_interface::_forward_children(alloc) * 2d. implement A::object_interface::_forward_children(alloc)
* in multiple inheritance scenarios * in multiple inheritance scenarios
* 2e. implement A::object_interface::_offset_destination(src) * 2e. implement A::object_interface::_offset_destination(src)
* *
* Design Notes: * Design Notes:
* - virtual-method choice requires vtable pointer per object; * - virtual-method choice requires vtable pointer per object;
* but zero *marginal* space cost for types that would have * but zero *marginal* space cost for types that would have
@ -82,12 +82,16 @@ namespace xo {
// gc_allocator_traits<Allocator>::template object_interface<Allocator> // gc_allocator_traits<Allocator>::template object_interface<Allocator>
// //
template <typename A, typename = void> template <typename A, typename = void>
struct object_interface {}; struct object_interface {
/** see also IObject::_requires_gc_hooks **/
static constexpr bool _requires_gc_hooks = false;
};
// specialization when A provides gc_object_interface // specialization when A provides gc_object_interface
template <typename A> template <typename A>
struct object_interface<A, std::void_t<typename A::gc_object_interface>> struct object_interface<A, std::void_t<typename A::gc_object_interface>>
: A::gc_object_interface {}; : public A::gc_object_interface {
};
/** true iff this allocator advertises itself as an incremental collector /** true iff this allocator advertises itself as an incremental collector
* allocator will include: * allocator will include:
@ -97,7 +101,6 @@ namespace xo {
* }; * };
**/ **/
static inline constexpr bool has_incremental_gc_interface_v = has_incremental_gc_interface<Allocator>::value; static inline constexpr bool has_incremental_gc_interface_v = has_incremental_gc_interface<Allocator>::value;
}; };
} /*namespace gc*/ } /*namespace gc*/
} /*namespace xo*/ } /*namespace xo*/

View file

@ -8,6 +8,7 @@
#include "RbTypes.hpp" #include "RbTypes.hpp"
#include "xo/reflect/Reflect.hpp" #include "xo/reflect/Reflect.hpp"
#include "xo/indentlog/scope.hpp" #include "xo/indentlog/scope.hpp"
#include "xo/allocutil/IAlloc.hpp"
#include "xo/allocutil/IObject.hpp" #include "xo/allocutil/IObject.hpp"
#include "xo/allocutil/gc_allocator_traits.hpp" #include "xo/allocutil/gc_allocator_traits.hpp"
#include <cassert> #include <cassert>
@ -301,7 +302,7 @@ namespace xo {
xtag("r2", this->reduced2())); xtag("r2", this->reduced2()));
} /*local_recalc_size*/ } /*local_recalc_size*/
private: private:
void assign_color(Color x) { this->color_ = x; } void assign_color(Color x) { this->color_ = x; }
void assign_size(size_t z) { this->size_ = z; } void assign_size(size_t z) { this->size_ = z; }
@ -344,20 +345,49 @@ namespace xo {
} /*replace_child_reparent*/ } /*replace_child_reparent*/
// ----- (possibly) inherited from GcObjectInterface ----- // ----- (possibly) inherited from GcObjectInterface -----
virtual TaggedPtr self_tp() const { return Reflect::make_tp(const_cast<Node*>(this)); }
virtual void display(std::ostream & os) const { os << "<Node>"; }
virtual std::size_t _shallow_size() const { return sizeof(*this); }
/* note: only relevant when GcObjectInterface is xo::IObject */
template <typename T = GcObjectInterface> virtual TaggedPtr self_tp() const {
virtual IObject * _shallow_copy(gc::IAlloc * gc) const return Reflect::make_tp(const_cast<Node *>(this));
-> std::enable_if_t<std::is_base_of_v<xo::IObject: }
{ virtual void display(std::ostream & os) const {
/* assert appropriate here. code will be present but dead when assert fails */ os << "<Node>";
xo::Cpof cpof(gc, this);
return new (cpof) Node(*this);
} }
virtual std::size_t _shallow_size() const { return sizeof(*this); }
/* note: only relevant when GcObjectInterface is xo::IObject */
virtual IObject * _shallow_copy(gc::IAlloc * gc) const {
if constexpr (GcObjectInterface::_requires_gc_hooks) {
xo::Cpof cpof(gc, this);
return new (cpof) Node(*this);
} else {
assert(false && "_shallow_copy assumes gc enabled");
return nullptr;
}
}
virtual std::size_t _forward_children(gc::IAlloc * gc) {
if constexpr (GcObjectInterface::_requires_gc_hooks) {
static_assert(std::is_convertible_v<decltype(parent_), IObject *>,
"parent_ must be convertible to IObject*");
static_assert(std::is_convertible_v<decltype(child_v_[0]), IObject *>,
"child_v_[0] must be convertible to IObject*");
gc->forward_inplace(reinterpret_cast<IObject **>(&parent_));
gc->forward_inplace(reinterpret_cast<IObject **>(&child_v_[0]));
gc->forward_inplace(reinterpret_cast<IObject **>(&child_v_[1]));
// how do we tell if any of
// {value_type, ReducedValue}
// also contain pointers that need forwarding?
return Node::_shallow_size();
} else {
assert(false && "_forward_children assumes gc enabled");
return 0ul;
}
}
private:
friend class RbTreeUtil<Key, Value, Reduce, GcObjectInterface>; friend class RbTreeUtil<Key, Value, Reduce, GcObjectInterface>;
template <typename Key1, typename Value1, typename Reduce1, typename Allocator> template <typename Key1, typename Value1, typename Reduce1, typename Allocator>
friend class xo::tree::RedBlackTree; friend class xo::tree::RedBlackTree;