xo-object2: type registration + gc fixes

This commit is contained in:
Roland Conybeare 2026-01-02 09:53:23 -05:00
commit 99108b8dbb
16 changed files with 285 additions and 17 deletions

View file

@ -41,7 +41,7 @@ xo_add_genfacetimpl(
# must complete definition of expression lib before configuring examples
add_subdirectory(src/object2)
#add_subdirectory(utest)
add_subdirectory(utest)
#xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
# ----------------------------------------------------------------

View file

@ -13,12 +13,24 @@ namespace xo {
struct DList {
using size_type = std::size_t;
using AGCObject = xo::mm::AGCObject;
using AAllocator = xo::mm::AAllocator;
DList(xo::obj<AGCObject> h,
DList * r) : head_{h}, rest_{r} {}
/** sentinel for null list **/
static DList * null();
/** list with one element @p h1, allocated from @p mm **/
static DList * list(obj<AAllocator> mm,
obj<AGCObject> h1);
/** list with two elements @p h1, @p h2, allocated from @p mm **/
static DList * list(obj<AAllocator> mm,
obj<AGCObject> h1,
obj<AGCObject> h2);
/** DList length is at least 1 **/
bool is_empty() const noexcept { return false; };
bool is_empty() const noexcept;
/** DList models a finite sequence **/
bool is_finite() const noexcept { return true; };
/** return number of elements in this DList **/
@ -26,7 +38,9 @@ namespace xo {
/** return element at 0-based index @p ix **/
obj<AGCObject> at(size_type ix) const;
/** first member of list **/
obj<AGCObject> head_;
/** remainder of list **/
DList * rest_ = nullptr;
};

View file

@ -5,13 +5,26 @@
#pragma once
#include <xo/alloc2/Allocator.hpp>
#include <xo/gc/Collector.hpp>
#include <xo/alloc2/alloc/AAllocator.hpp>
#include <xo/gc/detail/AGCObject.hpp>
#include <xo/gc/detail/IGCObject_Xfer.hpp>
#include "DFloat.hpp"
namespace xo {
namespace scm { struct IGCObject_DFloat; }
namespace facet {
template <>
struct FacetImplementation<xo::mm::AGCObject,
xo::scm::DFloat>
{
using ImplType = xo::mm::IGCObject_Xfer
<xo::scm::DFloat,
xo::scm::IGCObject_DFloat>;
};
}
namespace scm {
/* changes here coordinate with:
* IGCObject_Xfer
@ -19,11 +32,12 @@ namespace xo {
struct IGCObject_DFloat {
public:
using AAllocator = xo::mm::AAllocator;
using ACollector = xo::mm::ACollector;
using size_type = std::size_t;
static size_type shallow_size(const DFloat & d) noexcept;
static DFloat * shallow_copy(const DFloat & d, obj<AAllocator> mm) noexcept;
static size_type forward_children(DFloat & d) noexcept;
static size_type forward_children(DFloat & d, obj<ACollector> gc) noexcept;
};
} /*namespace scm*/
} /*namespace xo*/

View file

@ -12,6 +12,19 @@
#include "DInteger.hpp"
namespace xo {
namespace scm { struct IGCObject_DInteger; }
namespace facet {
template <>
struct FacetImplementation<xo::mm::AGCObject,
xo::scm::DInteger>
{
using ImplType = xo::mm::IGCObject_Xfer
<xo::scm::DInteger,
xo::scm::IGCObject_DInteger>;
};
}
namespace scm {
/* changes here coordinate with:
* IGCObject_Xfer
@ -19,11 +32,12 @@ namespace xo {
struct IGCObject_DInteger {
public:
using AAllocator = xo::mm::AAllocator;
using ACollector = xo::mm::ACollector;
using size_type = std::size_t;
static size_type shallow_size(const DInteger & d) noexcept;
static DInteger * shallow_copy(const DInteger & d, obj<AAllocator> mm) noexcept;
static size_type forward_children(DInteger & d) noexcept;
static size_type forward_children(DInteger & d, obj<ACollector> gc) noexcept;
};
} /*namespace scm*/
} /*namespace xo*/

View file

@ -5,8 +5,7 @@
#pragma once
#include <xo/alloc2/alloc/AAllocator.hpp>
#include <xo/alloc2/alloc/RAllocator.hpp>
#include <xo/alloc2/Allocator.hpp>
#include <xo/gc/Collector.hpp>
#include <xo/gc/detail/AGCObject.hpp>
#include <xo/gc/detail/IGCObject_Xfer.hpp>

View file

@ -0,0 +1,19 @@
/** @file object2_register_types.hpp
*
* @author Roland Conybeare, Dec 2025
**/
#pragma once
#include <xo/gc/Collector.hpp>
namespace xo {
namespace scm {
/** Register all object2/ gc-aware types with @p gc.
* Return true iff all types register successfully.
**/
bool object2_register_types(obj<xo::mm::ACollector> gc);
}
}
/* end object2_register_types.hpp */

View file

@ -14,6 +14,7 @@
#pragma once
#include "ASequence.hpp"
#include <xo/facet/typeseq.hpp>
#include <xo/facet/obj.hpp>
namespace xo { namespace scm { class ISequence_Any; } }
@ -42,6 +43,7 @@ namespace scm {
/** @defgroup scm-sequence-any-type-traits **/
///@{
using typeseq = xo::facet::typeseq;
using size_type = ASequence::size_type;
using AGCObject = ASequence::AGCObject;
@ -54,7 +56,7 @@ namespace scm {
// from ASequence
// const methods
int32_t _typeseq() const noexcept override { return s_typeseq; }
typeseq _typeseq() const noexcept override { return s_typeseq; }
[[noreturn]] bool is_empty(Copaque) const noexcept override { _fatal(); }
[[noreturn]] bool is_finite(Copaque) const noexcept override { _fatal(); }
[[noreturn]] obj<AGCObject> at(Copaque, size_type) const override { _fatal(); }
@ -75,7 +77,7 @@ namespace scm {
/** @defgraoup scm-sequence-any-member-vars **/
///@{
static int32_t s_typeseq;
static typeseq s_typeseq;
static bool _valid;
///@}
@ -84,4 +86,4 @@ namespace scm {
} /*namespace scm */
} /*namespace xo */
/* ISequence_Any.hpp */
/* ISequence_Any.hpp */

View file

@ -8,6 +8,7 @@ set(SELF_SRCS
ISequence_Any.cpp
ISequence_DList.cpp
DList.cpp
object2_register_types.cpp
)
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})

View file

@ -7,7 +7,42 @@
#include <xo/indentlog/print/tag.hpp>
namespace xo {
using xo::mm::AGCObject;
namespace scm {
static DList s_null(obj<AGCObject>(), nullptr);
DList *
DList::null()
{
return &s_null;
}
DList *
DList::list(obj<AAllocator> mm,
obj<AGCObject> h1)
{
void * mem = mm.alloc(sizeof(DList));
return new (mem) DList(h1, DList::null());
}
DList *
DList::list(obj<AAllocator> mm,
obj<AGCObject> h1,
obj<AGCObject> h2)
{
void * mem = mm.alloc(sizeof(DList));
return new (mem) DList(h1, DList::list(mm, h2));
}
bool
DList::is_empty() const noexcept
{
return this != &s_null;
}
auto
DList::size() const noexcept -> size_type
{
@ -15,7 +50,7 @@ namespace xo {
size_type z = 0;
while (l) {
while (l && l != &s_null) {
++z;
l = l->rest_;
}

View file

@ -33,9 +33,10 @@ namespace xo {
}
size_t
IGCObject_DFloat::forward_children(DFloat &) noexcept
IGCObject_DFloat::forward_children(DFloat & src,
obj<ACollector>) noexcept
{
return sizeof(DFloat);
return shallow_size(src);
}
} /*namespace scm*/

View file

@ -33,9 +33,10 @@ namespace xo {
}
size_t
IGCObject_DInteger::forward_children(DInteger &) noexcept
IGCObject_DInteger::forward_children(DInteger & src,
obj<ACollector>) noexcept
{
return sizeof(DInteger);
return shallow_size(src);
}
} /*namespace scm*/

View file

@ -37,8 +37,7 @@ namespace xo {
{
gc.forward_inplace(src.head_.iface(), (void **)&(src.head_.data_));
//auto rest = with_facet<AGCObject>::mkobj(src.rest_);
xo::facet::FacetImplementation<xo::mm::AGCObject, DList>::ImplType iface;
auto iface = xo::facet::impl_for<AGCObject, DList>();
gc.forward_inplace(&iface, (void **)(&src.rest_));
return shallow_size(src);

View file

@ -0,0 +1,34 @@
/** @file object2_register_types.cpp
*
* @author Roland Conybeare, Dec 2025
**/
#include "object2_register_types.hpp"
#include "IGCObject_DList.hpp"
#include "IGCObject_DFloat.hpp"
#include "IGCObject_DInteger.hpp"
namespace xo {
using xo::mm::ACollector;
using xo::mm::AGCObject;
using xo::mm::IGCObject_Any;
using xo::facet::impl_for;
using xo::facet::typeseq;
namespace scm {
bool
object2_register_types(obj<ACollector> gc)
{
bool ok = true;
ok &= gc.install_type(impl_for<AGCObject, DList>());
ok &= gc.install_type(impl_for<AGCObject, DFloat>());
return ok;
}
}
} /*namespace xo*/
/* end object2_register_types.cpp */

12
utest/CMakeLists.txt Normal file
View file

@ -0,0 +1,12 @@
# built unittest xo-object2/utest
set(UTEST_EXE utest.object2)
set(UTEST_SRCS
object2_utest_main.cpp
X1Collector.test.cpp
)
xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS})
xo_self_dependency(${UTEST_EXE} xo_object2)
#xo_dependency(${UTEST_EXE} randomgen)
xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2)

117
utest/X1Collector.test.cpp Normal file
View file

@ -0,0 +1,117 @@
/** @file X1Collector.test.cpp
*
* @author Roland Conybeare, Dec 2025
**/
#include <xo/gc/Collector.hpp>
#include <xo/gc/DX1Collector.hpp>
#include <catch2/catch.hpp>
namespace ut {
using xo::mm::DX1Collector;
using xo::mm::DArena;
using xo::mm::CollectorConfig;
using xo::mm::ArenaConfig;
using xo::mm::generation;
using xo::mm::role;
namespace {
struct testcase_x1 {
testcase_x1(std::size_t nz,
std::size_t tz,
std::size_t n_gct,
std::size_t t_gct)
: nursery_z_{nz},
tenured_z_{tz},
incr_gc_threshold_{n_gct},
full_gc_threshold_{t_gct} {}
std::size_t nursery_z_;
std::size_t tenured_z_;
std::size_t incr_gc_threshold_;
std::size_t full_gc_threshold_;
};
std::vector<testcase_x1>
s_testcase_v = {
// n_gct: nursery gc threshold
// t_gct: tenured gc threshold
//
// nz tz n_gct t_gct
testcase_x1(4096, 8192, 1024, 1024)
};
}
TEST_CASE("x1", "[gc][x1]")
{
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
try {
const testcase_x1 & tc = s_testcase_v[i_tc];
CollectorConfig cfg{
.name_ = "x1_test",
.arena_config_ = ArenaConfig{
.size_ = tc.tenured_z_,
.store_header_flag_ = true},
.object_types_z_ = 16384,
.gc_trigger_v_{{
tc.incr_gc_threshold_,
tc.full_gc_threshold_}},
};
DX1Collector gc(cfg);
{
REQUIRE(gc.name() == "x1_test");
const DArena * otypes = gc.get_object_types();
REQUIRE(otypes != nullptr);
REQUIRE(otypes->reserved() >= cfg.object_types_z_);
REQUIRE(otypes->reserved() < cfg.object_types_z_ + otypes->page_z_);
DArena * from_0 = gc.get_space(role::from_space(), generation{0});
REQUIRE(from_0 != nullptr);
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);
DArena * from_1 = gc.get_space(role::from_space(), generation{1});
REQUIRE(from_1 != nullptr);
REQUIRE(from_1->reserved() == from_0->reserved());
DArena * to_0 = gc.get_space(role::to_space(), generation{0});
REQUIRE(to_0 != nullptr);
REQUIRE(to_0->reserved() == from_0->reserved());
DArena * to_1 = gc.get_space(role::to_space(), generation{1});
REQUIRE(to_1 != nullptr);
REQUIRE(to_1->reserved() == to_0->reserved());
DArena * from_2 = gc.get_space(role::from_space(), generation{2});
REQUIRE(from_2 == nullptr);
DArena * to_2 = gc.get_space(role::to_space(), generation{2});
REQUIRE(to_2 == nullptr);
REQUIRE(gc.reserved_total()
== otypes->reserved() + 4 * from_0->reserved());
}
/* attempt allocation */
} catch (std::exception & ex) {
std::cerr << "caught exception: " << ex.what() << std::endl;
REQUIRE(false);
}
}
}
}
/* end X1Collector.test.cpp */

View file

@ -0,0 +1,6 @@
/* file object2_utest_main.cpp */
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
/* end object2_utest_main.cpp */