diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ee2db02..746ebaa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,6 @@ add_subdirectory(xo-allocutil) add_subdirectory(xo-refcnt) add_subdirectory(xo-subsys) add_subdirectory(xo-randomgen) -add_subdirectory(xo-ordinaltree) add_subdirectory(xo-flatstring) add_subdirectory(xo-pyutil) add_subdirectory(xo-reflectutil) @@ -90,8 +89,9 @@ add_subdirectory(xo-ratio) add_subdirectory(xo-unit) add_subdirectory(xo-pyunit) add_subdirectory(xo-callback) -# add_subdirectory(xo-alloc) +add_subdirectory(xo-ordinaltree) +# add_subdirectory(xo-object) # add_subdirectory(xo-webutil) diff --git a/xo-alloc/include/xo/alloc/GC.hpp b/xo-alloc/include/xo/alloc/GC.hpp index aeb918c9..092f2f36 100644 --- a/xo-alloc/include/xo/alloc/GC.hpp +++ b/xo-alloc/include/xo/alloc/GC.hpp @@ -144,9 +144,17 @@ namespace xo { using GcCopyCallbackSet = xo::fn::UpCallbackSet; using nanos = decltype(xo::qty::qty::nanosecond); + /** rebind is for typed allocators. since IAlloc is untyped, + * we want degenerate version + **/ + template + struct rebind { using other = GC; }; + public: /** create new GC instance with configuration @p config **/ explicit GC(const Config & config); + /** noncopyable **/ + GC(const GC & other) = delete; virtual ~GC(); /** create GC allocator. diff --git a/xo-alloc/include/xo/alloc/Object.hpp b/xo-alloc/include/xo/alloc/Object.hpp index 2c11ea9e..9681afab 100644 --- a/xo-alloc/include/xo/alloc/Object.hpp +++ b/xo-alloc/include/xo/alloc/Object.hpp @@ -141,19 +141,6 @@ namespace xo { std::ostream & operator<< (std::ostream & os, gp x); - /** @class Cpof - * @brief argument to operator new used for garbage collector evacuation phase - * - * Tag overloaded operator new to activate allocation policy based on location - * in memory of source object. - **/ - class Cpof { - public: - explicit Cpof(gc::IAlloc * mm, const Object * src) : mm_{mm}, src_{src} {} - - gc::IAlloc * mm_ = nullptr; - const void * src_ = nullptr; - }; } /*namespace xo*/ void * operator new (std::size_t z, const xo::Cpof & copy); diff --git a/xo-allocutil/include/xo/allocutil/IAlloc.hpp b/xo-allocutil/include/xo/allocutil/IAlloc.hpp index 5c18a2a7..4f9097f7 100644 --- a/xo-allocutil/include/xo/allocutil/IAlloc.hpp +++ b/xo-allocutil/include/xo/allocutil/IAlloc.hpp @@ -14,6 +14,7 @@ namespace xo { using up = std::unique_ptr; class IObject; + class Object; namespace gc { /** @class IAllocator @@ -31,10 +32,15 @@ namespace xo { **/ class IAlloc { public: + /** type-erased allocator **/ + using value_type = std::byte; using pointer_type = std::byte *; using size_type = std::size_t; - /** traits: see gc_allocator_traits **/ - using gc_object_interface = xo::IObject; + /** traits: see gc_allocator_traits + * + * want Object here (not IObject) to get Object::_forward_to() + **/ + using gc_object_interface = xo::Object; using has_incremental_gc_interface = std::true_type; public: @@ -151,6 +157,66 @@ namespace xo { // LCOV_EXCL_STOP } }; + + /** allocator wrapper (in the style of std::allocator) + **/ + template + struct allocator { + public: + using value_type = T; + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; + using size_type = IAlloc::size_type; + using difference_type = std::ptrdiff_t; + + using gc_object_interface = IAlloc::gc_object_interface; + using has_incremental_gc_interface = IAlloc::has_incremental_gc_interface; + + /** rebind is for typed allocators. since IAlloc is untyped, + * we want degenerate version + **/ + template + struct rebind { + using other = allocator; + }; + + public: + explicit allocator(IAlloc * mm) : mm_{mm} {} + + allocator(const allocator &) = default; + allocator & operator=(const allocator &) = default; + + template + allocator(const allocator & other) : mm_{other.mm_} {} + + pointer allocate(size_type n) { + std::byte * raw = mm_->allocate(n * sizeof(T)); + + return reinterpret_cast(raw); + } + + void deallocate(pointer p, size_type n) { + std::byte * raw = reinterpret_cast(p); + + mm_->deallocate(raw, n * sizeof(T)); + } + + // optional construct, destroy (but allocator_traits provides defaults) + + /** required! otherwise allocator, allocator with the same IAlloc* + * would be considered to own disjoin memory addresses + **/ + template + bool operator==(const allocator & other) const noexcept { + return mm_ == other.mm_; + } + + public: + IAlloc * mm_ = nullptr; + }; + } /*namespace gc*/ class MMPtr { diff --git a/xo-allocutil/include/xo/allocutil/IObject.hpp b/xo-allocutil/include/xo/allocutil/IObject.hpp index c4b15800..87c60130 100644 --- a/xo-allocutil/include/xo/allocutil/IObject.hpp +++ b/xo-allocutil/include/xo/allocutil/IObject.hpp @@ -92,6 +92,20 @@ namespace xo { **/ virtual std::size_t _forward_children(gc::IAlloc * gc) = 0; }; + + /** @class Cpof + * @brief argument to operator new used for garbage collector evacuation phase + * + * Tag overloaded operator new to activate allocation policy based on location + * in memory of source object. + **/ + class Cpof { + public: + explicit Cpof(gc::IAlloc * mm, const IObject * src) : mm_{mm}, src_{src} {} + + gc::IAlloc * mm_ = nullptr; + const void * src_ = nullptr; + }; } /* end IObject.hpp */ diff --git a/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp b/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp index e183471b..dd1a6322 100644 --- a/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp +++ b/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp @@ -57,6 +57,10 @@ namespace xo { using super = std::allocator_traits; using pointer = typename super::pointer; using value_type = typename super::value_type; + using super::construct; + using super::destroy; + using super::allocate; + using super::deallocate; // default: allocator A fallback to standard non-gc allocator behavior template diff --git a/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp b/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp index a46a222c..cd46ad6c 100644 --- a/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp +++ b/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp @@ -111,21 +111,22 @@ namespace xo { using allocator_type = Allocator; using allocator_traits = xo::gc::gc_allocator_traits; + using GcObjectInterface = allocator_traits::template object_interface; using ReducedValue = typename Reduce::value_type; - using RbUtil = detail::RbTreeUtil; - using RbNode = detail::Node; + using RbUtil = detail::RbTreeUtil; + using RbNode = detail::Node; using RbTreeLhs = detail::RedBlackTreeLhs; using RbTreeConstLhs = detail::RedBlackTreeConstLhs; using node_type = RbNode; - using node_allocator_type = typename xo::gc::gc_allocator_traits::template rebind_alloc; + using node_allocator_type = allocator_traits::template rebind_alloc; using node_allocator_traits = xo::gc::gc_allocator_traits; using Direction = detail::Direction; using size_type = std::size_t; using difference_type = std::ptrdiff_t; - using iterator = detail::Iterator; - using const_iterator = detail::ConstIterator; + using iterator = detail::Iterator; + using const_iterator = detail::ConstIterator; public: explicit RedBlackTree(const allocator_type & alloc = allocator_type{}, @@ -614,10 +615,11 @@ namespace xo { template + typename Reduce, + typename Allocator> inline std::ostream & - operator<<(std::ostream &os, - RedBlackTree const &tree) + operator<<(std::ostream & os, + RedBlackTree const & tree) { tree.display(); return os; @@ -626,10 +628,11 @@ namespace xo { template inline std::ostream & operator<<(std::ostream & os, - detail::IteratorBase const & iter) + detail::IteratorBase const & iter) { iter.print(os); return os; diff --git a/xo-ordinaltree/include/xo/ordinaltree/rbtree/Iterator.hpp b/xo-ordinaltree/include/xo/ordinaltree/rbtree/Iterator.hpp index bab4c4b8..73252294 100644 --- a/xo-ordinaltree/include/xo/ordinaltree/rbtree/Iterator.hpp +++ b/xo-ordinaltree/include/xo/ordinaltree/rbtree/Iterator.hpp @@ -16,14 +16,16 @@ namespace xo { template struct NodeTypeTraits { using NodeType = void; }; template - struct NodeTypeTraits { - using NativeNodeType = Node; + typename Reduce, + typename GcObjectInterface> + struct NodeTypeTraits { + using NativeNodeType = Node; using NodeType = NativeNodeType; using ContentsType = typename NodeType::value_type; using NodePtrType = NodeType *; @@ -31,9 +33,10 @@ namespace xo { template - struct NodeTypeTraits { - using NativeNodeType = Node; + typename Reduce, + typename GcObjectInterface> + struct NodeTypeTraits { + using NativeNodeType = Node; using NodeType = NativeNodeType const; using ContentsType = typename NodeType::value_type const; using NodePtrType = NodeType const *; @@ -48,12 +51,13 @@ namespace xo { template class IteratorBase { public: - using RbUtil = RbTreeUtil; - using RbNode = Node; - using Traits = NodeTypeTraits; + using RbUtil = RbTreeUtil; + using RbNode = Node; + using Traits = NodeTypeTraits; using ReducedValue = typename Reduce::value_type; using RbNativeNodeType = typename Traits::NativeNodeType; using RbNodePtrType = typename Traits::NodePtrType; @@ -233,12 +237,17 @@ namespace xo { */ template - class Iterator : public IteratorBase { + typename Reduce, + typename GcObjectInterface> + class Iterator : public IteratorBase { public: using iterator_concept = std::bidirectional_iterator_tag; - using RbIteratorBase = IteratorBase; + using RbIteratorBase = IteratorBase; using RbNode = typename RbIteratorBase::RbNode; using RbUtil = typename RbIteratorBase::RbUtil; using ReducedValue = typename Reduce::value_type; @@ -298,12 +307,13 @@ namespace xo { */ template - class ConstIterator : public IteratorBase { + typename Reduce, + typename GcObjectInterface> + class ConstIterator : public IteratorBase { public: using iterator_concept = std::bidirectional_iterator_tag; - using RbIteratorBase = IteratorBase; + using RbIteratorBase = IteratorBase; using RbNode = typename RbIteratorBase::RbNode; using RbUtil = typename RbIteratorBase::RbUtil; using ReducedValue = typename Reduce::value_type; diff --git a/xo-ordinaltree/include/xo/ordinaltree/rbtree/Node.hpp b/xo-ordinaltree/include/xo/ordinaltree/rbtree/Node.hpp index 90ae2dec..c1cca7ec 100644 --- a/xo-ordinaltree/include/xo/ordinaltree/rbtree/Node.hpp +++ b/xo-ordinaltree/include/xo/ordinaltree/rbtree/Node.hpp @@ -6,14 +6,18 @@ #pragma once #include "RbTypes.hpp" +#include "xo/reflect/Reflect.hpp" +#include "xo/indentlog/scope.hpp" +#include "xo/allocutil/IObject.hpp" #include "xo/allocutil/gc_allocator_traits.hpp" +#include #include namespace xo { namespace tree { namespace detail { /** see xo/ordinaltree/rbtree/RbTreeUtil.hpp */ - template + template class RbTreeUtil; /* xo::tree::detail::Node @@ -25,19 +29,23 @@ namespace xo { */ template - class Node { + typename Reduce, + typename GcObjectInterface> + class Node : public GcObjectInterface { public: using ReducedValue = typename Reduce::value_type; using ContentsType = std::pair; using value_type = std::pair; + using Reflect = xo::reflect::Reflect; + using TaggedPtr = xo::reflect::TaggedPtr; + using IObject = xo::IObject; public: Node() = default; Node(value_type const & kv_pair, std::pair const & r) : color_(C_Red), size_(1), contents_{kv_pair}, reduced_(r) {} - Node(value_type && kv_pair, + Node(value_type && kv_pair, std::pair && r) : color_(C_Red), size_(1), contents_{std::move(kv_pair)}, @@ -115,8 +123,7 @@ namespace xo { static std::pair reduced_pair(Reduce r, Node const * x) { - if(!x) - assert(false); + assert(x != nullptr); ReducedValue r1 = r(reduce_aux(r, x->left_child()), x->value()); @@ -294,7 +301,7 @@ namespace xo { xtag("r2", this->reduced2())); } /*local_recalc_size*/ - private: + private: void assign_color(Color x) { this->color_ = x; } void assign_size(size_t z) { this->size_ = z; } @@ -336,8 +343,24 @@ namespace xo { } } /*replace_child_reparent*/ - friend class RbTreeUtil; - friend class xo::tree::RedBlackTree; + // ----- (possibly) inherited from GcObjectInterface ----- + virtual TaggedPtr self_tp() const { return Reflect::make_tp(const_cast(this)); } + virtual void display(std::ostream & os) const { os << ""; } + virtual std::size_t _shallow_size() const { return sizeof(*this); } + /* note: only relevant when GcObjectInterface is xo::IObject */ + + template + virtual IObject * _shallow_copy(gc::IAlloc * gc) const + -> std::enable_if_t; + template + friend class xo::tree::RedBlackTree; private: /* red | black */ diff --git a/xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeUtil.hpp b/xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeUtil.hpp index 830bdecf..acc162cc 100644 --- a/xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeUtil.hpp +++ b/xo-ordinaltree/include/xo/ordinaltree/rbtree/RbTreeUtil.hpp @@ -18,10 +18,11 @@ namespace xo { */ template + typename Reduce, + typename GcObjectInterface> class RbTreeUtil { public: - using RbNode = Node; + using RbNode = Node; using ReducedValue = typename Reduce::value_type; using value_type = std::pair; diff --git a/xo-ordinaltree/utest/CMakeLists.txt b/xo-ordinaltree/utest/CMakeLists.txt index a0985542..878a2cb8 100644 --- a/xo-ordinaltree/utest/CMakeLists.txt +++ b/xo-ordinaltree/utest/CMakeLists.txt @@ -2,7 +2,7 @@ # note: tests in this directory use Catch2-provided main set(SELF_EXE utest.tree) -set(SELF_SOURCE_FILES tree_utest_main.cpp redblacktree.cpp bplustree.cpp) +set(SELF_SOURCE_FILES tree_utest_main.cpp redblacktree.cpp bplustree.cpp RedBlackTree-gc.test.cpp) add_executable(${SELF_EXE} ${SELF_SOURCE_FILES}) xo_include_options2(${SELF_EXE}) @@ -16,6 +16,7 @@ add_test(NAME ${SELF_EXE} COMMAND ${SELF_EXE}) # internal dependencies: refcnt, ... xo_self_dependency(${SELF_EXE} xo_ordinaltree) +xo_dependency(${SELF_EXE} xo_alloc) xo_dependency(${SELF_EXE} refcnt) xo_dependency(${SELF_EXE} indentlog) xo_dependency(${SELF_EXE} randomgen) diff --git a/xo-ordinaltree/utest/RedBlackTree-gc.test.cpp b/xo-ordinaltree/utest/RedBlackTree-gc.test.cpp new file mode 100644 index 00000000..d0d8d587 --- /dev/null +++ b/xo-ordinaltree/utest/RedBlackTree-gc.test.cpp @@ -0,0 +1,104 @@ +/** @file RedBlackTree-gc.test.cpp + **/ + +#include "random_tree_ops.hpp" +#include "xo/ordinaltree/RedBlackTree.hpp" +#include "xo/ordinaltree/rbtree/SumReduce.hpp" +#include "xo/alloc/GC.hpp" +#include + +namespace xo { + using xo::gc::GC; + using xo::tree::RedBlackTree; + using xo::tree::SumReduce; + + using utest::TreeUtil; + + namespace ut { + + namespace { + struct Testcase_RbTree { + Testcase_RbTree(std::size_t nz, + std::size_t tz, + std::size_t ngct, + std::size_t tgct) : nursery_z_{nz}, + tenured_z_{tz}, + incr_gc_threshold_{ngct}, + full_gc_threshold_{tgct} {} + + + 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 = { + Testcase_RbTree(1024, 4096, 512, 512), + }; + } + + TEST_CASE("rbtree-gc-1", "[gc][redblacktree]") + { + using RbTree = RedBlackTree, + xo::gc::allocator>>; + + constexpr bool c_debug_flag = false; + + for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) { + const Testcase_RbTree & tc = s_testcase_v[i_tc]; + + std::uint64_t seed = 8813374093428528487ULL; + auto rgen = xo::rng::xoshiro256ss(seed); + + for (std::uint32_t n=0; n<1; ++n) { + bool ok_flag = false; + + for (std::uint32_t attention = 0; !ok_flag && (attention < 2); ++attention) { + bool debug_flag = c_debug_flag || (attention == 1); + + scope log(XO_DEBUG2(debug_flag, "rbtree-gc-1")); + + ok_flag = true; // unless contradicted below + + up gc = GC::make( + { + .initial_nursery_z_ = tc.nursery_z_, + .initial_tenured_z_ = tc.tenured_z_, + .incr_gc_threshold_ = tc.incr_gc_threshold_, + .full_gc_threshold_ = tc.full_gc_threshold_, + .debug_flag_ = debug_flag + } + ); + + xo::gc::allocator allocator(gc.get()); + + /* verify that we can construct a tree node */ + RbTree::RbNode node; + +#ifdef NOT_YET + RedBlackTree::node_allocator_traits::construct(allocator, &node, + {0, 0.0}, + {0.0, 0.0}); +#endif + +#ifdef NOT_YET + RbTree rbtree(allocator, c_debug_flag); + + /* insert [0..n-1] in random order **/ + ok_flag &= TreeUtil::random_inserts(n, debug_flag, &rgen, &rbtree); +#endif + } + } + } + + } + + } /*namespace ut*/ +} /*namesapce xo*/ + +/* end RedBlackTree-gc.test.cpp */ +