From f7c269a5052a4ab5d27f546914b63efc4166ef0f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 29 Mar 2026 13:44:19 -0400 Subject: [PATCH] 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/ --- idl/RuntimeContext.json5 | 9 +++++++ include/xo/procedure2/DSimpleRcx.hpp | 7 +++-- include/xo/procedure2/GcPrimitives.hpp | 5 ++++ include/xo/procedure2/detail/AProcedure.hpp | 5 ++++ .../xo/procedure2/detail/ARuntimeContext.hpp | 7 +++++ .../procedure2/detail/IRuntimeContext_Any.hpp | 1 + .../detail/IRuntimeContext_DSimpleRcx.hpp | 2 ++ .../detail/IRuntimeContext_Xfer.hpp | 3 +++ .../xo/procedure2/detail/RRuntimeContext.hpp | 3 +++ src/procedure2/GcPrimitives.cpp | 26 ++++++++++++++----- src/procedure2/SetupProcedure2.cpp | 7 +++++ .../facet/IRuntimeContext_DSimpleRcx.cpp | 6 +++++ utest/DSimpleRcx.test.cpp | 4 +-- 13 files changed, 74 insertions(+), 11 deletions(-) diff --git a/idl/RuntimeContext.json5 b/idl/RuntimeContext.json5 index 481d0cb..8736f38 100644 --- a/idl/RuntimeContext.json5 +++ b/idl/RuntimeContext.json5 @@ -61,6 +61,15 @@ noexcept: true, attributes: [], }, + { + name: "error_allocator", + doc: [ "last-resort allocator for erros. e.g. regular allocator exhausted" ], + return_type: "obj", + args: [], + const: true, + noexcept: true, + attributes: [], + }, { name: "stringtable", doc: [ "stringtable for unique symbols" ], diff --git a/include/xo/procedure2/DSimpleRcx.hpp b/include/xo/procedure2/DSimpleRcx.hpp index f6bee0b..1bdf1f7 100644 --- a/include/xo/procedure2/DSimpleRcx.hpp +++ b/include/xo/procedure2/DSimpleRcx.hpp @@ -23,16 +23,19 @@ namespace xo { using MemorySizeVisitor = xo::mm::MemorySizeVisitor; public: - DSimpleRcx(obj mm, StringTable * st) - : allocator_{mm}, stringtable_{st} {} + DSimpleRcx(obj mm, obj error_mm, StringTable * st) + : allocator_{mm}, error_allocator_{error_mm}, + stringtable_{st} {} obj allocator() const noexcept { return allocator_; } obj collector() const noexcept; + obj error_allocator() const noexcept { return error_allocator_; } StringTable * stringtable() const noexcept { return stringtable_; } void visit_pools(const MemorySizeVisitor & visitor) const; private: obj allocator_; + obj error_allocator_; StringTable * stringtable_ = nullptr; }; diff --git a/include/xo/procedure2/GcPrimitives.hpp b/include/xo/procedure2/GcPrimitives.hpp index 65ead09..d0a1525 100644 --- a/include/xo/procedure2/GcPrimitives.hpp +++ b/include/xo/procedure2/GcPrimitives.hpp @@ -5,6 +5,7 @@ #pragma once +#include "Primitive_gco_0.hpp" #include "Primitive_gco_1_gco.hpp" namespace xo { @@ -18,6 +19,10 @@ namespace xo { using AAllocator = xo::mm::AAllocator; public: + /** create primitive: report gc statistics **/ + static DPrimitive_gco_0 * make_report_gc_statistics_pm(obj mm, + StringTable * stbl); + /** create primitive: request collection **/ static DPrimitive_gco_1_gco * make_request_gc_pm(obj mm, StringTable * stbl); diff --git a/include/xo/procedure2/detail/AProcedure.hpp b/include/xo/procedure2/detail/AProcedure.hpp index a5c41b4..2197c5d 100644 --- a/include/xo/procedure2/detail/AProcedure.hpp +++ b/include/xo/procedure2/detail/AProcedure.hpp @@ -47,6 +47,11 @@ public: /** @defgroup scm-procedure-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 will have non-null vtable (via IFacet_Any) with all methods terminating. + **/ + bool _has_null_vptr() const noexcept { return *reinterpret_cast(this) == nullptr; } /** RTTI: unique id# for actual runtime data representation **/ virtual typeseq _typeseq() const noexcept = 0; /** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/ diff --git a/include/xo/procedure2/detail/ARuntimeContext.hpp b/include/xo/procedure2/detail/ARuntimeContext.hpp index 626e2ba..c9da6d6 100644 --- a/include/xo/procedure2/detail/ARuntimeContext.hpp +++ b/include/xo/procedure2/detail/ARuntimeContext.hpp @@ -51,6 +51,11 @@ public: /** @defgroup scm-runtimecontext-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 will have non-null vtable (via IFacet_Any) with all methods terminating. + **/ + bool _has_null_vptr() const noexcept { return *reinterpret_cast(this) == nullptr; } /** RTTI: unique id# for actual runtime data representation **/ virtual typeseq _typeseq() const noexcept = 0; /** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/ @@ -59,6 +64,8 @@ public: virtual obj allocator(Copaque data) const noexcept = 0; /** collector facet for allocator. If non-null, same data pointer as allocator **/ virtual obj collector(Copaque data) const noexcept = 0; + /** last-resort allocator for erros. e.g. regular allocator exhausted **/ + virtual obj error_allocator(Copaque data) const noexcept = 0; /** stringtable for unique symbols **/ virtual StringTable * stringtable(Copaque data) const noexcept = 0; /** invoke visitor for each distinct memory pool **/ diff --git a/include/xo/procedure2/detail/IRuntimeContext_Any.hpp b/include/xo/procedure2/detail/IRuntimeContext_Any.hpp index d0789e5..89a0d7a 100644 --- a/include/xo/procedure2/detail/IRuntimeContext_Any.hpp +++ b/include/xo/procedure2/detail/IRuntimeContext_Any.hpp @@ -63,6 +63,7 @@ namespace scm { // const methods [[noreturn]] obj allocator(Copaque) const noexcept override { _fatal(); } [[noreturn]] obj collector(Copaque) const noexcept override { _fatal(); } + [[noreturn]] obj error_allocator(Copaque) const noexcept override { _fatal(); } [[noreturn]] StringTable * stringtable(Copaque) const noexcept override { _fatal(); } [[noreturn]] void visit_pools(Copaque, MemorySizeVisitor) const override { _fatal(); } diff --git a/include/xo/procedure2/detail/IRuntimeContext_DSimpleRcx.hpp b/include/xo/procedure2/detail/IRuntimeContext_DSimpleRcx.hpp index 496b593..cf2a052 100644 --- a/include/xo/procedure2/detail/IRuntimeContext_DSimpleRcx.hpp +++ b/include/xo/procedure2/detail/IRuntimeContext_DSimpleRcx.hpp @@ -52,6 +52,8 @@ namespace xo { static obj allocator(const DSimpleRcx & self) noexcept; /** collector facet for allocator. If non-null, same data pointer as allocator **/ static obj collector(const DSimpleRcx & self) noexcept; + /** last-resort allocator for erros. e.g. regular allocator exhausted **/ + static obj error_allocator(const DSimpleRcx & self) noexcept; /** stringtable for unique symbols **/ static StringTable * stringtable(const DSimpleRcx & self) noexcept; /** invoke visitor for each distinct memory pool **/ diff --git a/include/xo/procedure2/detail/IRuntimeContext_Xfer.hpp b/include/xo/procedure2/detail/IRuntimeContext_Xfer.hpp index 2c4e80e..d1a8e8c 100644 --- a/include/xo/procedure2/detail/IRuntimeContext_Xfer.hpp +++ b/include/xo/procedure2/detail/IRuntimeContext_Xfer.hpp @@ -54,6 +54,9 @@ namespace scm { obj collector(Copaque data) const noexcept override { return I::collector(_dcast(data)); } + obj error_allocator(Copaque data) const noexcept override { + return I::error_allocator(_dcast(data)); + } StringTable * stringtable(Copaque data) const noexcept override { return I::stringtable(_dcast(data)); } diff --git a/include/xo/procedure2/detail/RRuntimeContext.hpp b/include/xo/procedure2/detail/RRuntimeContext.hpp index b80cfce..9c047fd 100644 --- a/include/xo/procedure2/detail/RRuntimeContext.hpp +++ b/include/xo/procedure2/detail/RRuntimeContext.hpp @@ -61,6 +61,9 @@ public: obj collector() const noexcept { return O::iface()->collector(O::data()); } + obj error_allocator() const noexcept { + return O::iface()->error_allocator(O::data()); + } StringTable * stringtable() const noexcept { return O::iface()->stringtable(O::data()); } diff --git a/src/procedure2/GcPrimitives.cpp b/src/procedure2/GcPrimitives.cpp index 9948ff6..9573f66 100644 --- a/src/procedure2/GcPrimitives.cpp +++ b/src/procedure2/GcPrimitives.cpp @@ -21,23 +21,35 @@ namespace xo { // ----- report-gc-status ----- -#ifdef NOT_YET obj - xfer_report_gc_status(obj rcx) + xfer_report_gc_statistics(obj rcx) { - bool have_gc = false; - if (rcx.collector()) { // status currently only implemented for X1 collector - auto gc = obj::from(rcx.collector()); - + obj stats; + bool ok = rcx.collector().report_statistics(rcx.allocator(), + rcx.error_allocator(), + &stats); + if (ok && stats) + return stats; } return DBoolean::box(rcx.allocator(), false); } -#endif + + DPrimitive_gco_0 * + GcPrimitives::make_report_gc_statistics_pm(obj mm, + StringTable * stbl) + { + (void)stbl; + + auto any_ty = DAtomicType::make(mm, Metatype::t_any()); + auto pm_ty = obj(DFunctionType::_make(mm, any_ty)); + + return DPrimitive_gco_0::_make(mm, "report-gc-statistics", pm_ty, &xfer_report_gc_statistics); + } // ----- request-gc ----- diff --git a/src/procedure2/SetupProcedure2.cpp b/src/procedure2/SetupProcedure2.cpp index 00f2460..6772fff 100644 --- a/src/procedure2/SetupProcedure2.cpp +++ b/src/procedure2/SetupProcedure2.cpp @@ -130,6 +130,13 @@ namespace xo { ObjectPrimitives::make_fn_n_args_pm(mm, stbl), 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 (sink, GcPrimitives::make_request_gc_pm(mm, stbl), diff --git a/src/procedure2/facet/IRuntimeContext_DSimpleRcx.cpp b/src/procedure2/facet/IRuntimeContext_DSimpleRcx.cpp index ba56096..d32f574 100644 --- a/src/procedure2/facet/IRuntimeContext_DSimpleRcx.cpp +++ b/src/procedure2/facet/IRuntimeContext_DSimpleRcx.cpp @@ -27,6 +27,12 @@ namespace xo { return self.collector(); } + auto + IRuntimeContext_DSimpleRcx::error_allocator(const DSimpleRcx & self) noexcept -> obj + { + return self.error_allocator(); + } + auto IRuntimeContext_DSimpleRcx::stringtable(const DSimpleRcx & self) noexcept -> StringTable * { diff --git a/utest/DSimpleRcx.test.cpp b/utest/DSimpleRcx.test.cpp index 365bd3f..0fe56b8 100644 --- a/utest/DSimpleRcx.test.cpp +++ b/utest/DSimpleRcx.test.cpp @@ -39,7 +39,7 @@ namespace xo { auto stbl = StringTable(1024 /*hint_max_capacity*/, false /*!debug_flag*/); - DSimpleRcx rcx(alloc, &stbl); + DSimpleRcx rcx(alloc, alloc, &stbl); REQUIRE((void*)rcx.allocator().data() == (void*)alloc.data()); REQUIRE(rcx.stringtable() == &stbl); @@ -54,7 +54,7 @@ namespace xo { auto stbl = StringTable(1024 /*hint_max_capacity*/, false /*!debug_flag*/); - DSimpleRcx rcx(alloc, &stbl); + DSimpleRcx rcx(alloc, alloc, &stbl); obj rcx_obj = with_facet::mkobj(&rcx); // verify we can recover allocator from obj