xo-gc: + Collector.is_type_installed()

This commit is contained in:
Roland Conybeare 2026-01-02 22:33:54 -05:00
commit 44a096c6f7
9 changed files with 68 additions and 1 deletions

View file

@ -204,6 +204,12 @@ namespace xo {
/** true iff original alloc has been replaced by a forwarding pointer **/
bool is_forwarding_header(header_type hdr) const noexcept;
/** 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;
/** Retreive bookkeeping info for allocation at @p mem. **/
AllocInfo alloc_info(value_type mem) const noexcept;

View file

@ -42,6 +42,8 @@ namespace xo {
generation g, role r) const noexcept = 0;
virtual size_type committed(Copaque d,
generation g, role r) const noexcept = 0;
virtual bool is_type_installed(Copaque d,
typeseq tseq) const noexcept = 0;
/** install interface @p iface for representation with typeseq @p tseq
* in collector @p d.

View file

@ -34,6 +34,7 @@ namespace xo {
[[noreturn]] size_type allocated(Copaque, generation, role) const noexcept override { _fatal(); }
[[noreturn]] size_type reserved(Copaque, generation, role) const noexcept override { _fatal(); }
[[noreturn]] size_type committed(Copaque, generation, role) const noexcept override { _fatal(); }
[[noreturn]] bool is_type_installed(Copaque, typeseq) const noexcept override { _fatal(); }
// non-const methods
[[noreturn]] bool install_type(Opaque, const AGCObject &) noexcept override { _fatal(); }

View file

@ -30,6 +30,7 @@ namespace xo {
struct ICollector_DX1Collector {
using size_type = std::size_t;
using header_type = DArena::header_type;
using typeseq = xo::facet::typeseq;
static bool check_move_policy(const DX1Collector & d,
header_type alloc_hdr,
@ -40,6 +41,7 @@ namespace xo {
static size_type allocated(const DX1Collector & d, generation g, role r);
static size_type reserved(const DX1Collector & d, generation g, role r);
static size_type committed(const DX1Collector & d, generation g, role r);
static bool is_type_installed(const DX1Collector & d, typeseq tseq);
static bool install_type(DX1Collector & d, const AGCObject & iface);
static void add_gc_root(DX1Collector & d, int32_t tseq, Opaque * root);

View file

@ -41,6 +41,9 @@ namespace xo {
size_type committed(Copaque d, generation g, role r) const noexcept override {
return I::committed(_dcast(d), g, r);
}
bool is_type_installed(Copaque d, typeseq tseq) const noexcept override {
return I::is_type_installed(_dcast(d), tseq);
}
// non-const methods

View file

@ -17,6 +17,7 @@ namespace xo {
using ObjectType = Object;
using DataPtr = Object::DataPtr;
using size_type = std::size_t;
using typeseq = ACollector::typeseq;
//using value_type = std::byte *;
RCollector() = default;
@ -26,6 +27,7 @@ namespace xo {
size_type allocated(generation g, role r) const noexcept { return O::iface()->allocated(O::data(), g, r); }
size_type reserved(generation g, role r) const noexcept { return O::iface()->reserved(O::data(), g, r); }
size_type committed(generation g, role r) const noexcept { return O::iface()->committed(O::data(), g, r); }
bool is_type_installed(typeseq tseq) const noexcept { return O::iface()->is_type_installed(O::data(), tseq); }
bool install_type(const AGCObject & iface) { return O::iface()->install_type(O::data(), iface); }
void add_gc_root(int32_t tseq, Opaque * root) { O::iface()->add_gc_root(O::data(), tseq, root); }

View file

@ -198,6 +198,19 @@ namespace xo {
return config_.arena_config_.header_.is_forwarding_tseq(hdr);
}
bool
DX1Collector::is_type_installed(typeseq tseq) const noexcept
{
if (object_types_.committed() < sizeof(AGCObject) * (tseq.seqno() + 1))
return false;
AGCObject * v = reinterpret_cast<AGCObject *>(object_types_.lo_);
void * vtable = *(void **)&(v[tseq.seqno()]);
return (vtable != nullptr);
}
bool
DX1Collector::install_type(const AGCObject & meta) noexcept
{

View file

@ -47,6 +47,12 @@ namespace xo {
return stat_helper(d, &DArena::committed, g, r);
}
bool
ICollector_DX1Collector::is_type_installed(const DX1Collector & d, typeseq tseq)
{
return d.is_type_installed(tseq);
}
bool
ICollector_DX1Collector::install_type(DX1Collector & d,
const AGCObject & iface)

View file

@ -5,22 +5,29 @@
#include "DFloat.hpp"
#include "DList.hpp"
#include "object2_register_types.hpp"
#include "IGCObject_DFloat.hpp"
#include "IGCObject_DList.hpp"
#include <xo/gc/Collector.hpp>
#include <xo/gc/DX1Collector.hpp>
#include <xo/gc/detail/IAllocator_DX1Collector.hpp>
#include <xo/gc/detail/ICollector_DX1Collector.hpp>
#include <xo/alloc2/AllocInfo.hpp>
#include <xo/alloc2/padding.hpp>
#include <catch2/catch.hpp>
namespace ut {
using xo::scm::object2_register_types;
using xo::scm::DList;
using xo::scm::DFloat;
using xo::mm::AAllocator;
using xo::mm::ACollector;
using xo::mm::AllocHeader;
using xo::mm::AllocInfo;
using xo::mm::AGCObject;
using xo::mm::DX1Collector;
@ -79,6 +86,8 @@ namespace ut {
DX1Collector gc(cfg);
DArena * to_0 = nullptr;
/* verify initial collector state */
{
REQUIRE(gc.name() == "x1_test");
@ -95,21 +104,25 @@ namespace ut {
REQUIRE(from_0->reserved() >= tc.tenured_z_);
REQUIRE(from_0->reserved() < tc.tenured_z_ + from_0->page_z_);
REQUIRE(from_0->reserved() % from_0->page_z_ == 0);
REQUIRE(from_0->allocated() == 0);
DArena * from_1 = gc.get_space(role::from_space(), generation{1});
REQUIRE(from_1 != nullptr);
REQUIRE(from_1->reserved() == from_0->reserved());
REQUIRE(from_1->allocated() == 0);
DArena * to_0 = gc.get_space(role::to_space(), generation{0});
to_0 = gc.get_space(role::to_space(), generation{0});
REQUIRE(to_0 != nullptr);
REQUIRE(to_0->reserved() == from_0->reserved());
REQUIRE(to_0->allocated() == 0);
DArena * to_1 = gc.get_space(role::to_space(), generation{1});
REQUIRE(to_1 != nullptr);
REQUIRE(to_1->reserved() == to_0->reserved());
REQUIRE(to_1->allocated() == 0);
DArena * from_2 = gc.get_space(role::from_space(), generation{2});
@ -125,13 +138,30 @@ namespace ut {
/* attempt allocation */
auto gc_o = with_facet<AAllocator>::mkobj(&gc);
auto c_o = with_facet<ACollector>::mkobj(&gc);
/* register object types */
bool ok = object2_register_types(c_o);
REQUIRE(ok);
ok = c_o.is_type_installed(typeseq::id<DFloat>());
REQUIRE(ok);
ok = c_o.is_type_installed(typeseq::id<DList>());
REQUIRE(ok);
DFloat * x0 = DFloat::make(gc_o, 3.1415927);
auto x0_o = with_facet<AGCObject>::mkobj(x0);
REQUIRE(to_0->allocated() == sizeof(AllocHeader) + sizeof(DFloat));
DList * l0 = DList::list(gc_o, x0_o);
auto l0_o = with_facet<AGCObject>::mkobj(l0);
REQUIRE(to_0->allocated() == (sizeof(AllocHeader) + sizeof(DFloat)
+ sizeof(AllocHeader) + sizeof(DList)));
{
{
REQUIRE(x0_o.iface() != nullptr);
@ -152,6 +182,7 @@ namespace ut {
REQUIRE(l0_o.data() != nullptr);
REQUIRE(gc.contains(role::to_space(), l0_o.data()));
/* check alloc info for newly-allocated object */
AllocInfo info = gc.alloc_info((std::byte *)l0_o.data());
REQUIRE(info.age() == 0);
@ -162,6 +193,7 @@ namespace ut {
}
}
/* no GC roots, so GC is trivial */
} catch (std::exception & ex) {
std::cerr << "caught exception: " << ex.what() << std::endl;
REQUIRE(false);