xo-alloc xo-ordinaltree: GC option work in progress
This commit is contained in:
parent
540b43d971
commit
3a840546fe
12 changed files with 79 additions and 23 deletions
|
|
@ -76,6 +76,7 @@ set(DOX_EXCLUDE_PATTERNS [=[
|
|||
|
||||
add_subdirectory(xo-cmake)
|
||||
add_subdirectory(xo-indentlog)
|
||||
add_subdirectory(xo-allocutil)
|
||||
add_subdirectory(xo-refcnt)
|
||||
add_subdirectory(xo-subsys)
|
||||
add_subdirectory(xo-randomgen)
|
||||
|
|
@ -90,7 +91,6 @@ add_subdirectory(xo-unit)
|
|||
add_subdirectory(xo-pyunit)
|
||||
add_subdirectory(xo-callback)
|
||||
#
|
||||
add_subdirectory(xo-allocutil)
|
||||
add_subdirectory(xo-alloc)
|
||||
add_subdirectory(xo-object)
|
||||
#
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ namespace xo {
|
|||
* 3. return the location of the copy make in step 1.
|
||||
*
|
||||
* @p src. source object to be forwarded
|
||||
* @p gc. garbage collector
|
||||
* @p gc. allocator (poassibly garbage collector)
|
||||
*/
|
||||
static IObject * _forward(IObject * src, gc::IAlloc * gc);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace xo {
|
|||
|
||||
gp<Object> member() const { return member_; }
|
||||
void assign_member(Object * x) {
|
||||
Object::mm->assign_member(this, member_.ptr_address(), x);
|
||||
Object::mm->assign_member(this, reinterpret_cast<IObject**>(member_.ptr_address()), x);
|
||||
}
|
||||
|
||||
TaggedPtr self_tp() const final override {
|
||||
|
|
@ -33,7 +33,7 @@ namespace xo {
|
|||
void display(std::ostream & os) const final override { os << data_; }
|
||||
|
||||
virtual std::size_t _shallow_size() const final override { return sizeof(*this); }
|
||||
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override { return new (Cpof(mm, this)) DummyObject(*this); }
|
||||
virtual IObject * _shallow_copy(gc::IAlloc * mm) const final override { return new (Cpof(mm, this)) DummyObject(*this); }
|
||||
virtual std::size_t _forward_children(gc::IAlloc * gc) final override { return _shallow_size(); }
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/IAlloc.hpp"
|
||||
#include "xo/allocutil/IAlloc.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "gc_allocator_traits.hpp"
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
|
|
@ -25,8 +26,17 @@ namespace xo {
|
|||
*
|
||||
* See class GC for copying incremental collector.
|
||||
* See class ArenaAlloc for arena allocator
|
||||
*
|
||||
* In either case deallocation is trivial
|
||||
**/
|
||||
class IAlloc {
|
||||
public:
|
||||
using pointer_type = std::byte *;
|
||||
using size_type = std::size_t;
|
||||
/** traits: see gc_allocator_traits<IAlloc> **/
|
||||
using gc_object_interface = xo::IObject;
|
||||
using has_incremental_gc_interface = std::true_type;
|
||||
|
||||
public:
|
||||
virtual ~IAlloc() {}
|
||||
|
||||
|
|
@ -55,6 +65,23 @@ namespace xo {
|
|||
return z + alloc_padding(z);
|
||||
}
|
||||
|
||||
// ----- std::allocator interface -----
|
||||
|
||||
std::byte * allocate(std::size_t n) {
|
||||
return this->alloc(n);
|
||||
}
|
||||
|
||||
/** std::allocator locality hint. Not used for now **/
|
||||
std::byte * allocate(std::size_t n, const void * hint) {
|
||||
(void)hint;
|
||||
return this->alloc(n);
|
||||
}
|
||||
|
||||
/** deallocation trivial for IAlloc **/
|
||||
void deallocate(std::byte *, std::size_t) {}
|
||||
|
||||
// ----- IAlloc methods -----
|
||||
|
||||
/** optional name for this allocator; labelling for diagnostics **/
|
||||
virtual const std::string & name() const = 0;
|
||||
/** allocator size in bytes (up to reserved limit)
|
||||
|
|
|
|||
|
|
@ -16,15 +16,41 @@ namespace xo {
|
|||
* for garbage-collector-enabled allocators
|
||||
*
|
||||
* allocator A can identify itself as a copying collector:
|
||||
*
|
||||
* 1. provide A::object_interface
|
||||
* A::object_interface = xo::Object
|
||||
* 2. provide A::is_incremental_collector
|
||||
* A::is_incremental_collector = std::true_type
|
||||
* Collectible objects must:
|
||||
* per-object header interface: tells garbage collector
|
||||
* how to navigate object graph.
|
||||
* A::gc_object_interface = xo::IObject
|
||||
* contains virtual methods; classes that can be garbage
|
||||
* collected should inherit this interface
|
||||
*
|
||||
* 2. provide A::has_incremental_gc_interface
|
||||
* A::has_incremental_gc_interface = std::true_type
|
||||
* This doesn't imply A is a garbage-collecting allocator;
|
||||
* it just implies that it supports a collection api.
|
||||
* - xo::gc::ArenaAlloc has a collection API, but does not
|
||||
* provide garbage collection
|
||||
* - xo::gc::GC has a collection API and also provides
|
||||
* garbage collection
|
||||
*
|
||||
* GC-allocated objects must:
|
||||
* 2a. inherit A::object_interface
|
||||
* 2b. implement A::object_interface::_shallow_size()
|
||||
* 2c. implement A::object_interface::_shallow_copy(alloc)
|
||||
* 2d. implement A::object_interface::_forward_children(alloc)
|
||||
* in multiple inheritance scenarios
|
||||
* 2e. implement A::object_interface::_offset_destination(src)
|
||||
*
|
||||
* Design Notes:
|
||||
* - virtual-method choice requires vtable pointer per object;
|
||||
* but zero *marginal* space cost for types that would have
|
||||
* a vtable pointer anyway.
|
||||
* - can still handle non-vtable objects, by providing a
|
||||
* object-interface-inheriting wrapper.
|
||||
* - less-intrusive (though less space-efficient) alternative
|
||||
* would be to use a type-registration system;
|
||||
* then GC hooks could be setup independently of a subject type.
|
||||
* (watch out for pimpl implementations though!)
|
||||
**/
|
||||
template <typename Allocator>
|
||||
struct gc_allocator_traits : std::allocator_traits<Allocator> {
|
||||
|
|
@ -34,15 +60,15 @@ namespace xo {
|
|||
|
||||
// default: allocator A fallback to standard non-gc allocator behavior
|
||||
template <typename A, typename = void>
|
||||
struct is_incremental_collector : std::false_type {};
|
||||
struct has_incremental_gc_interface : std::false_type {};
|
||||
|
||||
// opt-in: A provides nested type 'is_incremental_collector':
|
||||
// opt-in: A provides nested type 'has_incremental_collector_interface':
|
||||
// struct A {
|
||||
// using is_incremental_collector = std::true_type;
|
||||
// };
|
||||
template <typename A>
|
||||
struct is_incremental_collector<A, std::void_t<typename A::is_incremental_collector>> :
|
||||
A::is_incremental_collector {};
|
||||
struct has_incremental_gc_interface<A, std::void_t<typename A::has_incremental_gc_interface>> :
|
||||
A::has_incremental_gc_interface {};
|
||||
|
||||
// default: empty object interface.
|
||||
// classes that want to conditionally support GC
|
||||
|
|
@ -63,10 +89,10 @@ namespace xo {
|
|||
* allocator will include:
|
||||
*
|
||||
* struct GC {
|
||||
* using is_incremental_collector = std::true_type;
|
||||
* using has_incremental_gc_interface = std::true_type;
|
||||
* };
|
||||
**/
|
||||
static inline constexpr bool is_incremental_collector_v = is_incremental_collector<Allocator>::value;
|
||||
static inline constexpr bool has_incremental_gc_interface_v = has_incremental_gc_interface<Allocator>::value;
|
||||
|
||||
};
|
||||
} /*namespace gc*/
|
||||
|
|
|
|||
|
|
@ -40,4 +40,5 @@ xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets
|
|||
# NOTE: dependency set here must be kept consistent with ordinaltree/cmake/xo_ordinaltreeConfig.cmake.in
|
||||
|
||||
# xo-ordinaltree is also header-only
|
||||
xo_headeronly_dependency(${SELF_LIB} xo_allocutil)
|
||||
xo_headeronly_dependency(${SELF_LIB} randomgen)
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(randomgen)
|
||||
find_dependency(xo_allocutil)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
|
|
|
|||
|
|
@ -112,10 +112,10 @@ namespace xo {
|
|||
using allocator_traits = std::allocator_traits<Allocator>;
|
||||
|
||||
using ReducedValue = typename Reduce::value_type;
|
||||
using RbTreeLhs = detail::RedBlackTreeLhs<RedBlackTree<Key, Value, Reduce>>;
|
||||
using RbTreeConstLhs = detail::RedBlackTreeConstLhs<RedBlackTree<Key, Value, Reduce>>;
|
||||
using RbUtil = detail::RbTreeUtil<Key, Value, Reduce>;
|
||||
using RbNode = detail::Node<Key, Value, Reduce>;
|
||||
using RbTreeLhs = detail::RedBlackTreeLhs<RedBlackTree>;
|
||||
using RbTreeConstLhs = detail::RedBlackTreeConstLhs<RedBlackTree>;
|
||||
|
||||
using node_type = RbNode;
|
||||
using node_allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<node_type>;
|
||||
|
|
@ -442,13 +442,14 @@ namespace xo {
|
|||
} /*find_sum_glb*/
|
||||
|
||||
void clear() {
|
||||
auto visitor_fn = [](RbNode const * x, uint32_t /*d*/) {
|
||||
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<RbNode *>(x);
|
||||
|
||||
delete xx;
|
||||
node_allocator_traits::deallocate(node_alloc_, xx, 1);
|
||||
// delete x
|
||||
};
|
||||
|
||||
RbUtil::postorder_node_visitor(this->root_,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "RbTypes.hpp"
|
||||
#include "xo/allocutil/gc_allocator_traits.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace xo {
|
||||
|
|
@ -62,11 +63,13 @@ namespace xo {
|
|||
}
|
||||
} /*make_leaf*/
|
||||
|
||||
#ifdef OBSOLETE
|
||||
static Node * make_leaf(value_type && kv_pair,
|
||||
ReducedValue const & leaf_rv) {
|
||||
return new Node(kv_pair,
|
||||
std::pair<ReducedValue, ReducedValue>(leaf_rv, leaf_rv));
|
||||
} /*make_leaf*/
|
||||
#endif
|
||||
|
||||
/* return #of key/vaue pairs in tree rooted at x. */
|
||||
static size_t tree_size(Node *x) {
|
||||
|
|
|
|||
|
|
@ -779,7 +779,6 @@ namespace xo {
|
|||
* - f=false for existing node (k already in tree before this call)
|
||||
* - n=node containing key k
|
||||
*/
|
||||
template<typename NodeAllocator>
|
||||
static std::pair<bool, RbNode *>
|
||||
insert_aux(NodeAllocator & alloc,
|
||||
value_type const & kv_pair,
|
||||
|
|
@ -885,7 +884,6 @@ namespace xo {
|
|||
* - N has no child nodes
|
||||
* - N->parent() != nullptr
|
||||
*/
|
||||
template <typename NodeAllocator>
|
||||
static void remove_black_leaf(NodeAllocator & alloc,
|
||||
RbNode *N,
|
||||
Reduce const & reduce_fn,
|
||||
|
|
@ -1342,7 +1340,6 @@ namespace xo {
|
|||
*
|
||||
* return true if a node was removed; false otherwise.
|
||||
*/
|
||||
template<typename NodeAllocator>
|
||||
static bool erase_aux(NodeAllocator & alloc,
|
||||
Key const & k,
|
||||
Reduce const & reduce_fn,
|
||||
|
|
@ -1556,7 +1553,6 @@ namespace xo {
|
|||
return true;
|
||||
} /*erase_aux*/
|
||||
|
||||
template <typename NodeAllocator>
|
||||
static void erase_1child_aux(NodeAllocator & alloc,
|
||||
RbNode * N,
|
||||
Reduce const & reduce_fn,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ add_test(NAME ${SELF_EXE} COMMAND ${SELF_EXE})
|
|||
# ----------------------------------------------------------------
|
||||
# internal dependencies: refcnt, ...
|
||||
|
||||
xo_self_dependency(${SELF_EXE} xo_ordinaltree)
|
||||
xo_dependency(${SELF_EXE} refcnt)
|
||||
xo_dependency(${SELF_EXE} indentlog)
|
||||
xo_dependency(${SELF_EXE} randomgen)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue