xo-umbrella2/xo-facet/include/xo/facet/AllocFlywheel.hpp
Roland Conybeare b6d4acca82
Some checks failed
cmake-docker / cmake-build (clang, clang++) (push) Failing after 10m52s
CI / smoke-test (push) Has been cancelled
cmake-docker / cmake-build (gcc, g++) (push) Has been cancelled
xo-facet: common base align recovers alloc size from ptr [REFACTOR]
2026-09-25 08:20:21 -04:00

144 lines
5.6 KiB
C++

/** @file AllocFlywheel.hpp
*
* @author Roland Conybeare, Sep 2026
**/
#pragma once
#include "cx/FacetAppcx.hpp"
#include "handlestore/DHandleStore.hpp"
#include "handlestore/ObjectSlot.hpp"
#include "Top.hpp"
#include <xo/refcnt/Displayable.hpp>
namespace xo::facet {
using xo::mm::DArenaVector;
/** @brief memory flywheel for python bindings
*
* Consolidated memory pool and reference set,
* for the sake of object handles with automatic
* memory management.
**/
class AllocFlywheel : public xo::ref::Displayable {
public:
using ATop = xo::facet::ATop;
using HandleStore = DHandleArena<ObjectSlot>; // was obj<ATop>
using handle_type = typename HandleStore::handle_type;
using handle_index_type = typename HandleStore::handle_index_type;
using PpSink = xo::pp::PpSink;
using DArena = xo::mm::DArena;
using ArenaConfig = xo::mm::ArenaConfig;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
public:
AllocFlywheel(const FacetAppcx & appcx,
DArena && storage,
DArenaVector<ObjectSlot> && strong,
DArenaVector<handle_index_type> && strong_freelist);
/** Create new instance from configuration.
* We don't ussually heap-allocate.
* Exception here because AllocFlywheel may be a
* global entry point for python bindings.
*
* @p appcx. Proof of work (as of Sep2026: facet+indentlog2)
* @p storage_cfg. Configures primary arena.
* @p strong_root_cfg. Configures strong root set.
*
* Free-list arenas are derived from the corresponding root-set configs.
**/
static rp<AllocFlywheel> make_app(const FacetAppcx & appcx,
const ArenaConfig & storage_cfg,
const ArenaConfig & strong_root_cfg);
/** Provide default arguments to make_app() **/
static rp<AllocFlywheel> make_default_app(const FacetAppcx & appcx);
FacetAppcx::CreationEvidence facetappcx_creation_evidence() const { return facetappcx_creation_evidence_; }
Indentlog2Appcx::CreationEvidence indentlog2appcx_creation_evidence() const { return indentlog2appcx_creation_evidence_; }
DArena & storage() { return store_.storage(); }
/** the strong root set, for a reader that wants to render it.
**/
const HandleStore & strong_root_set() const { return store_; }
/** report memory consumption, one @ref MemorySizeInfo per pool.
*
* Three pools, in the order @ref HandleStore visits them: the primary
* arena objects are allocated from, the strong root set, and the free
* list serving that set. Const: reporting must not be able to disturb
* what it measures.
*
* NB reserved/committed/used are three different numbers here. A root
* set reserves its whole configured extent up front and commits as it
* grows, so "reserved" says what a flywheel COULD consume and
* "committed" what it currently does.
**/
void visit_pools(const MemorySizeVisitor & fn) const { store_.visit_pools(fn); }
/** visit every root slot, cleared ones included -- see
* DHandleStore::visit_object_slots
**/
template <typename Fn>
void visit_object_slots(Fn && fn) const {
store_.visit_object_slots(std::forward<Fn>(fn));
}
/** visit each released slot's index, oldest release first; the LAST
* one visited is what the next @ref add_strong_ref reuses -- see
* DHandleStore::visit_free_list
**/
template <typename Fn>
void visit_free_list(Fn && fn) const {
store_.visit_free_list(std::forward<Fn>(fn));
}
/** slots ever allocated in the strong root set: a high-water mark.
* @ref strong_root_count is the population
**/
handle_index_type strong_size() const { return store_.strong_size(); }
/** slots the strong root set can hold **/
handle_index_type strong_capacity() const { return store_.strong_capacity(); }
/** adopt the erased fop (@p iface, @p data) into this flywheel's
* strong reference set.
*
* Require: @p data was allocated from @ref storage.
**/
std::pair<handle_index_type, handle_type*>
add_strong_ref(const ATop * iface, void * data);
/** release the strong slot at @p ix, returning it for reuse.
*
* Called by ~ObjectHandleBase, so dropping the last python reference
* to a handle unpins its object. Idempotent per index; see
* DHandleStore::remove_strong_ref for what that does and does not
* protect.
**/
void remove_strong_ref(handle_index_type ix);
/** count number of non-empty root slots. **/
handle_index_type strong_root_count() const;
// from Displayable
virtual void pretty(PpSink & pp) const override;
virtual std::string display_string() const override;
private:
/** evidence that a FacetAppcx instance was created **/
FacetAppcx::CreationEvidence facetappcx_creation_evidence_;
/** evidence that a Indentlog2Appcx instance was created **/
Indentlog2Appcx::CreationEvidence indentlog2appcx_creation_evidence_;
/** combined allocator and root set **/
HandleStore store_;
};
} /*namespace xo::facet*/
/* end AllocFlywheel.hpp */