diff --git a/xo-alloc2/src/alloc2/DArena.cpp b/xo-alloc2/src/alloc2/DArena.cpp index 07597b67..af4892a6 100644 --- a/xo-alloc2/src/alloc2/DArena.cpp +++ b/xo-alloc2/src/alloc2/DArena.cpp @@ -24,6 +24,9 @@ namespace xo { size_t align_z, bool enable_hugepage_flag) -> range_type { + scope log(XO_DEBUG(true), + xtag("req_z", req_z), xtag("align_z", align_z)); + // 1. round up to multiple of align_z size_t target_z = padding::with_padding(req_z, align_z); // 4. diff --git a/xo-facet/include/xo/facet/facet_implementation.hpp b/xo-facet/include/xo/facet/facet_implementation.hpp index 7e561c05..7ef18847 100644 --- a/xo-facet/include/xo/facet/facet_implementation.hpp +++ b/xo-facet/include/xo/facet/facet_implementation.hpp @@ -96,6 +96,17 @@ namespace xo { template using FacetImplType = FacetImplementation::ImplType; + /** Use: + * auto iface = xo::facet::impl_for(); + * if compiles, then iface is AGCObject interface with state DList. + **/ + template + inline auto impl_for() { + FacetImplType iface; + + return iface; + } + /** Data type for facet implementation that supports runtime polymorphism. * Implementation will stub all methods, since they will never be invoked. * diff --git a/xo-gc/include/xo/gc/DX1Collector.hpp b/xo-gc/include/xo/gc/DX1Collector.hpp index 778f0518..c5b84839 100644 --- a/xo-gc/include/xo/gc/DX1Collector.hpp +++ b/xo-gc/include/xo/gc/DX1Collector.hpp @@ -5,11 +5,13 @@ #pragma once -#include "arena/ArenaConfig.hpp" -#include "arena/DArena.hpp" +#include "GCObject.hpp" #include "generation.hpp" #include "object_age.hpp" #include "role.hpp" +#include +#include +#include #include #include @@ -86,6 +88,9 @@ namespace xo { **/ ArenaConfig arena_config_; + /** storage for N object types requires 8*N bytes **/ + std::size_t object_types_z_ = 2*1024*1024; + /** number of bits to represent generation **/ std::uint64_t gen_bits_ = 8; @@ -154,8 +159,15 @@ namespace xo { using value_type = DArena::value_type; using header_type = DArena::header_type; + /** hard max typeseq for collector-registered types **/ + static constexpr size_t c_max_typeseq = 4096; + + /** Create X1 collector instance. **/ explicit DX1Collector(const CollectorConfig & cfg); + std::string_view name() const { return config_.name_; } + + const DArena * get_object_types() const noexcept { return &object_types_; } const DArena * get_space(role r, generation g) const noexcept { return space_[r][g]; } DArena * get_space(role r, generation g) noexcept { return space_[r][g]; } DArena * from_space(generation g) noexcept { return get_space(role::from_space(), g); } @@ -194,6 +206,14 @@ namespace xo { /** Retreive bookkeeping info for allocation at @p mem. **/ AllocInfo alloc_info(value_type mem) const noexcept; + // ----- type registration ----- + + /** Register object type with this collector. + * Provides shallow copy and pointer forwarding for instances of this + * type. + **/ + bool install_type(const AGCObject & meta) noexcept; + // ----- allocation ----- /** simple allocation. new allocs always in gen0 to-space **/ @@ -239,6 +259,11 @@ namespace xo { /** current gc state **/ GCRunState runstate_; + /** (ab)using arena to get an extensible array of object types. + * For each type need to store one (8-byte) IGCObject_Any instance, + **/ + DArena object_types_; + /** collector-managed memory here. * - space_[1] is from-space * - space_[0] is to-space diff --git a/xo-gc/include/xo/gc/detail/ICollector_Any.hpp b/xo-gc/include/xo/gc/detail/ICollector_Any.hpp index ad55265d..5b0ce66d 100644 --- a/xo-gc/include/xo/gc/detail/ICollector_Any.hpp +++ b/xo-gc/include/xo/gc/detail/ICollector_Any.hpp @@ -24,10 +24,11 @@ namespace xo { * @brief Stub Collector Implementation for empty variant instance **/ struct ICollector_Any : public ACollector { + using typeseq = xo::facet::typeseq; using size_type = std::size_t; // from ACollector - int32_t _typeseq() const noexcept override { return s_typeseq; } + typeseq _typeseq() const noexcept override { return s_typeseq; } // const methods [[noreturn]] size_type allocated(Copaque, generation, role) const noexcept override { _fatal(); } diff --git a/xo-object2/CMakeLists.txt b/xo-object2/CMakeLists.txt index 4dabb209..bb540b37 100644 --- a/xo-object2/CMakeLists.txt +++ b/xo-object2/CMakeLists.txt @@ -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) # ---------------------------------------------------------------- diff --git a/xo-object2/include/xo/object2/DList.hpp b/xo-object2/include/xo/object2/DList.hpp index 79cc84f7..3f6a5639 100644 --- a/xo-object2/include/xo/object2/DList.hpp +++ b/xo-object2/include/xo/object2/DList.hpp @@ -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 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 mm, + obj h1); + /** list with two elements @p h1, @p h2, allocated from @p mm **/ + static DList * list(obj mm, + obj h1, + obj 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 at(size_type ix) const; + /** first member of list **/ obj head_; + /** remainder of list **/ DList * rest_ = nullptr; }; diff --git a/xo-object2/include/xo/object2/IGCObject_DFloat.hpp b/xo-object2/include/xo/object2/IGCObject_DFloat.hpp index 3ccf40d8..58b931bc 100644 --- a/xo-object2/include/xo/object2/IGCObject_DFloat.hpp +++ b/xo-object2/include/xo/object2/IGCObject_DFloat.hpp @@ -5,13 +5,26 @@ #pragma once +#include #include -#include #include #include #include "DFloat.hpp" namespace xo { + namespace scm { struct IGCObject_DFloat; } + + namespace facet { + template <> + struct FacetImplementation + { + using ImplType = xo::mm::IGCObject_Xfer + ; + }; + } + 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 mm) noexcept; - static size_type forward_children(DFloat & d) noexcept; + static size_type forward_children(DFloat & d, obj gc) noexcept; }; } /*namespace scm*/ } /*namespace xo*/ diff --git a/xo-object2/include/xo/object2/IGCObject_DInteger.hpp b/xo-object2/include/xo/object2/IGCObject_DInteger.hpp index c53a79f5..b3061980 100644 --- a/xo-object2/include/xo/object2/IGCObject_DInteger.hpp +++ b/xo-object2/include/xo/object2/IGCObject_DInteger.hpp @@ -12,6 +12,19 @@ #include "DInteger.hpp" namespace xo { + namespace scm { struct IGCObject_DInteger; } + + namespace facet { + template <> + struct FacetImplementation + { + using ImplType = xo::mm::IGCObject_Xfer + ; + }; + } + 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 mm) noexcept; - static size_type forward_children(DInteger & d) noexcept; + static size_type forward_children(DInteger & d, obj gc) noexcept; }; } /*namespace scm*/ } /*namespace xo*/ diff --git a/xo-object2/include/xo/object2/IGCObject_DList.hpp b/xo-object2/include/xo/object2/IGCObject_DList.hpp index c469d17b..3d61a71d 100644 --- a/xo-object2/include/xo/object2/IGCObject_DList.hpp +++ b/xo-object2/include/xo/object2/IGCObject_DList.hpp @@ -5,8 +5,7 @@ #pragma once -#include -#include +#include #include #include #include diff --git a/xo-object2/include/xo/object2/object2_register_types.hpp b/xo-object2/include/xo/object2/object2_register_types.hpp new file mode 100644 index 00000000..9b2656a1 --- /dev/null +++ b/xo-object2/include/xo/object2/object2_register_types.hpp @@ -0,0 +1,19 @@ +/** @file object2_register_types.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include + +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 gc); + } +} + +/* end object2_register_types.hpp */ diff --git a/xo-object2/include/xo/object2/sequence/ISequence_Any.hpp b/xo-object2/include/xo/object2/sequence/ISequence_Any.hpp index ebb172d5..06838b36 100644 --- a/xo-object2/include/xo/object2/sequence/ISequence_Any.hpp +++ b/xo-object2/include/xo/object2/sequence/ISequence_Any.hpp @@ -14,6 +14,7 @@ #pragma once #include "ASequence.hpp" +#include #include 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 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 */ \ No newline at end of file +/* ISequence_Any.hpp */ diff --git a/xo-object2/src/object2/CMakeLists.txt b/xo-object2/src/object2/CMakeLists.txt index dd76874e..e2ffee22 100644 --- a/xo-object2/src/object2/CMakeLists.txt +++ b/xo-object2/src/object2/CMakeLists.txt @@ -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}) diff --git a/xo-object2/src/object2/DList.cpp b/xo-object2/src/object2/DList.cpp index 2377d182..99469d1a 100644 --- a/xo-object2/src/object2/DList.cpp +++ b/xo-object2/src/object2/DList.cpp @@ -7,7 +7,42 @@ #include namespace xo { + using xo::mm::AGCObject; + namespace scm { + static DList s_null(obj(), nullptr); + + DList * + DList::null() + { + return &s_null; + } + + DList * + DList::list(obj mm, + obj h1) + { + void * mem = mm.alloc(sizeof(DList)); + + return new (mem) DList(h1, DList::null()); + } + + DList * + DList::list(obj mm, + obj h1, + obj 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_; } diff --git a/xo-object2/src/object2/IGCObject_DFloat.cpp b/xo-object2/src/object2/IGCObject_DFloat.cpp index 4c67631e..a56175f1 100644 --- a/xo-object2/src/object2/IGCObject_DFloat.cpp +++ b/xo-object2/src/object2/IGCObject_DFloat.cpp @@ -33,9 +33,10 @@ namespace xo { } size_t - IGCObject_DFloat::forward_children(DFloat &) noexcept + IGCObject_DFloat::forward_children(DFloat & src, + obj) noexcept { - return sizeof(DFloat); + return shallow_size(src); } } /*namespace scm*/ diff --git a/xo-object2/src/object2/IGCObject_DInteger.cpp b/xo-object2/src/object2/IGCObject_DInteger.cpp index 0bd97f12..706ab093 100644 --- a/xo-object2/src/object2/IGCObject_DInteger.cpp +++ b/xo-object2/src/object2/IGCObject_DInteger.cpp @@ -33,9 +33,10 @@ namespace xo { } size_t - IGCObject_DInteger::forward_children(DInteger &) noexcept + IGCObject_DInteger::forward_children(DInteger & src, + obj) noexcept { - return sizeof(DInteger); + return shallow_size(src); } } /*namespace scm*/ diff --git a/xo-object2/src/object2/IGCObject_DList.cpp b/xo-object2/src/object2/IGCObject_DList.cpp index d4355a29..19188dfd 100644 --- a/xo-object2/src/object2/IGCObject_DList.cpp +++ b/xo-object2/src/object2/IGCObject_DList.cpp @@ -37,8 +37,7 @@ namespace xo { { gc.forward_inplace(src.head_.iface(), (void **)&(src.head_.data_)); - //auto rest = with_facet::mkobj(src.rest_); - xo::facet::FacetImplementation::ImplType iface; + auto iface = xo::facet::impl_for(); gc.forward_inplace(&iface, (void **)(&src.rest_)); return shallow_size(src); diff --git a/xo-object2/src/object2/object2_register_types.cpp b/xo-object2/src/object2/object2_register_types.cpp new file mode 100644 index 00000000..e472401a --- /dev/null +++ b/xo-object2/src/object2/object2_register_types.cpp @@ -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 gc) + { + bool ok = true; + + ok &= gc.install_type(impl_for()); + + ok &= gc.install_type(impl_for()); + + return ok; + } + } +} /*namespace xo*/ + +/* end object2_register_types.cpp */ diff --git a/xo-object2/utest/CMakeLists.txt b/xo-object2/utest/CMakeLists.txt new file mode 100644 index 00000000..258f56a1 --- /dev/null +++ b/xo-object2/utest/CMakeLists.txt @@ -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) diff --git a/xo-object2/utest/X1Collector.test.cpp b/xo-object2/utest/X1Collector.test.cpp new file mode 100644 index 00000000..3857f0bf --- /dev/null +++ b/xo-object2/utest/X1Collector.test.cpp @@ -0,0 +1,117 @@ +/** @file X1Collector.test.cpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#include +#include +#include + +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 + 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 */ diff --git a/xo-object2/utest/object2_utest_main.cpp b/xo-object2/utest/object2_utest_main.cpp new file mode 100644 index 00000000..a3dcb813 --- /dev/null +++ b/xo-object2/utest/object2_utest_main.cpp @@ -0,0 +1,6 @@ +/* file object2_utest_main.cpp */ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" + +/* end object2_utest_main.cpp */