diff --git a/include/xo/alloc2/Collector.hpp b/include/xo/alloc2/Collector.hpp new file mode 100644 index 0000000..45c7fdb --- /dev/null +++ b/include/xo/alloc2/Collector.hpp @@ -0,0 +1,13 @@ +/** @file Collector.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "gc/ACollector.hpp" +#include "gc/ICollector_Any.hpp" +#include "gc/ICollector_Xfer.hpp" +#include "gc/RCollector.hpp" + +/* end Collector.hpp */ diff --git a/include/xo/alloc2/GCObject.hpp b/include/xo/alloc2/GCObject.hpp new file mode 100644 index 0000000..b7758ac --- /dev/null +++ b/include/xo/alloc2/GCObject.hpp @@ -0,0 +1,47 @@ +/** @file GCObject.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/GCObject.json5] + * 2. jinja2 template for facet .hpp file: + * [facet.hpp.j2] + * 3. idl for facet methods + * [idl/GCObject.json5] + **/ + +#pragma once + +#include "gc/AGCObject.hpp" +#include "gc/IGCObject_Any.hpp" +#include "gc/IGCObject_Xfer.hpp" +#include "gc/RGCObject.hpp" + +namespace xo { + namespace mm { + /** defined here to avoid #include cycle, since + * template obj awkward to make available there + **/ + template + template + void + RCollector::forward_inplace(xo::facet::obj * p_obj) + { + this->forward_inplace(p_obj->iface(), (void **)&(p_obj->data_)); + } + + template + template + void + RCollector::forward_inplace(DRepr ** p_repr) + { + // fetch static interface for DRepr + auto iface = xo::facet::impl_for(); + + this->forward_inplace(&iface, (void **)p_repr); + } + } +} + +/* end GCObject.hpp */ diff --git a/include/xo/alloc2/alloc/AAllocator.hpp b/include/xo/alloc2/alloc/AAllocator.hpp index ecc5286..74c1c6b 100644 --- a/include/xo/alloc2/alloc/AAllocator.hpp +++ b/include/xo/alloc2/alloc/AAllocator.hpp @@ -31,7 +31,8 @@ namespace xo { * Implementations of AAllocator will downcast to a * to some specific representation. **/ - struct AAllocator { + class AAllocator { + public: /** @defgroup mm-allocator-type-traits allocator type traits **/ ///@{ /** memory size report **/ diff --git a/include/xo/alloc2/gc/ACollector.hpp b/include/xo/alloc2/gc/ACollector.hpp new file mode 100644 index 0000000..1dfca96 --- /dev/null +++ b/include/xo/alloc2/gc/ACollector.hpp @@ -0,0 +1,83 @@ +/** @file ACollector.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +//#include "IGCObject_Any.hpp" + +#include +#include +#include + +#include +#include + +#include +#include + +namespace xo { + namespace mm { + using Copaque = const void *; + using Opaque = void *; + + class AGCObject; + class IGCObject_Any; // see IGCObject_Any.hpp + + /** @class ACollector + * @brief Abstract facet for the XO garbage collector + * + * A collector implementation will also support the @ref AAllocator facet, see also + **/ + class ACollector { + public: + using typeseq = xo::facet::typeseq; + using size_type = std::size_t; + + virtual typeseq _typeseq() const noexcept = 0; + + virtual size_type allocated(Copaque d, + generation g, role r) const noexcept = 0; + virtual size_type reserved(Copaque d, + generation g, role r) const noexcept = 0; + virtual size_type committed(Copaque d, + generation g, role r) const noexcept = 0; + virtual bool is_type_installed(Copaque d, + typeseq tseq) const noexcept = 0; + + /** install interface @p iface for representation with typeseq @p tseq + * in collector @p d. + * + * The type AGCObject_Any here is misleading. + * Will have been replaced by an instance of + * @c AGCObject_Xfer for some @c DFoo + * in which case calls through @c std::launder(&iface) + * will properly act on @c DFoo. + * + * Return false if installation fails (e.g. memory exhausted) + **/ + virtual bool install_type(Opaque d, const AGCObject & iface) = 0; + virtual void add_gc_root_poly(Opaque d, obj * p_root) = 0; + //virtual void add_gc_root_typed(Opaque d, typeseq tseq, Opaque * root) = 0; + + /** Request immediate collection. + * 1. if collection is enabled, immediately collect all generations + * up to (but not including) g + * 2. may nevertheless escalate to older generations, + * depending on collector state. + * 3. if collection is currently disabled, + * collection will trigger the next time gc is enabled. + **/ + virtual void request_gc(Opaque d, generation upto) = 0; + + /** evacuate @p *lhs, that refers to state with interface @p lhs_iface, + * to collector @p d's to-space. Replace *lhs_data with forwarding pointer + * + * Require: gc in progress + **/ + virtual void forward_inplace(Opaque d, AGCObject * lhs_iface, void ** lhs_data) = 0; + }; + } + +} /*namespace xo*/ diff --git a/include/xo/alloc2/gc/AGCObject.hpp b/include/xo/alloc2/gc/AGCObject.hpp new file mode 100644 index 0000000..87fc666 --- /dev/null +++ b/include/xo/alloc2/gc/AGCObject.hpp @@ -0,0 +1,87 @@ +/** @file AGCObject.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/GCObject.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [abstract_facet.hpp.j2] + * 3. idl for facet methods + * [idl/GCObject.json5] + **/ + +#pragma once + +// includes (via {facet_includes}) +#include +#include +#include +#include +#include +#include +#include + +namespace xo { namespace mm { class ACollector; }} + +namespace xo { +namespace mm { + +using Copaque = const void *; +using Opaque = void *; + +/** + GC hooks for collector-aware data +**/ +class AGCObject { +public: + /** @defgroup mm-gcobject-type-traits **/ + ///@{ + // types + /** integer identifying a type **/ + using typeseq = xo::facet::typeseq; + using Copaque = const void *; + using Opaque = void *; + /** type for an amount of memory **/ + using size_type = std::size_t; + /** fomo allocator type **/ + using AAllocator = xo::mm::AAllocator; + /** fomo collector type **/ + using ACollector = xo::mm::ACollector; + ///@} + + /** @defgroup mm-gcobject-methods **/ + ///@{ + // const methods + /** 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; + /** memory consumption for this instance **/ + virtual size_type shallow_size(Copaque data) const noexcept = 0; + /** copy instance using allocator **/ + virtual Opaque shallow_copy(Copaque data, obj mm) const noexcept = 0; + + // nonconst methods + /** during GC: forward immdiate children **/ + virtual size_type forward_children(Opaque data, obj gc) const noexcept = 0; + ///@} +}; /*AGCObject*/ + +/** Implementation IGCObject_DRepr of AGCObject for state DRepr + * should provide a specialization: + * + * template <> + * struct xo::facet::FacetImplementation { + * using Impltype = IGCObject_DRepr; + * }; + * + * then IGCObject_ImplType --> IGCObject_DRepr + **/ +template +using IGCObject_ImplType = xo::facet::FacetImplType; + +} /*namespace mm*/ +} /*namespace xo*/ + +/* AGCObject.hpp */ diff --git a/include/xo/alloc2/gc/ICollector_Any.hpp b/include/xo/alloc2/gc/ICollector_Any.hpp new file mode 100644 index 0000000..6ed758c --- /dev/null +++ b/include/xo/alloc2/gc/ICollector_Any.hpp @@ -0,0 +1,55 @@ +/** @file ICollector_Any.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "ACollector.hpp" +#include "AGCObject.hpp" +//#include + +namespace xo { + namespace mm { struct ICollector_Any; } + + namespace facet { + template <> + struct FacetImplementation { + using ImplType = xo::mm::ICollector_Any; + }; + } + + namespace mm { + /** @class ICollector_Any + * @brief Stub Collector Implementation for empty variant instance + **/ + struct ICollector_Any : public ACollector { + using typeseq = xo::facet::typeseq; + using size_type = std::size_t; + + // from ACollector + typeseq _typeseq() const noexcept override { return s_typeseq; } + + // const methods + [[noreturn]] size_type allocated(Copaque, generation, role) const noexcept override { _fatal(); } + [[noreturn]] size_type reserved(Copaque, generation, role) const noexcept override { _fatal(); } + [[noreturn]] size_type committed(Copaque, generation, role) const noexcept override { _fatal(); } + [[noreturn]] bool is_type_installed(Copaque, typeseq) const noexcept override { _fatal(); } + + // non-const methods + [[noreturn]] bool install_type(Opaque, const AGCObject &) noexcept override { _fatal(); } + [[noreturn]] void add_gc_root_poly(Opaque, obj *) override { _fatal(); } + [[noreturn]] void request_gc(Opaque, generation) override { _fatal(); } + [[noreturn]] void forward_inplace(Opaque, AGCObject *, void **) override { _fatal(); } + + private: + [[noreturn]] static void _fatal(); + + public: + static typeseq s_typeseq; + static bool _valid; + }; + } /*namespace mm*/ +} /*namespace xo*/ + +/* end ICollector_Any.hpp */ diff --git a/include/xo/alloc2/gc/ICollector_Xfer.hpp b/include/xo/alloc2/gc/ICollector_Xfer.hpp new file mode 100644 index 0000000..d7d43ef --- /dev/null +++ b/include/xo/alloc2/gc/ICollector_Xfer.hpp @@ -0,0 +1,83 @@ +/** @file ICollector_Xfer.hpp + * + * @author Roland Conybeare, 2025 + **/ + +#pragma once + +#include "ACollector.hpp" +#include "AGCObject.hpp" + +namespace xo { + namespace mm { + /** @class ICollector_Xfer + * + * Adapts typed ACollector implementation @tparam ICollector_DRepr + * to type-erased @ref ACollector interface + * + * See for example + * @ref ICollector_DX1Collector + **/ + template + struct ICollector_Xfer : public ACollector { + public: + using Impl = ICollector_DRepr; + using size_type = ACollector::size_type; + + static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; } + static DRepr & _dcast(Opaque d) { return *(DRepr *)d; } + + // from ACollector + + // const methods + + typeseq _typeseq() const noexcept override { return s_typeseq; } + size_type allocated(Copaque d, generation g, role r) const noexcept override { + return I::allocated(_dcast(d), g, r); + } + size_type reserved(Copaque d, generation g, role r) const noexcept override { + return I::reserved(_dcast(d), g, r); + } + size_type committed(Copaque d, generation g, role r) const noexcept override { + return I::committed(_dcast(d), g, r); + } + bool is_type_installed(Copaque d, typeseq tseq) const noexcept override { + return I::is_type_installed(_dcast(d), tseq); + } + + // non-const methods + + bool install_type(Opaque d, const AGCObject & iface) override { + return I::install_type(_dcast(d), iface); + } + void add_gc_root_poly(Opaque d, obj * p_root) override { + I::add_gc_root_poly(_dcast(d), p_root); + } + void request_gc(Opaque d, generation upto) override { + I::request_gc(_dcast(d), upto); + } + void forward_inplace(Opaque d, + AGCObject * lhs_iface, void ** lhs_data) override { + I::forward_inplace(_dcast(d), lhs_iface, lhs_data); + } + + private: + using I = Impl; + + public: + static typeseq s_typeseq; + static bool _valid; + }; + + template + xo::facet::typeseq + ICollector_Xfer::s_typeseq = facet::typeseq::id(); + + template + bool + ICollector_Xfer::_valid = facet::valid_facet_implementation(); + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end ICollector_Xfer.hpp */ diff --git a/include/xo/alloc2/gc/IGCObject_Any.hpp b/include/xo/alloc2/gc/IGCObject_Any.hpp new file mode 100644 index 0000000..9ff7bb2 --- /dev/null +++ b/include/xo/alloc2/gc/IGCObject_Any.hpp @@ -0,0 +1,93 @@ +/** @file IGCObject_Any.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/GCObject.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/GCObject.json5] + **/ + +#pragma once + +#include "AGCObject.hpp" +#include + +namespace xo { namespace mm { class IGCObject_Any; } } + +namespace xo { +namespace facet { + +template <> +struct FacetImplementation +{ + using ImplType = xo::mm::IGCObject_Any; +}; + +} +} + +namespace xo { +namespace mm { + + /** @class IGCObject_Any + * @brief AGCObject implementation for empty variant instance + **/ + class IGCObject_Any : public AGCObject { + public: + /** @defgroup mm-gcobject-any-type-traits **/ + ///@{ + + /** integer identifying a type **/ + using typeseq = xo::facet::typeseq; + using size_type = AGCObject::size_type; + using AAllocator = AGCObject::AAllocator; + using ACollector = AGCObject::ACollector; + + ///@} + /** @defgroup mm-gcobject-any-methods **/ + ///@{ + + const AGCObject * iface() const { return std::launder(this); } + + // from AGCObject + + // builtin methods + typeseq _typeseq() const noexcept override { return s_typeseq; } + [[noreturn]] void _drop(Opaque) const noexcept override { _fatal(); } + + // const methods + [[noreturn]] size_type shallow_size(Copaque) const noexcept override { _fatal(); } + [[noreturn]] Opaque shallow_copy(Copaque, obj) const noexcept override { _fatal(); } + + // nonconst methods + [[noreturn]] size_type forward_children(Opaque, obj) const noexcept override; + + ///@} + + private: + /** @defgraoup mm-gcobject-any-private-methods **/ + ///@{ + + [[noreturn]] static void _fatal(); + + ///@} + + public: + /** @defgroup mm-gcobject-any-member-vars **/ + ///@{ + + static typeseq s_typeseq; + static bool _valid; + + ///@} + }; + +} /*namespace mm */ +} /*namespace xo */ + +/* IGCObject_Any.hpp */ diff --git a/include/xo/alloc2/gc/IGCObject_Xfer.hpp b/include/xo/alloc2/gc/IGCObject_Xfer.hpp new file mode 100644 index 0000000..12010ed --- /dev/null +++ b/include/xo/alloc2/gc/IGCObject_Xfer.hpp @@ -0,0 +1,95 @@ +/** @file IGCObject_Xfer.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/GCObject.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/GCObject.json5] + **/ + +#pragma once + +#include +#include +#include +#include + +namespace xo { +namespace mm { + /** @class IGCObject_Xfer + **/ + template + class IGCObject_Xfer : public AGCObject { + public: + /** @defgroup mm-gcobject-xfer-type-traits **/ + ///@{ + /** actual implementation (not generated; often delegates to DRepr) **/ + using Impl = IGCObject_DRepr; + /** integer identifying a type **/ + using typeseq = AGCObject::typeseq; + using size_type = AGCObject::size_type; + using AAllocator = AGCObject::AAllocator; + using ACollector = AGCObject::ACollector; + ///@} + + /** @defgroup mm-gcobject-xfer-methods **/ + ///@{ + + static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; } + static DRepr & _dcast(Opaque d) { return *(DRepr *)d; } + + // from AGCObject + + // builtin methods + typeseq _typeseq() const noexcept override { return s_typeseq; } + void _drop(Opaque d) const noexcept override { _dcast(d).~DRepr(); } + + // const methods + size_type shallow_size(Copaque data) const noexcept override { + return I::shallow_size(_dcast(data)); + } + Opaque shallow_copy(Copaque data, obj mm) const noexcept override { + return I::shallow_copy(_dcast(data), mm); + } + + // non-const methods + size_type forward_children(Opaque data, obj gc) const noexcept override { + return I::forward_children(_dcast(data), gc); + } + + ///@} + + private: + using I = Impl; + + public: + /** @defgroup mm-gcobject-xfer-member-vars **/ + ///@{ + + /** typeseq for template parameter DRepr **/ + static typeseq s_typeseq; + /** true iff satisfies facet implementation **/ + static bool _valid; + + ///@} + }; + + template + xo::facet::typeseq + IGCObject_Xfer::s_typeseq + = xo::facet::typeseq::id(); + + template + bool + IGCObject_Xfer::_valid + = xo::facet::valid_facet_implementation(); + +} /*namespace mm */ +} /*namespace xo*/ + +/* end IGCObject_Xfer.hpp */ diff --git a/include/xo/alloc2/gc/RCollector.hpp b/include/xo/alloc2/gc/RCollector.hpp new file mode 100644 index 0000000..9cce484 --- /dev/null +++ b/include/xo/alloc2/gc/RCollector.hpp @@ -0,0 +1,69 @@ +/** @file RCollector.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#include "ACollector.hpp" +#include + +namespace xo { + namespace mm { + /** @class RCollector **/ + template + struct RCollector : public Object { + private: + using O = Object; + public: + using ObjectType = Object; + using DataPtr = Object::DataPtr; + using size_type = std::size_t; + using typeseq = ACollector::typeseq; + //using value_type = std::byte *; + + RCollector() = default; + RCollector(DataPtr data) : Object{std::move(data)} {} + + /** forward op in place. Defined in GCObject.hpp to avoid #include cycle **/ + template + void forward_inplace(obj * p_obj); + + /** another convenience template for forwarding. + * Defined in RGCObject.hpp to avoid #include cycle. + **/ + template + void forward_inplace(DRepr ** pp_repr); + + int32_t _typeseq() const noexcept { return O::iface()->_typeseq(); } + size_type allocated(generation g, role r) const noexcept { return O::iface()->allocated(O::data(), g, r); } + size_type reserved(generation g, role r) const noexcept { return O::iface()->reserved(O::data(), g, r); } + size_type committed(generation g, role r) const noexcept { return O::iface()->committed(O::data(), g, r); } + bool is_type_installed(typeseq tseq) const noexcept { return O::iface()->is_type_installed(O::data(), tseq); } + + bool install_type(const AGCObject & iface) { return O::iface()->install_type(O::data(), iface); } + void add_gc_root_poly(obj * p_root) { O::iface()->add_gc_root_poly(O::data(), p_root); } + void request_gc(generation g) { O::iface()->request_gc(O::data(), g); } + void forward_inplace(AGCObject * lhs_iface, void ** lhs_data) { O::iface()->forward_inplace(O::data(), lhs_iface, lhs_data); } + + /** add root @p p_root **/ + template + void add_gc_root(obj * p_root) { + O::iface()->add_gc_root_poly(O::data(), (obj *)p_root); + } + + static bool _valid; + }; + + template + bool + RCollector::_valid = facet::valid_object_router(); + } /*namespace mm*/ + + namespace facet { + template + struct RoutingFor { + using RoutingType = xo::mm::RCollector; + }; + } +} /*namespace xo*/ + +/* end RCollector.hpp */ diff --git a/include/xo/alloc2/gc/RGCObject.hpp b/include/xo/alloc2/gc/RGCObject.hpp new file mode 100644 index 0000000..d24994a --- /dev/null +++ b/include/xo/alloc2/gc/RGCObject.hpp @@ -0,0 +1,93 @@ +/** @file RGCObject.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/GCObject.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/GCObject.json5] + **/ + +#pragma once + +#include "AGCObject.hpp" + +namespace xo { +namespace mm { + +/** @class RGCObject + **/ +template +class RGCObject : public Object { +private: + using O = Object; + +public: + /** @defgroup mm-gcobject-router-type-traits **/ + ///@{ + using ObjectType = Object; + using DataPtr = Object::DataPtr; + using typeseq = xo::reflect::typeseq; + using size_type = AGCObject::size_type; + using AAllocator = AGCObject::AAllocator; + using ACollector = AGCObject::ACollector; + ///@} + + /** @defgroup mm-gcobject-router-ctors **/ + ///@{ + RGCObject() {} + RGCObject(Object::DataPtr data) : Object{std::move(data)} {} + RGCObject(const AGCObject * iface, void * data) + requires std::is_same_v + : Object(iface, data) {} + + ///@} + /** @defgroup mm-gcobject-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 + size_type shallow_size() const noexcept { + return O::iface()->shallow_size(O::data()); + } + Opaque shallow_copy(obj mm) const noexcept { + return O::iface()->shallow_copy(O::data(), mm); + } + + // non-const methods (still const in router!) + size_type forward_children(obj gc) noexcept { + return O::iface()->forward_children(O::data(), gc); + } + + ///@} + /** @defgroup mm-gcobject-member-vars **/ + ///@{ + + static bool _valid; + + ///@} +}; + +template +bool +RGCObject::_valid = xo::facet::valid_object_router(); + +} /*namespace mm*/ +} /*namespace xo*/ + +namespace xo { namespace facet { + template + struct RoutingFor { + using RoutingType = xo::mm::RGCObject; + }; +} } + +/* end RGCObject.hpp */ diff --git a/include/xo/alloc2/generation.hpp b/include/xo/alloc2/generation.hpp new file mode 100644 index 0000000..12b36d0 --- /dev/null +++ b/include/xo/alloc2/generation.hpp @@ -0,0 +1,36 @@ +/** @file generation.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include +#include + +namespace xo { + namespace mm { + /** hard maximum number of generations **/ + static constexpr uint32_t c_max_generation = 16; + + /** @class generation + * @brief type-safe generation number + **/ + struct generation { + using value_type = std::uint32_t; + + constexpr generation() = default; + explicit constexpr generation(value_type x) : value_{x} {} + + static generation nursery() { return generation{0}; } + + constexpr operator value_type() const { return value_; } + + generation & operator++() { ++value_; return *this; } + + std::uint32_t value_ = 0; + }; + } +} + +/* end generation.hpp */ diff --git a/include/xo/alloc2/role.hpp b/include/xo/alloc2/role.hpp new file mode 100644 index 0000000..58fc72a --- /dev/null +++ b/include/xo/alloc2/role.hpp @@ -0,0 +1,35 @@ +/** @file role.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include +#include + +namespace xo { + namespace mm { + static constexpr uint32_t c_n_role = 2; + + struct role { + using value_type = std::uint32_t; + + explicit constexpr role(value_type x) : role_{x} {} + + static constexpr role to_space() { return role{0}; } + static constexpr role from_space() { return role{1}; } + + static constexpr std::array all() { return {{to_space(), from_space()}}; } + + static constexpr role begin() { return role{0}; } + static constexpr role end() { return role{2}; } + + operator value_type() const { return role_; } + + value_type role_ = 0; + }; + } /*namespace mm*/ +} /*namespace xo*/ + +/* end role.hpp */