144 lines
5.6 KiB
C++
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 */
|