/** @file GCObjectStore.hpp * * @author Roland Conybeare, Apr 2026 **/ #pragma once #include "GCObjectStoreConfig.hpp" #include "ObjectTypeSlot.hpp" #include "object_age.hpp" #include #include namespace xo { namespace mm { class X1VerifyStats; /** @brief container to hold gc-aware objects for X1 collector * * Note: X1VerifyStats are in DX1Collector. * They need to be there, since also interact with MutationLogStore. * This is reason for DX1Collector to invoke .verify_aux() * so it can supply X1VerifyStats location **/ class GCObjectStore { public: using ObjectTypeTable = DArenaVector; /* TODO: AllocIterator pointing to free pointer instead of std::byte* */ using GCMoveCheckpoint = std::array; using header_type = DArena::header_type; using value_type = DArena::value_type; using size_type = DArena::size_type; using typeseq = xo::reflect::typeseq; public: explicit GCObjectStore(const GCObjectStoreConfig & cfg, X1VerifyStats * p_verify_stats); const GCObjectStoreConfig & config() const noexcept { return config_; } const ObjectTypeTable * get_object_types() const noexcept { return &object_types_; } const DArena * get_space(Role r, Generation g) const noexcept { return space_[r][g]; } const DArena * from_space(Generation g) const noexcept { return this->get_space(Role::from_space(), g); } const DArena * to_space(Generation g) const noexcept { return this->get_space(Role::to_space(), g); } const DArena * new_space() const noexcept { return this->get_space(Role::to_space(), Generation{0}); } DArena * get_space(Role r, Generation g) noexcept { return space_[r][g]; } DArena * from_space(Generation g) noexcept { return this->get_space(Role::from_space(), g); } DArena * to_space(Generation g) noexcept { return this->get_space(Role::to_space(), g); } DArena * new_space() noexcept { return this->get_space(Role::to_space(), Generation{0}); } X1VerifyStats * verify_stats() noexcept { return p_verify_stats_; } /** true iff type with id @p tseq has known metadata * (i.e. has appeared in preceding call to install_type * for this collector) **/ bool is_type_installed(typeseq tseq) const noexcept; /** lookup interface from type sequence * (can use tseq = typeseq::id() for type T) **/ AGCObject * lookup_type(typeseq tseq) const noexcept; /** generation to which pointer @p addr belongs, given Role @p r; * sentinel if not found in this collector **/ Generation generation_of(Role r, const void * addr) const noexcept; /** return details from last error (i.e. from g0 to-space) **/ AllocError last_error() const noexcept; /** get allocation size from header **/ std::size_t header2size(header_type hdr) const noexcept; /** get generation counter from alloc header **/ object_age header2age(header_type hdr) const noexcept; /** get tseq from alloc header **/ uint32_t header2tseq(header_type hdr) const noexcept; /** true iff original alloc has been replaced by a forwarding pointer **/ bool is_forwarding_header(header_type hdr) const noexcept; /** Retreive bookkeeping info for allocation at @p mem. **/ AllocInfo alloc_info(value_type mem) const noexcept; /** Call @p visitor for each memory pool owned by this store **/ void visit_pools(const MemorySizeVisitor & visitor) const; /** true iff address @p addr allocated from this collector * in Role @p r (according to current GC state) **/ bool contains(Role r, const void * addr) const noexcept; /** true iff address @p addr allocated from this collector and currently live * in Role @p r (according to current GC state) * * (i.e. in [lo,free) for an arena) **/ bool contains_allocated(Role r, const void * addr) const noexcept; /** Report per-age-bucket information as an array of dictionaries. * Scans to-space to count per-age statistics. * Each dictionary has keys "n-live" and "bytes". * Array index corresponds to object age. * * @p mm allocate stats from this allocator. * @p error_mm allocator for error reporting when out-of-memory. * @p p_output on exit @p *p_output contains stats array **/ bool report_object_types(obj mm, obj error_mm, obj * p_output) const noexcept; /** Report per-age-bucket information as an array of dictionaries. * Scans to-space to count per-age statistics. * Each dictionary has keys "n-live" and "bytes". * Array index corresponds to object age. * * @p mm allocate stats from this allocator. * @p error_mm allocator for error reporting when out-of-memory. * @p p_output on exit @p *p_output contains stats array **/ bool report_object_ages(obj mm, obj error_mm, obj * p_output) const noexcept; /** snap checkpoint containing allocator state * use to detect forwarding activity after visiting objects **/ GCMoveCheckpoint snap_move_checkpoint(Generation upto); /** verify consistency of this object store, on behalf of collector @p gc. * Advancing counters in @p *p_verify_stats. * * @p gc argument is load-bearing so we have collector interface * to call AGCObject visitor method (forward_children()) on each * object stored here. **/ void verify_ok() noexcept; /** Register object type with this collector. * Provides shallow copy and pointer forwarding for instances of this * type. **/ bool install_type(const AGCObject & meta) noexcept; /** For each generation g in [0 ,.., upto) * swap arenas assigned to {to-space, from-space}. * Invoked once at the beginning of each gc cycle. **/ void swap_roles(Generation upto) noexcept; /** move subgraph at @p root to to-space on behalf of collector @p gc * Special behavior relative to @ref _deep_move_interior : * If @p root is not in gc-space, visit immediate children and move them in place (!). * * @return new address for @p from_src * * Require: runstate_.is_running() **/ void * deep_move_root(const AGCObject * root_iface, void ** root_data, Generation upto); /** move interior subgraph at @p from_src to to-space. * no-op if not in gc-space. * * NOTE: load-bearing for MutationLogStore **/ void * deep_move_interior(obj gc, void * from_src, Generation upto); /** Target for GCObjectVisitor facet * During gc phase (@p reason is 'forward') * 1. evacuate object at @p *lhs_data to to-space. * 2. replace @p *lhs_data with forwarding pointer * to new location. **/ void visit_child_aux(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data, Generation upto); /** Evacuate object at @p *lhs_data to to-space, during collection phase * acting on generations g in [0 ,.., upto). * Need @p gc to pass to invoke AGCObject methods shallow_copy() and * forward_children() * * Replace original with forwarding pointer to new location **/ void forward_inplace_aux(obj gc, AGCObject * lhs_iface, void ** lhs_data, Generation upto); /** categorize fop {@p lhs_iface, @p lhs_data} * based on location of @p lhs_data. * Update @ref p_verify_stats_ based on the result: * increment exactly one of {n_from_, n_to_, n_ext_} **/ void verify_aux(AGCObject * lhs_iface, void * lhs_data); /** Cleanup at the end of a gc cycle. * Reset from-space * (current from-space is former to-space, * relabeled at the beginning of collector cycle) * for generations in [0 ,.., upto) **/ void cleanup_phase(Generation upto, bool sanitize_flag); private: /** configure @ref object_types_, using @p page_z **/ void _init_object_types(std::size_t page_z); /** auxiliary init function **/ void _init_space(); /** true iff {@p alloc_hdr, @p object_data} should move for * a collection of all generations strictly younger than @p upto. * * Require: runstate_.is_running() **/ bool _check_move_policy(header_type alloc_hdr, void * gco_data, Generation upto) const noexcept; /** Common driver for _deep_move_root(), _deep_move_interior(). * Move object subgraph @p from_src on behalf of @p gc collection cycle, * covering generations in [0 ,.., upto). **/ void * _deep_move_gc_owned(obj gc, void * from_src, Generation upto); /** traverse objects allocated after @p ckp, to make sure their children * are forwarded. Repeat until traverse doesn't find any unforwarded children. * * 1. Breadth-first implementation, bad for memory locality * 2. Need @p gc for per-object-type forward_children api **/ void _forward_children_until_fixpoint(obj gc, Generation upto, GCMoveCheckpoint gray_lo_v); /** during a gc cycle: * evacuate object @p from_src, with gc-object interface @p iface. * Shallow: does not traverse children **/ void * _shallow_move(obj gc, AGCObject * iface, void * from_src); private: /** configuration for gc-aware object store **/ GCObjectStoreConfig config_; /** gc-aware object types **/ ObjectTypeTable object_types_; /** arena objects for collector managed memory * 1:1 with roles, but polarity reverses for each collection **/ std::array space_storage_[c_n_role]; /** arena pointers. The roles of space_storage_[0][g] and space_storage_[1][g] * are reversed each time generation g gets collected. **/ std::array space_[c_n_role]; /** dedicated counters. updated by .verify_aux() **/ X1VerifyStats * p_verify_stats_ = nullptr; }; } /*namespace mm*/ } /*namespace xo*/ /* end GCObjectStore.hpp */