diff --git a/xo-allocutil/include/xo/allocutil/IAlloc.hpp b/xo-allocutil/include/xo/allocutil/IAlloc.hpp index b126d8a3..703e968c 100644 --- a/xo-allocutil/include/xo/allocutil/IAlloc.hpp +++ b/xo-allocutil/include/xo/allocutil/IAlloc.hpp @@ -42,6 +42,7 @@ namespace xo { **/ using gc_object_interface = xo::Object; using has_incremental_gc_interface = std::true_type; + using has_trivial_deallocate = std::true_type; public: virtual ~IAlloc() {} diff --git a/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp b/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp index 30cbb4f4..80184b0c 100644 --- a/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp +++ b/xo-allocutil/include/xo/allocutil/gc_allocator_traits.hpp @@ -74,7 +74,20 @@ namespace xo { struct has_incremental_gc_interface> : A::has_incremental_gc_interface {}; + // default: allocate A fallback to standard non-GC allocator behavior + template + struct has_trivial_deallocate : std::false_type {}; + + // opt-in: A provides nested type 'has_trivial_deallocate': + // struct A { + // using has_trivial_deallocate = std::true_type; + // }; + template + struct has_trivial_deallocate> : + A::has_trivial_deallocate {}; + // default: empty object interface. + // // classes that want to conditionally support GC // (e.g. see xo::tree::RedBlackTree, xo::tree::Node // in xo-ordinal-tree) @@ -85,13 +98,24 @@ namespace xo { struct object_interface { /** see also IObject::_requires_gc_hooks **/ static constexpr bool _requires_gc_hooks = false; + /** see also IObject::_requires_write_barrier **/ + static constexpr bool _requires_write_barrier = false; + + /** see also IObject::_gc_assign_member **/ + template + void _gc_assign_member(T ** lhs, + T * rhs, + A & alloc) + { + *lhs = rhs; + } + }; // specialization when A provides gc_object_interface template struct object_interface> - : public A::gc_object_interface { - }; + : public A::gc_object_interface {}; // classes that want to conditionally support GC // (e.g. see xo::tree::RedBlackTree, xo::tree::Node @@ -101,14 +125,23 @@ namespace xo { // using object_interface_type = object_interface; - /** true iff this allocator advertises itself as an incremental collector - * allocator will include: + /** true iff this allocator advertises itself as an incremental collector. + * Allocator will include: * - * struct GC { + * struct IAlloc { * using has_incremental_gc_interface = std::true_type; * }; **/ static inline constexpr bool has_incremental_gc_interface_v = has_incremental_gc_interface::value; + + /** true iff this allocator advertises trivial deallocate + * Allocate will include: + * + * struct IAlloc { + * using has_trivial_deallocate = std::true_type; + * }; + **/ + static inline constexpr bool has_trivial_deallocate_v = has_trivial_deallocate::value; }; } /*namespace gc*/ } /*namespace xo*/ diff --git a/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp b/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp index 52235274..d4d6a568 100644 --- a/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp +++ b/xo-ordinaltree/include/xo/ordinaltree/RedBlackTree.hpp @@ -420,35 +420,53 @@ namespace xo { } /*find_sum_glb*/ void clear() { - auto visitor_fn = [this](RbNode const * x, uint32_t /*d*/) { - /* RbUtil.postorder_node_visitor() isn't expecting us to - * alter node, but will not examine it after it's deleted - */ - RbNode * xx = const_cast(x); + if constexpr (node_allocator_traits::has_trivial_deallocate_v) { + // nothing to do since trivial deallocator + } else { + // visitor to delete all Nodes + auto visitor_fn = [this](RbNode const * x, uint32_t depth) { + (void)depth; + + /* RbUtil.postorder_node_visitor() isn't expecting us to + * alter node, but will not examine it after it's deleted + */ + RbNode * xx = const_cast(x); + + node_allocator_traits::deallocate(node_alloc_, xx, 1); + }; + + RbUtil::postorder_node_visitor(this->root_, + 0 /*depth -- ignored by lambda*/, + visitor_fn); - node_allocator_traits::deallocate(node_alloc_, xx, 1); - // delete x }; - RbUtil::postorder_node_visitor(this->root_, - 0 /*depth -- ignored by lambda*/, - visitor_fn); - this->size_ = 0; this->root_ = nullptr; } /*clear*/ std::pair insert(std::pair const & kv_pair) { + RbNode * adj_root = this->root_; + std::pair insert_result = RbUtil::insert_aux(kv_pair, true /*allow_replace_flag*/, this->reduce_fn_, - &(this->root_)); + &adj_root); - if (insert_result.first) + if (insert_result.first) { ++(this->size_); + if (adj_root != root_) { + allocator_type alloc = node_alloc_; + + this->_gc_assign_member(&(this->root_), adj_root, alloc); + } + } else { + assert(adj_root == root_); + } + return (std::pair (iterator(detail::ID_Forward, detail::IL_Regular, @@ -464,16 +482,28 @@ namespace xo { constexpr bool c_logging_enabled = false; scope log(XO_DEBUG(c_logging_enabled)); + RbNode * adj_root = this->root_; + std::pair insert_result = RbUtil::insert_aux(this->node_alloc_, std::move(kv_pair), true /*allow_replace_flag*/, this->reduce_fn_, - &(this->root_)); + this->debug_flag_, + &adj_root); - if (insert_result.first) + if (insert_result.first) { ++(this->size_); + if (adj_root != root_) { + allocator_type alloc = node_alloc_; + + this->_gc_assign_member(&(this->root_), adj_root, alloc); + } + } else { + assert(adj_root == root_); + } + return (std::pair (iterator(detail::ID_Forward, detail::IL_Regular,