diff --git a/xo-gc/src/gc/DX1Collector.cpp b/xo-gc/src/gc/DX1Collector.cpp index cc7eb051..e5f951f2 100644 --- a/xo-gc/src/gc/DX1Collector.cpp +++ b/xo-gc/src/gc/DX1Collector.cpp @@ -293,6 +293,9 @@ namespace xo { log && log("step 1 : swap from/to roles"); this->swap_roles(upto); + log && log(xtag("from_0", get_space(role::from_space(), generation{0})->lo_), + xtag("to_0", get_space(role::to_space(), generation{0})->lo_)); + log && log("step 2a : copy roots"); this->copy_roots(upto); @@ -305,7 +308,11 @@ namespace xo { void DX1Collector::swap_roles(generation upto) noexcept { + scope log(XO_DEBUG(true), xtag("upto", upto)); + for (generation g = generation{0}; g < upto; ++g) { + log && log("swap roles", xtag("g", g)); + std::swap(space_[role::to_space()][g], space_[role::from_space()][g]); } } @@ -345,6 +352,7 @@ namespace xo { * * Coordinates with forward_inplace() */ + log && log("disposition: already forwarded"); return *(void **)from_src; } @@ -353,6 +361,8 @@ namespace xo { if (!this->check_move_policy(hdr, from_src)) { /* object at from_src in generation that is not being collected */ + log && log("disposition: not moving from_src"); + return from_src; } @@ -418,6 +428,8 @@ namespace xo { * **/ + log && log("disposition: move subtree"); + /* TODO: AllocIterator pointing to free pointer */ std::array gray_lo_v; { @@ -462,16 +474,24 @@ namespace xo { } } while (fixup_work > 0); + log && log(xtag("to_dest", to_dest)); + return to_dest; } void DX1Collector::copy_roots(generation upto) noexcept { + scope log(XO_DEBUG(true)); + for (obj ** p_root = (obj **)roots_.lo_; p_root < (obj **)roots_.free_; ++p_root) { + log && log("copy root", xtag("**p_root.data.pre", (**p_root).data_)); + (*p_root)->reset_opaque(this->deep_move((*p_root)->data_, upto)); + + log && log(xtag("**p_root.data.post", (**p_root).data_)); } } @@ -479,6 +499,10 @@ namespace xo { DX1Collector::forward_inplace(AGCObject * lhs_iface, void ** lhs_data) { + scope log(XO_DEBUG(config_.debug_flag_), + xtag("lhs_data", lhs_data), + xtag("*lhs_data", *lhs_data)); + /* coordinates with DX1Collector::_deep_move() */ (void)lhs_iface; @@ -497,28 +521,31 @@ namespace xo { * +----------+ */ - void * object_data = (std::byte *)lhs_data; + void * object_data = (std::byte *)*lhs_data; if (!this->contains(role::from_space(), object_data)) { - /* *lhs isn't in GC-allocated space. + /* *lhs_data either: + * 1. already in to-space + * 2. not in GC-allocated space at all + * (small number of niche examples of this) * - * This happens for a modest number of global - * constants, for example DBoolean {true, false}. - * - * It's important we recognize these up front. + * It's important we recognize case (2) up front. * Since not allocated from GC, they don't have * an alloc-header. */ + log && log("disposition: not in from-space"); + return; } + log && log("disposition: in from-space"); + /** NOTE: for form's sake: - * better to lookup actual arena that + * lookup actual arena that * allocated object data. * Only using this to get alloc header - * **/ - DArena * some_arena = this->to_space(generation(0)); + DArena * some_arena = this->from_space(generation(0)); DArena::header_type * p_header = some_arena->obj2hdr(object_data); @@ -528,37 +555,96 @@ namespace xo { /* recover allocation size */ std::size_t alloc_z = some_arena->config_.header_.size_with_padding(alloc_hdr); + log && log(xtag("some_arena.lo", some_arena->lo_), + xtag("p_header", p_header), + xtag("alloc_z", alloc_z)); + /* need to be able to fit forwarding pointer * in place of forwarded object. * * This is guaranteed anyway, by alignment rules */ - assert(alloc_z > sizeof(uintptr_t)); + assert(alloc_z >= sizeof(uintptr_t)); if (this->is_forwarding_header(alloc_hdr)) { - /* *lhs already refers to a forwarding pointer */ + /* *lhs_data already refers to a forwarding pointer */ /* - * lhs obj + * lhs obj (from-space) * | +---------+ +---+-+----+ * \--->| .iface | |FWD|G|size| alloc_hdr * +---------+ object_data +---+-+----+ - * | .data x----------------->| x--------> - * +---------+ | | dest + * | .data x----------------->| x--------\ + * +---------+ | | | dest + * | | | + * +----------+ | + * | + * (to-space) | + * +---+-+----+ | + * |FWD|G|size|<--/ + * +---+-+----+ + * | | + * | | * | | * +----------+ */ void * dest = *(void**)object_data; - /* update *lhs in-place */ *lhs_data = dest; + /* + * lhs obj + * | +---------+ + * \--->| .iface | + * +---------+ + * | .data x------------\ + * +---------+ | + * | dest + * | + * | + * | (to-space) + * | +---+-+----+ + * \---->|FWD|G|size| + * +---+-+----+ + * | | + * | | + * | | + * +----------+ + */ } else if (this->check_move_policy(alloc_hdr, object_data)) { /* copy object *lhs + replace with forwarding pointer */ - /* which arena are we writing to? need allocator interface */ + /* + * lhs obj (from-space) + * | +---------+ +---+-+----+ + * \--->| .iface | |FWD|G|size| alloc_hdr + * +---------+ object_data +---+-+----+ + * | .data x----------------->| | + * +---------+ | | + * | | + * +----------+ + */ *lhs_data = this->shallow_move(lhs_iface, *lhs_data); + /* + * lhs obj (from-space) + * | +---------+ +---+-+----+ + * \--->| .iface | |FWD|G|SIZE| + * +---------+ +---+-+----+ + * | .data x------------\ | x--------\ + * +---------+ | | | | + * | | | | + * dest | +----------+ | + * | | + * | (to-space) | + * | +---+-+----+ | + * \---->|FWD|G|size|<--/ + * +---+-+----+ + * | | + * | | + * | | + * +----------+ + */ } else { /* object doesn't need to move. * e.g. incremental collection + object is tenured @@ -569,11 +655,15 @@ namespace xo { void * DX1Collector::shallow_move(const AGCObject * iface, void * from_src) { + scope log(XO_DEBUG(config_.debug_flag_)); + AllocInfo info = this->alloc_info((std::byte *)from_src); obj alloc(this); void * to_dest = iface->shallow_copy(from_src, alloc); + log && log(xtag("from_src", from_src), xtag("to_dest", to_dest)); + if(to_dest == from_src) { assert(false); } else { @@ -699,7 +789,7 @@ namespace xo { DX1Collector::reverse_roles(generation g) noexcept { assert(g < config_.n_generation_); - std::swap(space_[0][g], space_[1][g]); + std::swap(space_[role::from_space()][g], space_[role::to_space()][g]); } void diff --git a/xo-object2/src/object2/DList.cpp b/xo-object2/src/object2/DList.cpp index d95f0bd0..a30aef82 100644 --- a/xo-object2/src/object2/DList.cpp +++ b/xo-object2/src/object2/DList.cpp @@ -41,7 +41,7 @@ namespace xo { bool DList::is_empty() const noexcept { - return this != &s_null; + return this == &s_null; } auto diff --git a/xo-object2/src/object2/IGCObject_DList.cpp b/xo-object2/src/object2/IGCObject_DList.cpp index 2139d682..af7353f3 100644 --- a/xo-object2/src/object2/IGCObject_DList.cpp +++ b/xo-object2/src/object2/IGCObject_DList.cpp @@ -4,6 +4,7 @@ **/ #include "IGCObject_DList.hpp" +#include namespace xo { using xo::mm::AGCObject; @@ -36,6 +37,8 @@ namespace xo { IGCObject_DList::forward_children(DList & src, obj gc) noexcept { + scope log(XO_DEBUG(true)); + gc.forward_inplace(src.head_.iface(), (void **)&(src.head_.data_)); auto iface = xo::facet::impl_for(); diff --git a/xo-object2/utest/X1Collector.test.cpp b/xo-object2/utest/X1Collector.test.cpp index b15a0c56..1174635c 100644 --- a/xo-object2/utest/X1Collector.test.cpp +++ b/xo-object2/utest/X1Collector.test.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + #include namespace ut { @@ -39,6 +42,8 @@ namespace ut { using xo::mm::padding; using xo::facet::with_facet; using xo::facet::typeseq; + using xo::scope; + using xo::xtag; namespace { struct testcase_x1 { @@ -69,6 +74,9 @@ namespace ut { TEST_CASE("x1", "[gc][x1]") { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + 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]; @@ -82,6 +90,7 @@ namespace ut { .gc_trigger_v_{{ tc.incr_gc_threshold_, tc.full_gc_threshold_}}, + .debug_flag_ = c_debug_flag, }; DX1Collector gc(cfg); @@ -140,6 +149,8 @@ namespace ut { REQUIRE(gc.reserved_total() == otypes->reserved() + roots->reserved() + 4 * from_0->reserved()); + + log && log(xtag("from_0", from_0->lo_), xtag("to_0", to_0->lo_)); } /* attempt allocation */ @@ -197,11 +208,22 @@ namespace ut { } } - - /* no GC roots, so GC is trivial */ c_o.request_gc(generation{1}); + log && log(xtag("l0_o.data()", l0_o.data())); + log && log(xtag("l0_o.data()->head_.data()", l0_o.data()->head_.data())); + log && log(xtag("x0_o.data()", x0_o.data())); + + REQUIRE(!gc.contains(role::from_space(), x0_o.data())); + REQUIRE(gc.contains(role::to_space(), x0_o.data())); + REQUIRE(x0_o.data()->value() == 3.1415927); + REQUIRE(!gc.contains(role::from_space(), l0_o.data())); + REQUIRE(gc.contains(role::to_space(), l0_o.data())); + REQUIRE(l0_o.data()->is_empty() == false); + + REQUIRE((void*)l0_o.data()->head_.data() == (void*)x0_o.data()); + } catch (std::exception & ex) { std::cerr << "caught exception: " << ex.what() << std::endl; REQUIRE(false);