xo-gc stack: fix mutation setup + xo-reader2 utest
This commit is contained in:
parent
b1d2ae6f19
commit
f56b01e7b6
11 changed files with 234 additions and 23 deletions
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
|
||||
/** @class CollectorTypeRegistry
|
||||
*
|
||||
* @brief Runtime registry for gc-aware types
|
||||
|
|
@ -68,7 +69,8 @@ namespace xo {
|
|||
/** initialization steps for a new Collector instance **/
|
||||
std::vector<init_function_type> init_seq_v_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace mm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end CollectorTypeRegistry.hpp */
|
||||
|
|
|
|||
|
|
@ -13,13 +13,27 @@
|
|||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
class GCObjectConversionUtil {
|
||||
public:
|
||||
using AGCObject = xo::mm::AGCObject;
|
||||
using typeseq = xo::reflect::typeseq;
|
||||
|
||||
/** helper method fro GCObjectConversion<..>::from_gco()
|
||||
* on conversion failure
|
||||
**/
|
||||
static void _from_gco_fail_aux(obj<AGCObject> gco,
|
||||
typeseq tseq,
|
||||
scope * p_log);
|
||||
};
|
||||
|
||||
/** @brief compile-time conversion obj<AGCObject> <-> T
|
||||
*
|
||||
* Specialize for each T that participates in conversion.
|
||||
* Methods here aren't implemented
|
||||
**/
|
||||
template <typename T>
|
||||
struct GCObjectConversion {
|
||||
class GCObjectConversion {
|
||||
public:
|
||||
using AGCObject = xo::mm::AGCObject;
|
||||
using AAllocator = xo::mm::AAllocator;
|
||||
|
||||
|
|
@ -73,6 +87,13 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
/** Several use cases here:
|
||||
* 1. runtime polymorphism
|
||||
* obj<AGCObject,DArray> v(DArray::make(..));
|
||||
* // from_gco() doesn't know v repr
|
||||
* auto gc = GCObjectConversion<ASequence,DArray>::from_gco(mm, v);
|
||||
*
|
||||
**/
|
||||
static obj<AFacet,DRepr> from_gco(obj<AAllocator>,
|
||||
obj<AGCObject> gco) {
|
||||
scope log(XO_DEBUG(false));
|
||||
|
|
@ -92,14 +113,10 @@ namespace xo {
|
|||
auto retval = obj<AFacet,DRepr>::from(gco);
|
||||
|
||||
if (!retval) {
|
||||
log.retroactively_enable();
|
||||
|
||||
log && log(xtag("gco.tseq", gco._typeseq()));
|
||||
log && log(xtag("DRepr.tseq", reflect::typeseq::id<DRepr>()));
|
||||
GCObjectConversionUtil::_from_gco_fail_aux
|
||||
(gco, reflect::typeseq::id<DRepr>(), &log);
|
||||
}
|
||||
|
||||
assert(retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,16 @@ namespace xo {
|
|||
/** @defgroup mm-allocator-methods Allocator methods **/
|
||||
///@{
|
||||
|
||||
/** An uninitialized AAllocator 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 **/
|
||||
virtual typeseq _typeseq() const noexcept = 0;
|
||||
/** destroy instance @p d. Calls c++ destructor for actual runtime type.
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ namespace xo {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool _has_null_vptr() const noexcept { return O::iface()->_has_null_vptr(); }
|
||||
typeseq _typeseq() const noexcept { return O::iface()->_typeseq(); }
|
||||
void _drop() const noexcept { O::iface()->_drop(O::data()); }
|
||||
std::string_view name() const noexcept { return O::iface()->name(O::data()); }
|
||||
|
|
|
|||
|
|
@ -33,9 +33,14 @@ namespace xo {
|
|||
obj<AGCObject> * p_lhs,
|
||||
obj<AGCObject> rhs) noexcept
|
||||
{
|
||||
this->barrier_assign_aux(parent,
|
||||
p_lhs->iface(), p_lhs->opaque_data_addr(),
|
||||
rhs.iface(), rhs.opaque_data());
|
||||
if (this->data()) {
|
||||
this->barrier_assign_aux(parent,
|
||||
p_lhs->iface(), p_lhs->opaque_data_addr(),
|
||||
rhs.iface(), rhs.opaque_data());
|
||||
} else {
|
||||
// special case: for null allocator want no write-barrier
|
||||
*p_lhs = rhs;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Object>
|
||||
|
|
@ -48,11 +53,16 @@ namespace xo {
|
|||
// need to get AGCObject i/face that goes with DRepr.
|
||||
obj<AGCObject,DRepr> rhs_gco(rhs_data);
|
||||
|
||||
this->barrier_assign_aux(parent,
|
||||
nullptr /*not needed*/,
|
||||
lhs_data,
|
||||
rhs_gco.iface(),
|
||||
rhs_data);
|
||||
if (this->data()) {
|
||||
this->barrier_assign_aux(parent,
|
||||
nullptr /*not needed*/,
|
||||
lhs_data,
|
||||
rhs_gco.iface(),
|
||||
rhs_data);
|
||||
} else {
|
||||
// special case: for null allocator want no write-barrier
|
||||
*lhs_data = rhs_data;
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace mm*/
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ set(SELF_SRCS
|
|||
SetupAlloc2.cpp
|
||||
|
||||
CollectorTypeRegistry.cpp
|
||||
GCObjectConversion.cpp
|
||||
|
||||
facet/ICollector_Any.cpp
|
||||
|
||||
|
|
|
|||
28
src/alloc2/GCObjectConversion.cpp
Normal file
28
src/alloc2/GCObjectConversion.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/** @file GCObjectConversion.cpp
|
||||
*
|
||||
* @author Roland Conybeare, May 2026
|
||||
**/
|
||||
|
||||
#include "GCObjectConversion.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::typeseq;
|
||||
|
||||
namespace scm {
|
||||
|
||||
void
|
||||
GCObjectConversionUtil::_from_gco_fail_aux(obj<AGCObject> gco,
|
||||
typeseq tseq,
|
||||
scope * p_log)
|
||||
{
|
||||
p_log->retroactively_enable();
|
||||
if (p_log) {
|
||||
(*p_log)(xtag("gco.tseq", gco._typeseq()));
|
||||
(*p_log)(xtag("DRepr.tseq", tseq));
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GCObjectConversion.cpp */
|
||||
|
|
@ -9,6 +9,8 @@ set(UTEST_SRCS
|
|||
DArenaIterator.test.cpp
|
||||
# Collector.test.cpp
|
||||
# DX1CollectorIterator.test.cpp
|
||||
Generation.test.cpp
|
||||
dp.test.cpp
|
||||
random_allocs.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
42
utest/Generation.test.cpp
Normal file
42
utest/Generation.test.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/** @file Generation.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, May 2026
|
||||
**/
|
||||
|
||||
#include "Generation.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::mm::Generation;
|
||||
using xo::mm::c_max_generation;
|
||||
|
||||
namespace ut {
|
||||
|
||||
TEST_CASE("Generation-1", "[Generation]")
|
||||
{
|
||||
REQUIRE(Generation::nursery() == 0);
|
||||
REQUIRE(Generation::g0() == 0);
|
||||
REQUIRE(Generation::g1() == 1);
|
||||
REQUIRE(Generation::sentinel() == c_max_generation);
|
||||
|
||||
REQUIRE(Generation::g0().is_sentinel() == false);
|
||||
REQUIRE(Generation::g1().is_sentinel() == false);
|
||||
REQUIRE(Generation::sentinel().is_sentinel());
|
||||
|
||||
REQUIRE(Generation::g0() != Generation::sentinel());
|
||||
REQUIRE(Generation::g1() != Generation::sentinel());
|
||||
|
||||
REQUIRE(!(Generation::g0() > Generation::g1()));
|
||||
REQUIRE(Generation::g1() > Generation::g0());
|
||||
|
||||
auto g = Generation::g0();
|
||||
++g;
|
||||
REQUIRE(g == Generation::g1());
|
||||
++g;
|
||||
REQUIRE(g > Generation::g1());
|
||||
}
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Generation.hpp */
|
||||
|
|
@ -3,11 +3,10 @@
|
|||
* @author Roland Conybeare, Dec 2025
|
||||
**/
|
||||
|
||||
#include "xo/alloc2/Allocator.hpp"
|
||||
#include "xo/alloc2/Arena.hpp"
|
||||
//#include "xo/alloc2/arena/IAllocator_DArena.hpp"
|
||||
#include "xo/arena/print.hpp"
|
||||
#include "xo/arena/padding.hpp"
|
||||
#include <xo/alloc2/Allocator.hpp>
|
||||
#include <xo/alloc2/Arena.hpp>
|
||||
#include <xo/arena/print.hpp>
|
||||
#include <xo/arena/padding.hpp>
|
||||
#include <xo/facet/obj.hpp>
|
||||
#include <xo/indentlog/scope.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
|
@ -150,7 +149,6 @@ namespace xo {
|
|||
.size_ = 64*1024,
|
||||
.debug_flag_ = false };
|
||||
DArena arena = DArena::map(cfg);
|
||||
//obj<AAllocator, DArena> a1o{&arena};
|
||||
auto a1o = with_facet<AAllocator>::mkobj(&arena);
|
||||
|
||||
REQUIRE(a1o.reserved() >= cfg.size_);
|
||||
|
|
|
|||
100
utest/dp.test.cpp
Normal file
100
utest/dp.test.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/** @file dp.test.cpp
|
||||
*
|
||||
* @author Roland Conybeare, May 2026
|
||||
**/
|
||||
|
||||
#include "dp.hpp"
|
||||
#include <xo/alloc2/Allocator.hpp>
|
||||
#include <xo/alloc2/Arena.hpp>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::mm::AAllocator;
|
||||
using xo::mm::DArena;
|
||||
using xo::mm::ArenaConfig;
|
||||
|
||||
namespace {
|
||||
class Foo {
|
||||
public:
|
||||
explicit Foo(uint32_t * p_counter) : p_counter_{p_counter} {}
|
||||
|
||||
static constexpr bool is_gc_eligible() { return false; }
|
||||
|
||||
~Foo() {
|
||||
++(*p_counter_);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t * p_counter_ = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
namespace ut {
|
||||
|
||||
TEST_CASE("dp-1", "[dp]")
|
||||
{
|
||||
//ArenaConfig cfg { .name_ = "testarena", .size_ = 1024 };
|
||||
//DArena arena = DArena::map(cfg);
|
||||
//auto mm = obj<AAllocator,DArena>(&arena);
|
||||
|
||||
uint32_t counter = 0;
|
||||
Foo foo(&counter);
|
||||
|
||||
REQUIRE(counter == 0);
|
||||
{
|
||||
dp<Foo> foo_dp(&foo);
|
||||
|
||||
REQUIRE(foo_dp);
|
||||
REQUIRE(foo_dp.data());
|
||||
REQUIRE(foo_dp.is_gc_eligible() == false);
|
||||
|
||||
// foo_dp dtor runs here, increments counter
|
||||
}
|
||||
REQUIRE(counter == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("dp-2", "[dp]")
|
||||
{
|
||||
uint32_t counter = 0;
|
||||
Foo foo(&counter);
|
||||
|
||||
REQUIRE(counter == 0);
|
||||
{
|
||||
dp<Foo> foo_dp(&foo);
|
||||
|
||||
REQUIRE(foo_dp);
|
||||
REQUIRE(foo_dp.data() == &foo);
|
||||
|
||||
foo_dp.release();
|
||||
|
||||
REQUIRE(!foo_dp);
|
||||
REQUIRE(!foo_dp.data());
|
||||
|
||||
// foo_dp dtor runs here, increments counter
|
||||
}
|
||||
REQUIRE(counter == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("dp-DArena", "[dp][DArena]")
|
||||
{
|
||||
ArenaConfig cfg { .name_ = "testarena", .size_ = 1024 };
|
||||
DArena arena = DArena::map(cfg);
|
||||
//auto mm = obj<AAllocator,DArena>(&arena);
|
||||
|
||||
REQUIRE(arena.reserved() > 0);
|
||||
REQUIRE(arena.is_mapped());
|
||||
{
|
||||
dp<DArena> arena_dp(&arena);
|
||||
|
||||
REQUIRE(arena_dp);
|
||||
REQUIRE(arena_dp.data() == &arena);
|
||||
}
|
||||
|
||||
REQUIRE(arena.reserved() == 0);
|
||||
REQUIRE(!arena.is_mapped());
|
||||
}
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end dp.test.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue