xo-gc stack: + request-gc-statistics() primitive

1. xo-gc now depends on xo-object2.
2. use genfacet for ICollector_DX1Collector
3. moves xo-gc utest previously in xo-object2 to more natural
   location in xo-gc/
This commit is contained in:
Roland Conybeare 2026-03-29 13:44:19 -04:00
commit f7c269a505
13 changed files with 74 additions and 11 deletions

View file

@ -61,6 +61,15 @@
noexcept: true, noexcept: true,
attributes: [], attributes: [],
}, },
{
name: "error_allocator",
doc: [ "last-resort allocator for erros. e.g. regular allocator exhausted" ],
return_type: "obj<AAllocator>",
args: [],
const: true,
noexcept: true,
attributes: [],
},
{ {
name: "stringtable", name: "stringtable",
doc: [ "stringtable for unique symbols" ], doc: [ "stringtable for unique symbols" ],

View file

@ -23,16 +23,19 @@ namespace xo {
using MemorySizeVisitor = xo::mm::MemorySizeVisitor; using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
public: public:
DSimpleRcx(obj<AAllocator> mm, StringTable * st) DSimpleRcx(obj<AAllocator> mm, obj<AAllocator> error_mm, StringTable * st)
: allocator_{mm}, stringtable_{st} {} : allocator_{mm}, error_allocator_{error_mm},
stringtable_{st} {}
obj<AAllocator> allocator() const noexcept { return allocator_; } obj<AAllocator> allocator() const noexcept { return allocator_; }
obj<ACollector> collector() const noexcept; obj<ACollector> collector() const noexcept;
obj<AAllocator> error_allocator() const noexcept { return error_allocator_; }
StringTable * stringtable() const noexcept { return stringtable_; } StringTable * stringtable() const noexcept { return stringtable_; }
void visit_pools(const MemorySizeVisitor & visitor) const; void visit_pools(const MemorySizeVisitor & visitor) const;
private: private:
obj<AAllocator> allocator_; obj<AAllocator> allocator_;
obj<AAllocator> error_allocator_;
StringTable * stringtable_ = nullptr; StringTable * stringtable_ = nullptr;
}; };

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "Primitive_gco_0.hpp"
#include "Primitive_gco_1_gco.hpp" #include "Primitive_gco_1_gco.hpp"
namespace xo { namespace xo {
@ -18,6 +19,10 @@ namespace xo {
using AAllocator = xo::mm::AAllocator; using AAllocator = xo::mm::AAllocator;
public: public:
/** create primitive: report gc statistics **/
static DPrimitive_gco_0 * make_report_gc_statistics_pm(obj<AAllocator> mm,
StringTable * stbl);
/** create primitive: request collection **/ /** create primitive: request collection **/
static DPrimitive_gco_1_gco * make_request_gc_pm(obj<AAllocator> mm, static DPrimitive_gco_1_gco * make_request_gc_pm(obj<AAllocator> mm,
StringTable * stbl); StringTable * stbl);

View file

@ -47,6 +47,11 @@ public:
/** @defgroup scm-procedure-methods **/ /** @defgroup scm-procedure-methods **/
///@{ ///@{
// const methods // const methods
/** An uninitialized AProcedure instance will have zero vtable pointer (per {linux,osx} abi).
* Use case for this is narrow. We go to some lengths to avoid null vtable pointers. For example
* obj<AFacet> will have non-null vtable (via IFacet_Any) with all methods terminating.
**/
bool _has_null_vptr() const noexcept { return *reinterpret_cast<const void * const *>(this) == nullptr; }
/** RTTI: unique id# for actual runtime data representation **/ /** RTTI: unique id# for actual runtime data representation **/
virtual typeseq _typeseq() const noexcept = 0; virtual typeseq _typeseq() const noexcept = 0;
/** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/ /** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/

View file

@ -51,6 +51,11 @@ public:
/** @defgroup scm-runtimecontext-methods **/ /** @defgroup scm-runtimecontext-methods **/
///@{ ///@{
// const methods // const methods
/** An uninitialized ARuntimeContext instance will have zero vtable pointer (per {linux,osx} abi).
* Use case for this is narrow. We go to some lengths to avoid null vtable pointers. For example
* obj<AFacet> will have non-null vtable (via IFacet_Any) with all methods terminating.
**/
bool _has_null_vptr() const noexcept { return *reinterpret_cast<const void * const *>(this) == nullptr; }
/** RTTI: unique id# for actual runtime data representation **/ /** RTTI: unique id# for actual runtime data representation **/
virtual typeseq _typeseq() const noexcept = 0; virtual typeseq _typeseq() const noexcept = 0;
/** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/ /** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/
@ -59,6 +64,8 @@ public:
virtual obj<AAllocator> allocator(Copaque data) const noexcept = 0; virtual obj<AAllocator> allocator(Copaque data) const noexcept = 0;
/** collector facet for allocator. If non-null, same data pointer as allocator **/ /** collector facet for allocator. If non-null, same data pointer as allocator **/
virtual obj<ACollector> collector(Copaque data) const noexcept = 0; virtual obj<ACollector> collector(Copaque data) const noexcept = 0;
/** last-resort allocator for erros. e.g. regular allocator exhausted **/
virtual obj<AAllocator> error_allocator(Copaque data) const noexcept = 0;
/** stringtable for unique symbols **/ /** stringtable for unique symbols **/
virtual StringTable * stringtable(Copaque data) const noexcept = 0; virtual StringTable * stringtable(Copaque data) const noexcept = 0;
/** invoke visitor for each distinct memory pool **/ /** invoke visitor for each distinct memory pool **/

View file

@ -63,6 +63,7 @@ namespace scm {
// const methods // const methods
[[noreturn]] obj<AAllocator> allocator(Copaque) const noexcept override { _fatal(); } [[noreturn]] obj<AAllocator> allocator(Copaque) const noexcept override { _fatal(); }
[[noreturn]] obj<ACollector> collector(Copaque) const noexcept override { _fatal(); } [[noreturn]] obj<ACollector> collector(Copaque) const noexcept override { _fatal(); }
[[noreturn]] obj<AAllocator> error_allocator(Copaque) const noexcept override { _fatal(); }
[[noreturn]] StringTable * stringtable(Copaque) const noexcept override { _fatal(); } [[noreturn]] StringTable * stringtable(Copaque) const noexcept override { _fatal(); }
[[noreturn]] void visit_pools(Copaque, MemorySizeVisitor) const override { _fatal(); } [[noreturn]] void visit_pools(Copaque, MemorySizeVisitor) const override { _fatal(); }

View file

@ -52,6 +52,8 @@ namespace xo {
static obj<AAllocator> allocator(const DSimpleRcx & self) noexcept; static obj<AAllocator> allocator(const DSimpleRcx & self) noexcept;
/** collector facet for allocator. If non-null, same data pointer as allocator **/ /** collector facet for allocator. If non-null, same data pointer as allocator **/
static obj<ACollector> collector(const DSimpleRcx & self) noexcept; static obj<ACollector> collector(const DSimpleRcx & self) noexcept;
/** last-resort allocator for erros. e.g. regular allocator exhausted **/
static obj<AAllocator> error_allocator(const DSimpleRcx & self) noexcept;
/** stringtable for unique symbols **/ /** stringtable for unique symbols **/
static StringTable * stringtable(const DSimpleRcx & self) noexcept; static StringTable * stringtable(const DSimpleRcx & self) noexcept;
/** invoke visitor for each distinct memory pool **/ /** invoke visitor for each distinct memory pool **/

View file

@ -54,6 +54,9 @@ namespace scm {
obj<ACollector> collector(Copaque data) const noexcept override { obj<ACollector> collector(Copaque data) const noexcept override {
return I::collector(_dcast(data)); return I::collector(_dcast(data));
} }
obj<AAllocator> error_allocator(Copaque data) const noexcept override {
return I::error_allocator(_dcast(data));
}
StringTable * stringtable(Copaque data) const noexcept override { StringTable * stringtable(Copaque data) const noexcept override {
return I::stringtable(_dcast(data)); return I::stringtable(_dcast(data));
} }

View file

@ -61,6 +61,9 @@ public:
obj<ACollector> collector() const noexcept { obj<ACollector> collector() const noexcept {
return O::iface()->collector(O::data()); return O::iface()->collector(O::data());
} }
obj<AAllocator> error_allocator() const noexcept {
return O::iface()->error_allocator(O::data());
}
StringTable * stringtable() const noexcept { StringTable * stringtable() const noexcept {
return O::iface()->stringtable(O::data()); return O::iface()->stringtable(O::data());
} }

View file

@ -21,23 +21,35 @@ namespace xo {
// ----- report-gc-status ----- // ----- report-gc-status -----
#ifdef NOT_YET
obj<AGCObject> obj<AGCObject>
xfer_report_gc_status(obj<ARuntimeContext> rcx) xfer_report_gc_statistics(obj<ARuntimeContext> rcx)
{ {
bool have_gc = false;
if (rcx.collector()) { if (rcx.collector()) {
// status currently only implemented for X1 collector // status currently only implemented for X1 collector
auto gc = obj<ACollector,DX1Collector>::from(rcx.collector()); obj<AGCObject> stats;
bool ok = rcx.collector().report_statistics(rcx.allocator(),
rcx.error_allocator(),
&stats);
if (ok && stats)
return stats;
} }
return DBoolean::box(rcx.allocator(), false); return DBoolean::box(rcx.allocator(), false);
} }
#endif
DPrimitive_gco_0 *
GcPrimitives::make_report_gc_statistics_pm(obj<AAllocator> mm,
StringTable * stbl)
{
(void)stbl;
auto any_ty = DAtomicType::make(mm, Metatype::t_any());
auto pm_ty = obj<AType,DFunctionType>(DFunctionType::_make(mm, any_ty));
return DPrimitive_gco_0::_make(mm, "report-gc-statistics", pm_ty, &xfer_report_gc_statistics);
}
// ----- request-gc ----- // ----- request-gc -----

View file

@ -130,6 +130,13 @@ namespace xo {
ObjectPrimitives::make_fn_n_args_pm(mm, stbl), ObjectPrimitives::make_fn_n_args_pm(mm, stbl),
flags & InstallFlags::f_generalpurpose)); flags & InstallFlags::f_generalpurpose));
// ----- gc primitives -----
ok = ok & (PrimitiveRegistry::install_aux
(sink,
GcPrimitives::make_report_gc_statistics_pm(mm, stbl),
flags & InstallFlags::f_generalpurpose));
ok = ok & (PrimitiveRegistry::install_aux ok = ok & (PrimitiveRegistry::install_aux
(sink, (sink,
GcPrimitives::make_request_gc_pm(mm, stbl), GcPrimitives::make_request_gc_pm(mm, stbl),

View file

@ -27,6 +27,12 @@ namespace xo {
return self.collector(); return self.collector();
} }
auto
IRuntimeContext_DSimpleRcx::error_allocator(const DSimpleRcx & self) noexcept -> obj<AAllocator>
{
return self.error_allocator();
}
auto auto
IRuntimeContext_DSimpleRcx::stringtable(const DSimpleRcx & self) noexcept -> StringTable * IRuntimeContext_DSimpleRcx::stringtable(const DSimpleRcx & self) noexcept -> StringTable *
{ {

View file

@ -39,7 +39,7 @@ namespace xo {
auto stbl = StringTable(1024 /*hint_max_capacity*/, auto stbl = StringTable(1024 /*hint_max_capacity*/,
false /*!debug_flag*/); false /*!debug_flag*/);
DSimpleRcx rcx(alloc, &stbl); DSimpleRcx rcx(alloc, alloc, &stbl);
REQUIRE((void*)rcx.allocator().data() == (void*)alloc.data()); REQUIRE((void*)rcx.allocator().data() == (void*)alloc.data());
REQUIRE(rcx.stringtable() == &stbl); REQUIRE(rcx.stringtable() == &stbl);
@ -54,7 +54,7 @@ namespace xo {
auto stbl = StringTable(1024 /*hint_max_capacity*/, auto stbl = StringTable(1024 /*hint_max_capacity*/,
false /*!debug_flag*/); false /*!debug_flag*/);
DSimpleRcx rcx(alloc, &stbl); DSimpleRcx rcx(alloc, alloc, &stbl);
obj<ARuntimeContext> rcx_obj = with_facet<ARuntimeContext>::mkobj(&rcx); obj<ARuntimeContext> rcx_obj = with_facet<ARuntimeContext>::mkobj(&rcx);
// verify we can recover allocator from obj<ARuntimeContext> // verify we can recover allocator from obj<ARuntimeContext>