From 116dfa23065a648ff0cb4e18ba815179f614083d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 3 Apr 2026 18:33:09 -0400 Subject: [PATCH] xo-gc stack: + ACollector.shallow_copy() --- xo-alloc2/idl/Collector.json5 | 34 ++++++++++++++++++- xo-alloc2/include/xo/alloc2/gc/ACollector.hpp | 4 +++ .../include/xo/alloc2/gc/ICollector_Any.hpp | 1 + .../include/xo/alloc2/gc/ICollector_Xfer.hpp | 3 ++ xo-alloc2/include/xo/alloc2/gc/RCollector.hpp | 21 +++++++++++- xo-alloc2/src/alloc2/facet/ICollector_Any.cpp | 6 ++++ .../xo/gc/detail/ICollector_DX1Collector.hpp | 4 +++ .../src/gc/facet/ICollector_DX1Collector.cpp | 5 +++ 8 files changed, 76 insertions(+), 2 deletions(-) diff --git a/xo-alloc2/idl/Collector.json5 b/xo-alloc2/idl/Collector.json5 index d49e6ad6..f7b8d3c8 100644 --- a/xo-alloc2/idl/Collector.json5 +++ b/xo-alloc2/idl/Collector.json5 @@ -267,6 +267,22 @@ noexcept: false, attributes: [], }, + // void alloc_copy(void * src) + { + name: "alloc_copy", + doc: [ + "allocate copy of source object at address @p src.", + "Source must be owned by this collector.", + "Increments object age" + ], + return_type: "void *", + args: [ + {type: "std::byte *", name: "src"}, + ], + const: false, + noexcept: false, + attributes: [], + }, // void forward_inplace(AGCObject * lhs_iface, void ** lhs_data); { name: "forward_inplace", @@ -288,7 +304,23 @@ }, ], router_facet_explicit_content: [ - "/** forward op in place. Defined in GCObject.hpp to avoid #include cycle **/", + "/** convenience template for gc object copy **/", + "template ", + "void * alloc_copy_for(const T * src) noexcept {", + " return O::iface()->alloc_copy(O::data(), (std::byte *)const_cast(src));", + "}", + "", + "/** convenience template for move-constructible T (this is common) **/", + "template ", + "void * std_copy_for(const T * src) noexcept {", + " void * mem = this->alloc_copy_for(src);", + " if (mem) {", + " new (mem) T(std::move(*src));", + " }", + " return (T *)mem;", + "}", + "", + "/** forward faceted object pointer in place. Defined in GCObject.hpp to avoid #include cycle **/", "template ", "void forward_inplace(obj * p_obj);", "", diff --git a/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp b/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp index 93a4152f..c4af2494 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp @@ -119,6 +119,10 @@ Return false if installation fails (e.g. memory exhausted) **/ Require: gc not in progress **/ virtual void assign_member(Opaque data, void * parent, obj * p_lhs, obj & rhs) = 0; + /** allocate copy of source object at address @p src. +Source must be owned by this collector. +Increments object age **/ + virtual void * alloc_copy(Opaque data, std::byte * src) = 0; /** evacuate @p *lhs, that refers to state with interface @p lhs_iface, to collector @p d's to-space. Replace *lhs_data with forwarding pointer diff --git a/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp b/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp index 7d36415d..831aad49 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp @@ -75,6 +75,7 @@ namespace mm { [[noreturn]] void remove_gc_root_poly(Opaque, obj *) override; [[noreturn]] void request_gc(Opaque, Generation) override; [[noreturn]] void assign_member(Opaque, void *, obj *, obj &) override; + [[noreturn]] void * alloc_copy(Opaque, std::byte *) override; [[noreturn]] void forward_inplace(Opaque, AGCObject *, void **) override; ///@} diff --git a/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp b/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp index 5bd9d9c3..8475c4bb 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp @@ -90,6 +90,9 @@ namespace mm { void assign_member(Opaque data, void * parent, obj * p_lhs, obj & rhs) override { return I::assign_member(_dcast(data), parent, p_lhs, rhs); } + void * alloc_copy(Opaque data, std::byte * src) override { + return I::alloc_copy(_dcast(data), src); + } void forward_inplace(Opaque data, AGCObject * lhs_iface, void ** lhs_data) override { return I::forward_inplace(_dcast(data), lhs_iface, lhs_data); } diff --git a/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp b/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp index 4580f059..4bf2ad1e 100644 --- a/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp @@ -47,7 +47,23 @@ public: ///@{ // explicit injected content - /** forward op in place. Defined in GCObject.hpp to avoid #include cycle **/ + /** convenience template for gc object copy **/ + template + void * alloc_copy_for(const T * src) noexcept { + return O::iface()->alloc_copy(O::data(), (std::byte *)const_cast(src)); + } + + /** convenience template for move-constructible T (this is common) **/ + template + void * std_copy_for(const T * src) noexcept { + void * mem = this->alloc_copy_for(src); + if (mem) { + new (mem) T(std::move(*src)); + } + return (T *)mem; + } + + /** forward faceted object pointer in place. Defined in GCObject.hpp to avoid #include cycle **/ template void forward_inplace(obj * p_obj); @@ -123,6 +139,9 @@ public: void assign_member(void * parent, obj * p_lhs, obj & rhs) { return O::iface()->assign_member(O::data(), parent, p_lhs, rhs); } + void * alloc_copy(std::byte * src) { + return O::iface()->alloc_copy(O::data(), src); + } void forward_inplace(AGCObject * lhs_iface, void ** lhs_data) { return O::iface()->forward_inplace(O::data(), lhs_iface, lhs_data); } diff --git a/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp b/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp index aa244092..269d95bb 100644 --- a/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp +++ b/xo-alloc2/src/alloc2/facet/ICollector_Any.cpp @@ -65,6 +65,12 @@ ICollector_Any::assign_member(Opaque, void *, obj *, obj & _fatal(); } +auto +ICollector_Any::alloc_copy(Opaque, std::byte *) -> void * +{ + _fatal(); +} + auto ICollector_Any::forward_inplace(Opaque, AGCObject *, void **) -> void { diff --git a/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp b/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp index 1ae5fac0..6f203e77 100644 --- a/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp +++ b/xo-gc/include/xo/gc/detail/ICollector_DX1Collector.hpp @@ -107,6 +107,10 @@ Return false if installation fails (e.g. memory exhausted) **/ Require: gc not in progress **/ static void assign_member(DX1Collector & self, void * parent, obj * p_lhs, obj & rhs); + /** allocate copy of source object at address @p src. +Source must be owned by this collector. +Increments object age **/ + static void * alloc_copy(DX1Collector & self, std::byte * src); /** evacuate @p *lhs, that refers to state with interface @p lhs_iface, to collector @p d's to-space. Replace *lhs_data with forwarding pointer diff --git a/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp b/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp index 33a28aa9..261deaa0 100644 --- a/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp +++ b/xo-gc/src/gc/facet/ICollector_DX1Collector.cpp @@ -95,6 +95,11 @@ namespace xo { self.assign_member(parent, p_lhs, rhs); } auto + ICollector_DX1Collector::alloc_copy(DX1Collector & self, std::byte * src) -> void * + { + return self.alloc_copy(src); + } + auto ICollector_DX1Collector::forward_inplace(DX1Collector & self, AGCObject * lhs_iface, void ** lhs_data) -> void { self.forward_inplace(lhs_iface, lhs_data);