xo-gc stack: fix mutation setup + xo-reader2 utest
This commit is contained in:
parent
1a88975567
commit
50d87d3371
9 changed files with 224 additions and 23 deletions
|
|
@ -57,6 +57,24 @@ namespace xo {
|
|||
Generation gc_upto_;
|
||||
};
|
||||
|
||||
/** @class GcStatistics
|
||||
**/
|
||||
struct GCStatistics {
|
||||
public:
|
||||
GCStatistics() = default;
|
||||
explicit GCStatistics(uint32_t n_gc) : n_gc_{n_gc} {};
|
||||
|
||||
uint32_t n_gc() const noexcept { return n_gc_; }
|
||||
|
||||
void include_gc() {
|
||||
++n_gc_;
|
||||
}
|
||||
|
||||
private:
|
||||
/** count #gc **/
|
||||
uint32_t n_gc_ = 0;
|
||||
};
|
||||
|
||||
struct DX1CollectorIterator;
|
||||
|
||||
/** @brief GC root struct
|
||||
|
|
@ -116,6 +134,8 @@ namespace xo {
|
|||
|
||||
std::string_view name() const noexcept { return config_.name_; }
|
||||
GCRunState runstate() const noexcept { return runstate_; }
|
||||
const GCStatistics & gc_stats() const noexcept { return gc_stats_; }
|
||||
|
||||
const ObjectTypeTable * get_object_types() const noexcept { return gco_store_.get_object_types(); }
|
||||
const RootSet * get_root_set() const noexcept { return &root_set_; }
|
||||
const DArena * get_space(Role r, Generation g) const noexcept { return gco_store_.get_space(r, g); }
|
||||
|
|
@ -405,6 +425,9 @@ namespace xo {
|
|||
**/
|
||||
MutationLogStore mlog_store_;
|
||||
|
||||
/** counters collected across GC phases **/
|
||||
GCStatistics gc_stats_;
|
||||
|
||||
/** counters collected during @ref verify_ok call **/
|
||||
X1VerifyStats verify_stats_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -528,6 +528,9 @@ namespace xo {
|
|||
log && log("step 5 : cleanup");
|
||||
this->_cleanup_phase(upto);
|
||||
|
||||
log && log("step 6 : update gc statistics");
|
||||
gc_stats_.include_gc();
|
||||
|
||||
if (config_.sanitize_flag_) {
|
||||
log && log("step 5b : verify");
|
||||
bool ok = this->verify_ok();
|
||||
|
|
@ -550,7 +553,7 @@ namespace xo {
|
|||
void
|
||||
DX1Collector::_swap_roles(Generation upto) noexcept
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("upto", upto));
|
||||
scope log(XO_DEBUG(config_.debug_flag_), xtag("upto", upto));
|
||||
|
||||
gco_store_.swap_roles(upto);
|
||||
mlog_store_.swap_roles(upto);
|
||||
|
|
@ -559,7 +562,7 @@ namespace xo {
|
|||
void
|
||||
DX1Collector::_cleanup_phase(Generation upto)
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("upto", upto));
|
||||
scope log(XO_DEBUG(config_.debug_flag_), xtag("upto", upto));
|
||||
|
||||
this->gco_store_.cleanup_phase(upto, config_.sanitize_flag_);
|
||||
this->runstate_ = GCRunState::idle();
|
||||
|
|
@ -568,7 +571,7 @@ namespace xo {
|
|||
void
|
||||
DX1Collector::_copy_roots(Generation upto) noexcept
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
scope log(XO_DEBUG(config_.debug_flag_));
|
||||
|
||||
for (RootSet::size_type i = 0, n = root_set_.size(); i < n; ++i) {
|
||||
GCRoot & slot = root_set_[i];
|
||||
|
|
|
|||
|
|
@ -705,7 +705,7 @@ namespace xo {
|
|||
void
|
||||
GCObjectStore::_verify_aux(AGCObject * iface, void * data)
|
||||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_));
|
||||
scope log(XO_DEBUG(false));
|
||||
|
||||
(void)iface;
|
||||
|
||||
|
|
@ -719,6 +719,7 @@ namespace xo {
|
|||
if (!g2.is_sentinel()) {
|
||||
// verify failure - live pointer still refers to from-space
|
||||
|
||||
log.retroactively_enable();
|
||||
print_backtrace_dwarf(true /*demangle*/);
|
||||
|
||||
++(p_verify_stats_->n_from_);
|
||||
|
|
@ -1020,13 +1021,17 @@ namespace xo {
|
|||
AGCObject * iface,
|
||||
void * from_src)
|
||||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_));
|
||||
scope log(XO_DEBUG(config_.debug_flag_),
|
||||
xtag("iface", iface),
|
||||
xtag("from_src", from_src));
|
||||
|
||||
assert(!iface->_has_null_vptr());
|
||||
|
||||
AllocInfo info = this->alloc_info((std::byte *)from_src);
|
||||
|
||||
void * to_dest = iface->gco_shallow_move(from_src, gc);
|
||||
|
||||
log && log(xtag("from_src", from_src), xtag("to_dest", to_dest));
|
||||
log && log(xtag("to_dest", to_dest));
|
||||
log && log(xtag("tseq", info.tseq()),
|
||||
xtag("tname", TypeRegistry::id2name(typeseq(info.tseq()))),
|
||||
xtag("age", info.age()),
|
||||
|
|
|
|||
|
|
@ -160,8 +160,14 @@ namespace xo {
|
|||
assert(rhs_iface);
|
||||
assert(rhs_data);
|
||||
|
||||
if (lhs_iface)
|
||||
*lhs_iface = *rhs_iface;
|
||||
if (lhs_iface) {
|
||||
// memcpy (not assignment): lhs_iface points to AGCObject storage
|
||||
// whose vptr was set at construction (e.g. IGCObject_Any from
|
||||
// a default-constructed obj<AGCObject>). Polymorphic copy-assignment
|
||||
// copies AGCObject's data members but NOT the vptr, so it would
|
||||
// leave the slot dispatching to the wrong (often fatal) iface.
|
||||
::memcpy((void *)lhs_iface, (void *)rhs_iface, sizeof(AGCObject));
|
||||
}
|
||||
|
||||
*lhs_addr = rhs_data;
|
||||
|
||||
|
|
@ -195,6 +201,13 @@ namespace xo {
|
|||
return;
|
||||
}
|
||||
|
||||
if (dest_g + 1 == config_.n_generation_) {
|
||||
log && log(xtag("msg", "noop because dest in last gen"));
|
||||
|
||||
// don't need mlog entry to final gen
|
||||
return;
|
||||
}
|
||||
|
||||
if (src_g < dest_g) {
|
||||
log && log(xtag("msg", "noop because src gen younger than dest gen"));
|
||||
|
||||
|
|
@ -259,7 +272,7 @@ namespace xo {
|
|||
void
|
||||
MutationLogStore::swap_roles(Generation upto) noexcept
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("upto", upto));
|
||||
scope log(XO_DEBUG(config_.debug_flag_), xtag("upto", upto));
|
||||
|
||||
for (Generation g = Generation{0}; g < upto; ++g) {
|
||||
log && log("swap roles", xtag("g", g));
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ set(UTEST_SRCS
|
|||
DX1CollectorIterator.test.cpp
|
||||
MutationLogStore.test.cpp
|
||||
GCObjectStore.test.cpp
|
||||
GCObjectConversion.test.cpp
|
||||
Object2.test.cpp
|
||||
|
||||
DMockCollector.cpp
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <xo/object2/Array.hpp>
|
||||
#include <xo/object2/List.hpp>
|
||||
#include <xo/object2/Integer.hpp>
|
||||
#include <xo/alloc2/CollectorTypeRegistry.hpp>
|
||||
#include <xo/alloc2/Allocator.hpp>
|
||||
#include <xo/randomgen/xoshiro256.hpp>
|
||||
#include <xo/randomgen/random_seed.hpp>
|
||||
|
|
@ -25,6 +26,7 @@ namespace xo {
|
|||
using xo::scm::DList;
|
||||
using xo::scm::DArray;
|
||||
using xo::scm::DInteger;
|
||||
using xo::mm::CollectorTypeRegistry;
|
||||
using xo::mm::AAllocator;
|
||||
using xo::mm::ACollector;
|
||||
using xo::mm::AGCObject;
|
||||
|
|
@ -33,9 +35,11 @@ namespace xo {
|
|||
using xo::mm::Role;
|
||||
using xo::mm::ArenaConfig;
|
||||
using xo::mm::AllocHeaderConfig;
|
||||
using xo::mm::AllocHeader;
|
||||
using xo::mm::Generation;
|
||||
using xo::mm::c_max_generation;
|
||||
using xo::facet::with_facet;
|
||||
using xo::reflect::typeseq;
|
||||
using xo::scope;
|
||||
|
||||
namespace ut {
|
||||
|
|
@ -321,7 +325,11 @@ namespace xo {
|
|||
.with_n_survive(tc.n_survive_)
|
||||
.with_size(tc.gc_halfspace_z_)
|
||||
.with_debug_flag(tc.debug_flag_))
|
||||
{}
|
||||
{
|
||||
auto gc = obj<ACollector,DX1Collector>(&gc_);
|
||||
|
||||
CollectorTypeRegistry::instance().install_types(gc);
|
||||
}
|
||||
|
||||
# define nil nullptr
|
||||
# define T true
|
||||
|
|
@ -332,11 +340,11 @@ namespace xo {
|
|||
* debug_flag
|
||||
* object_type_z |
|
||||
* gc_halfspace_z | |
|
||||
* n_survive | | |
|
||||
* n_gen | | | |
|
||||
* v v v v v
|
||||
* n_survive | | |
|
||||
* n_gen | | | |
|
||||
* v v v v v
|
||||
**/
|
||||
Testcase(1, 2, 16 * 1024, 128, F),
|
||||
Testcase(1, 2, 16 * 1024, 128, T),
|
||||
};
|
||||
|
||||
# undef T
|
||||
|
|
@ -345,10 +353,23 @@ namespace xo {
|
|||
} /*namespace*/
|
||||
|
||||
// full collector test.
|
||||
//
|
||||
// PLAN:
|
||||
// eventually: make generative
|
||||
//
|
||||
// Setup (
|
||||
// 1. gc_utest_main.cpp Subsystem::initialize_all()
|
||||
// invokes per-module plugin init. Gets types registered
|
||||
// with FacetRegistry, CollectorTypeRegistry etc.
|
||||
// 2. per-utest collector setup (fixture)
|
||||
// calls CollectorTypeRegistry::instance().install_types(gc)
|
||||
// to establish the set of types that collector knows.
|
||||
//
|
||||
TEST_CASE("collector-x1-gc", "[alloc2][gc]")
|
||||
{
|
||||
scope log(XO_DEBUG(true),
|
||||
"DX1Collector gc test");
|
||||
const auto & testname = Catch::getResultCapture().getCurrentTestName();
|
||||
|
||||
scope log(XO_DEBUG(true), xtag("test", testname));
|
||||
|
||||
//std::uint64_t seed = 7988747704879432247ul;
|
||||
//random_seed(&seed);
|
||||
|
|
@ -372,7 +393,17 @@ namespace xo {
|
|||
|
||||
auto mm = x1.ref<AAllocator>();
|
||||
auto gc = mm.to_facet<ACollector>();
|
||||
Generation g1{1};
|
||||
|
||||
REQUIRE(mm._typeseq() == typeseq::id<DX1Collector>());
|
||||
REQUIRE(gc._typeseq() == typeseq::id<DX1Collector>());
|
||||
|
||||
Generation g0 = Generation::g0();
|
||||
|
||||
REQUIRE(mm.allocated() == tc.object_type_z_);
|
||||
REQUIRE(gc.allocated(g0, Role::to_space()) == 0);
|
||||
REQUIRE(gc.allocated(g0, Role::from_space()) == 0);
|
||||
|
||||
Generation g1 = Generation::g1();
|
||||
{
|
||||
auto roots = DArray::_empty(mm, 1)->ref<AGCObject>();
|
||||
REQUIRE(mm->contains_allocated(Role::to_space(), roots.data()));
|
||||
|
|
@ -383,12 +414,32 @@ namespace xo {
|
|||
auto x1_gco = obj<AGCObject>(x1);
|
||||
auto l1 = DList::cons(mm, x1, DList::_nil());
|
||||
|
||||
#ifdef NOT_YET
|
||||
REQUIRE(roots->push_back(l1));
|
||||
REQUIRE(l1._typeseq() == typeseq::id<DList>());
|
||||
REQUIRE(roots->push_back(mm, l1));
|
||||
REQUIRE(mm->contains_allocated(Role::to_space(), x1.data()));
|
||||
REQUIRE(mm->contains_allocated(Role::to_space(), l1.data()));
|
||||
|
||||
gc->add_gc_root_poly(&(*roots.operator->())[0]);
|
||||
REQUIRE(roots->at(0) == l1);
|
||||
REQUIRE(roots->at(0)._typeseq() == typeseq::id<DList>());
|
||||
|
||||
// z: total allocated so far
|
||||
// 3x 8-byte header
|
||||
// sizeof(DInteger)
|
||||
// sizeof(DList)
|
||||
// sizeof(DArray(1))
|
||||
//
|
||||
auto z = (3 * sizeof(AllocHeader)
|
||||
+ sizeof(DInteger)
|
||||
+ sizeof(DList)
|
||||
+ sizeof(DArray) + sizeof(obj<AGCObject>));
|
||||
{
|
||||
REQUIRE(z == 80);
|
||||
REQUIRE(mm.allocated() == tc.object_type_z_ + z);
|
||||
REQUIRE(gc.allocated(g0, Role::to_space()) == z);
|
||||
REQUIRE(gc.allocated(g1, Role::to_space()) == 0);
|
||||
REQUIRE(gc.allocated(g0, Role::from_space()) == 0);
|
||||
REQUIRE(gc.allocated(g1, Role::from_space()) == 0);
|
||||
}
|
||||
|
||||
gc->request_gc(g1); // 1st GC
|
||||
|
||||
|
|
@ -398,9 +449,20 @@ namespace xo {
|
|||
// l1 target got moved, og locn now relabeled from-space
|
||||
REQUIRE(mm->contains(Role::from_space(), l1.data()));
|
||||
REQUIRE(!mm->contains_allocated(Role::from_space(), l1.data()));
|
||||
#endif
|
||||
|
||||
REQUIRE(mm.allocated() == tc.object_type_z_ + z);
|
||||
REQUIRE(gc.allocated(g0, Role::to_space()) == z);
|
||||
REQUIRE(gc.allocated(g1, Role::to_space()) == 0);
|
||||
REQUIRE(gc.allocated(g0, Role::from_space()) == 0);
|
||||
REQUIRE(gc.allocated(g1, Role::from_space()) == 0);
|
||||
}
|
||||
|
||||
// NOTE: if this fails:
|
||||
// look for preceding GCObjectStore::lookup_type out-of-bounds.
|
||||
// May need to add to CollectorTypeRegistry
|
||||
//
|
||||
REQUIRE(mm->contains_allocated(Role::to_space(), roots.data()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
95
utest/GCObjectConversion.test.cpp
Normal file
95
utest/GCObjectConversion.test.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/** @file GCObjectConversion.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, May 2026
|
||||
**/
|
||||
|
||||
#include "GCObjectConversion.hpp"
|
||||
#include <xo/object2/ListOps.hpp>
|
||||
#include <xo/object2/List.hpp>
|
||||
#include <xo/object2/Array.hpp>
|
||||
#include <xo/object2/Integer.hpp>
|
||||
#include <xo/alloc2/Arena.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
//using xo::scm::ASequence;
|
||||
using xo::scm::ListOps;
|
||||
using xo::scm::DArray;
|
||||
using xo::scm::DList;
|
||||
using xo::scm::DInteger;
|
||||
using xo::scm::GCObjectConversion;
|
||||
using xo::mm::AGCObject;
|
||||
using xo::mm::ArenaConfig;
|
||||
using xo::mm::AAllocator;
|
||||
using xo::mm::DArena;
|
||||
using xo::facet::obj;
|
||||
|
||||
namespace ut {
|
||||
|
||||
TEST_CASE("GCObjectConversion-1", "[GCObjectConversion]")
|
||||
{
|
||||
scope log(XO_DEBUG(true), "GCObjectConversion-1");
|
||||
|
||||
ArenaConfig cfg {
|
||||
.name_ = "testarena",
|
||||
.size_ = 128
|
||||
};
|
||||
DArena arena = DArena::map(cfg);
|
||||
auto mm = obj<AAllocator,DArena>(&arena);
|
||||
auto v1 = DArray::empty(mm, 3);
|
||||
|
||||
REQUIRE(v1);
|
||||
REQUIRE(v1->size() == 0);
|
||||
|
||||
{
|
||||
obj v1_seq
|
||||
= GCObjectConversion<obj<AGCObject,DArray>>::from_gco(mm /*not used*/, v1);
|
||||
|
||||
REQUIRE(v1_seq);
|
||||
REQUIRE(v1_seq == v1);
|
||||
REQUIRE(v1_seq->size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
obj l1_seq
|
||||
= GCObjectConversion<obj<AGCObject,DList>>::from_gco(mm /*not used*/, v1);
|
||||
|
||||
REQUIRE(!l1_seq);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("GCObjectConversion-2", "[GCObjectConversion]")
|
||||
{
|
||||
scope log(XO_DEBUG(true), "GCObjectConversion-2");
|
||||
|
||||
ArenaConfig cfg {
|
||||
.name_ = "testarena",
|
||||
.size_ = 128
|
||||
};
|
||||
DArena arena = DArena::map(cfg);
|
||||
auto mm = obj<AAllocator,DArena>(&arena);
|
||||
auto l1 = ListOps::cons(mm, DInteger::box(mm, 42), ListOps::nil());
|
||||
|
||||
REQUIRE(l1);
|
||||
REQUIRE(l1->size() == 1);
|
||||
|
||||
{
|
||||
// will fail; source is DArena
|
||||
obj l1_seq
|
||||
= GCObjectConversion<obj<AGCObject,DList>>::from_gco(mm /*not used*/, l1);
|
||||
|
||||
REQUIRE(l1_seq);
|
||||
}
|
||||
|
||||
{
|
||||
obj v1_seq
|
||||
= GCObjectConversion<obj<AGCObject,DArray>>::from_gco(mm /*not used*/, l1);
|
||||
|
||||
REQUIRE(!v1_seq);
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GCObjectConversion.cpp */
|
||||
|
|
@ -341,7 +341,7 @@ namespace ut {
|
|||
Testcase(2, 1, 16 * 1024, 8 * 128, T, seq_2, 128, T, c_fixed, 3, 0, 0, 0, 0, F),
|
||||
Testcase(2, 2, 16 * 1024, 8 * 128, T, seq_3, 128, T, c_fixed, 4, 0, 0, 0, 0, F),
|
||||
Testcase(2, 2, 16 * 1024, 8 * 128, T, seq_4, 128, T, c_fixed, 4, 0, 0, 0, 0, F),
|
||||
Testcase(2, 2, 16 * 1024, 8 * 128, T, seq_5, 128, T, c_fixed, 4, 0, 0, 0, 0, T),
|
||||
Testcase(2, 2, 16 * 1024, 8 * 128, T, seq_5, 128, T, c_fixed, 4, 0, 0, 0, 0, F),
|
||||
};
|
||||
|
||||
# undef T
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include <xo/object2/Float.hpp>
|
||||
#include <xo/object2/Integer.hpp>
|
||||
#include <xo/object2/List.hpp>
|
||||
//#include "list/IGCObject_DList.hpp"
|
||||
|
||||
#include <xo/gc/X1Collector.hpp>
|
||||
//#include <xo/alloc2/Collector.hpp>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue