diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dabb20..bb540b3 100644 --- a/CMakeLists.txt +++ b/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/include/xo/object2/DList.hpp b/include/xo/object2/DList.hpp index 79cc84f..3f6a563 100644 --- a/include/xo/object2/DList.hpp +++ b/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/include/xo/object2/IGCObject_DFloat.hpp b/include/xo/object2/IGCObject_DFloat.hpp index 3ccf40d..58b931b 100644 --- a/include/xo/object2/IGCObject_DFloat.hpp +++ b/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/include/xo/object2/IGCObject_DInteger.hpp b/include/xo/object2/IGCObject_DInteger.hpp index c53a79f..b306198 100644 --- a/include/xo/object2/IGCObject_DInteger.hpp +++ b/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/include/xo/object2/IGCObject_DList.hpp b/include/xo/object2/IGCObject_DList.hpp index c469d17..3d61a71 100644 --- a/include/xo/object2/IGCObject_DList.hpp +++ b/include/xo/object2/IGCObject_DList.hpp @@ -5,8 +5,7 @@ #pragma once -#include -#include +#include #include #include #include diff --git a/include/xo/object2/object2_register_types.hpp b/include/xo/object2/object2_register_types.hpp new file mode 100644 index 0000000..9b2656a --- /dev/null +++ b/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/include/xo/object2/sequence/ISequence_Any.hpp b/include/xo/object2/sequence/ISequence_Any.hpp index ebb172d..06838b3 100644 --- a/include/xo/object2/sequence/ISequence_Any.hpp +++ b/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/src/object2/CMakeLists.txt b/src/object2/CMakeLists.txt index dd76874..e2ffee2 100644 --- a/src/object2/CMakeLists.txt +++ b/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/src/object2/DList.cpp b/src/object2/DList.cpp index 2377d18..99469d1 100644 --- a/src/object2/DList.cpp +++ b/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/src/object2/IGCObject_DFloat.cpp b/src/object2/IGCObject_DFloat.cpp index 4c67631..a56175f 100644 --- a/src/object2/IGCObject_DFloat.cpp +++ b/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/src/object2/IGCObject_DInteger.cpp b/src/object2/IGCObject_DInteger.cpp index 0bd97f1..706ab09 100644 --- a/src/object2/IGCObject_DInteger.cpp +++ b/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/src/object2/IGCObject_DList.cpp b/src/object2/IGCObject_DList.cpp index d4355a2..19188df 100644 --- a/src/object2/IGCObject_DList.cpp +++ b/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/src/object2/object2_register_types.cpp b/src/object2/object2_register_types.cpp new file mode 100644 index 0000000..e472401 --- /dev/null +++ b/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/utest/CMakeLists.txt b/utest/CMakeLists.txt new file mode 100644 index 0000000..258f56a --- /dev/null +++ b/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/utest/X1Collector.test.cpp b/utest/X1Collector.test.cpp new file mode 100644 index 0000000..3857f0b --- /dev/null +++ b/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/utest/object2_utest_main.cpp b/utest/object2_utest_main.cpp new file mode 100644 index 0000000..a3dcb81 --- /dev/null +++ b/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 */