diff --git a/xo-alloc2/include/xo/alloc2/ACollector.hpp b/xo-alloc2/include/xo/alloc2/ACollector.hpp new file mode 100644 index 00000000..1b4cf4c0 --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/ACollector.hpp @@ -0,0 +1,45 @@ +/** @file ACollector.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#include "gc/generation.hpp" +#include "gc/role.hpp" + +#include +#include + +namespace xo { + namespace mm { + using Copaque = const void *; + using Opaque = void *; + + /** @class ACollector + * @brief Abstract facet for the XO garbage collector + * + * Collector also supports the @ref AAllocator facet, see also + **/ + struct ACollector { + using size_type = std::size_t; + + virtual int32_t _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; + + /** 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. + **/ + virtual void install_type(Opaque d, int32_t tseq, AGCObject_Any & iface); + virtual void add_gc_root(Opaque d, int32_t tid, Opaque * root) = 0; + }; + } + +} /*namespace xo*/ diff --git a/xo-alloc2/include/xo/alloc2/AGCObject.hpp b/xo-alloc2/include/xo/alloc2/AGCObject.hpp new file mode 100644 index 00000000..ef9e33aa --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/AGCObject.hpp @@ -0,0 +1,50 @@ +/** @file AGCObject.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "xo/facet/facet_implementation.hpp" +#include "xo/facet/typeseq.hpp" +#include +#include + +namespace xo { + namespace mm { + using Copaque = const void *; + using Opaque = void *; + + /** @class AObject + * @brief Abstract facet for collector-eligible data + * + * Data that supports AGCObject can have memory managed + * by ACollector + **/ + struct AGCObject { + using size_type = std::size_t; + + /** RTTI: unique id# for actual runtime data representation **/ + virtual int32_t _typeseq() const noexcept = 0; + + virtual size_type shallow_size(Copaque d) const noexcept = 0; + virtual Opaque * shallow_copy(Copaque d) const noexcept = 0; + virtual size_type forward_children(Opaque d) const noexcept = 0; + }; + + // 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*/ + +/* end AGCObject.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/DCollector.hpp b/xo-alloc2/include/xo/alloc2/DCollector.hpp new file mode 100644 index 00000000..af4bf1d5 --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/DCollector.hpp @@ -0,0 +1,84 @@ +/** @file DCollector.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "ArenaConfig.hpp" +#include "DArena.hpp" +#include + +namespace xo { + namespace mm { + template + using up = std::unique_ptr; + + struct CollectorConfig { + using size_type = std::size_t; + + /** Configuration for collector spaces. + * Will have at least {nursery,tenured} x {from,to} spaces. + * Not using name_ member. + **/ + ArenaConfig arena_config_; + + /** Number of generations. + * Must be at least 2. + **/ + uint32_t n_generation_ = 2; + + /** Number of promotion steps. + * An object that survives this number of collections + * advances to the next generation. + **/ + uint32_t n_survive_threshold_ = 2; + + /** Trigger garbage collection when to-space allocation for + * generation g reaches gc_trigger_v_[g] + **/ + std::array gc_trigger_v_; + + /** true -> enable incremental collection. + * false -> only do full collection. + * + * Incremental collection requires mutation logs. + **/ + bool allow_incremental_gc_ = true; + + /** If non-zero remember statistics for + * the last @p stats_history_z_ collections. + **/ + uint32_t stats_history_z_ = false; + + /** true to enable debug logging **/ + bool debug_flag_ = false; + }; + + /** State associated with a single DCollector generation + **/ + struct Generation { + Generation(uint8_t gen_id, up from_space, up to_space); + ~Generation() = default; + + /** identity of this generation. Generations are numbered from + * 0 (youngest) to N (oldest), with N <= c_max_generation + **/ + uint8_t gen_id_; + /** from-space. empty between collection episodes. + * During collection holds former to-space + **/ + up from_space_; + /** to-space. New allocations occur here **/ + up to_space_; + }; + + struct DCollector { + std::uint32_t + + std::array, c_max_generation> generations_[2]; + }; + } /*namespace mm*/ +} /*namespace xo*/ + +/* end DCollector.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/IGCObject_Any.hpp b/xo-alloc2/include/xo/alloc2/IGCObject_Any.hpp new file mode 100644 index 00000000..114b17a4 --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/IGCObject_Any.hpp @@ -0,0 +1,47 @@ +/** @file IGCObject_Any.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "AGCObject.hpp" +#include + +namespace xo { + namespace mm { struct IGCObject_Any; } + + namespace facet { + template <> + struct FacetImplementation { + using ImplType = xo::mm::IGCObject_Any; + }; + } + + namespace mm { + /** @class IGCObject_Any + * @brief AGCObject implementation for empty variant instance + **/ + struct IGCObject_Any : public AGCObject { + using size_type = std::size_t; + + const AGCObject * iface() const { return std::launder(this); } + + // from AGCObject + int32_t _typeseq() const noexcept override { return s_typeseq; } + + [[noreturn]] size_type shallow_size() const noexcept override { _fatal(); } + [[noreturn]] Opaque * shallow_copy() const noexcept override { _fatal(); } + [[noreturn]] size_type forward_children() const noexcept override { _fatal(); } + + private: + [[noreturn]] static void _fatal(); + + public: + static int32_t s_typeseq; + static bool _valid; + }; + } /*namespace mm*/ +} /*namespace xo*/ + +/* end IGCObject_Any.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/IGCObject_Xfer.hpp b/xo-alloc2/include/xo/alloc2/IGCObject_Xfer.hpp new file mode 100644 index 00000000..1756d389 --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/IGCObject_Xfer.hpp @@ -0,0 +1,62 @@ +/** @file IGCObject_Xfer.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "AGCObject.hpp" + +namespace xo { + namespace mm { + /** @class IGCObject_Xfer + * + * Adapts typed GC object implementation @tparam IGCObject_DRepr + * to type-erased @ref AGCObject interface + **/ + template + struct IGCObject_Xfer : public AGCObject { + using Impl = IGCObject_DRepr; + using size_type = AGCObject::size_type; + + static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; } + static DRepr & _dcast(Opaque d) { return *(DRepr *)d; } + + // from AGCObject + + // const methods + + int32_t _typeseq() const noexcept override { return s_typeseq; } + size_type shallow_size(Copaque d) const noexcept override { + return I::shallow_copy(_dcast(d)); + } + Opaque * shallow_copy(Copaque d) const noexcept override { + return I::shallow_size(_dcast(d)); + } + + // non-const methods + + size_type forward_children(Opaque d) const noexcept override { + return I::forward_children(d); + } + + private: + using I = Impl; + + public: + static int32_t s_typeseq; + static bool _valid; + }; + + template + int32_t + IGCObject_Xfer::s_typeseq = facet::typeseq::id(); + + template + bool + IGCObject_Xfer::_valid = facet::valid_facet_implementation(); + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end IGCObject_Xfer.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/gc/generation.hpp b/xo-alloc2/include/xo/alloc2/gc/generation.hpp new file mode 100644 index 00000000..e7a39eea --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/gc/generation.hpp @@ -0,0 +1,29 @@ +/** @file generation.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#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; + + explicit generation(value_type x) : value_{x} {} + + operator value_type() const { return value_; } + + std::uint32_t value_; + }; + } +} + +/* end generation.hpp */ diff --git a/xo-alloc2/include/xo/alloc2/gc/role.hpp b/xo-alloc2/include/xo/alloc2/gc/role.hpp new file mode 100644 index 00000000..619522af --- /dev/null +++ b/xo-alloc2/include/xo/alloc2/gc/role.hpp @@ -0,0 +1,32 @@ +/** @file role.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include + +namespace xo { + namespace mm { + enum class role { + /** GC will keep one to-space for each generation. + * Application allocs always happen in to-space. + **/ + to_space, + /** During normal operation from-space is empty. + * During collection phase itself, + * to-space and from-space are exchanged, + * with from-space becoming the space to be collected + **/ + from_space, + /** counts entries **/ + N + }; + + constexpr uint32_t role2int(role x) { return static_cast(x); } + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end role,hpp */ diff --git a/xo-alloc2/src/alloc2/IGCObject_Any.cpp b/xo-alloc2/src/alloc2/IGCObject_Any.cpp new file mode 100644 index 00000000..788fe413 --- /dev/null +++ b/xo-alloc2/src/alloc2/IGCObject_Any.cpp @@ -0,0 +1,34 @@ +/** @file IGCObject_Any.cpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#include "IGCObject_Any.hpp" +#include + +namespace xo { + using xo::facet::DVariantPlaceholder; + using xo::facet::typeseq; + using xo::facet::valid_facet_implementation; + + namespace mm { + + void + IGCObject_Any::_fatal() { + std::cerr << "fatal" + << ": attempt to call uninitialized" + << " IGCObject_Any method" + << std::endl; + std::terminate(); + } + + int32_t + IGCObject_Any::s_typeseq = typeseq::id(); + + bool + IGCObject_Any::_valid = valid_facet_implementation(); + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end IGCObject_Any.cpp */