diff --git a/include/xo/alloc/ArenaAlloc.hpp b/include/xo/alloc/ArenaAlloc.hpp index 1f48121b..df4d2a01 100644 --- a/include/xo/alloc/ArenaAlloc.hpp +++ b/include/xo/alloc/ArenaAlloc.hpp @@ -177,7 +177,6 @@ namespace xo { virtual std::byte * alloc(std::size_t z) final override; virtual bool check_owned(IObject * src) const final override; - ArenaAlloc & operator=(const ArenaAlloc &) = delete; ArenaAlloc & operator=(ArenaAlloc &&) = delete; diff --git a/include/xo/alloc/GC.hpp b/include/xo/alloc/GC.hpp index 214027b7..e5dd6781 100644 --- a/include/xo/alloc/GC.hpp +++ b/include/xo/alloc/GC.hpp @@ -337,6 +337,12 @@ namespace xo { * - incr GC -> if not tenured **/ virtual bool check_move(IObject * src) const final override; + /** if src is cross-generational (or cross-checkpoint), verify that it + * is recorded in mutation log, + * given an object @p parent that contains object pointer @p lhs + **/ + virtual bool check_write_barrier(IObject * parent, IObject ** lhs, bool may_throw) const final; + virtual std::byte * alloc(std::size_t z) final override; virtual std::byte * alloc_gc_copy(std::size_t z, const void * src) final override; @@ -447,7 +453,15 @@ namespace xo { std::vector gc_root_v_; /** log cross-generational and cross-checkpoint mutations. - * These need to be adjusted on next incremental collection + * These need to be adjusted on next incremental collection. + * + * mutation_log_[tospace] accumulates {xgen,xckp} pointers until + * the next GC. + * + * See GC aux functions + * @ref incremental_gc_forward_mlog + * @ref full_gc_forward_mlog + * **/ std::array, role2int(role::N)> mutation_log_; /** temporary mutation log (for deferred entries) **/ diff --git a/include/xo/alloc/generation.hpp b/include/xo/alloc/generation.hpp index 0acd943a..9f122044 100644 --- a/include/xo/alloc/generation.hpp +++ b/include/xo/alloc/generation.hpp @@ -41,6 +41,13 @@ namespace xo { return generation::tenured; } + const char * genresult2str(generation_result x); + + inline std::ostream & operator<<(std::ostream & os, generation_result x) { + os << genresult2str(x); + return os; + } + } /*namespace gc*/ } /*namespace xo*/ diff --git a/src/alloc/GC.cpp b/src/alloc/GC.cpp index 40234bf3..5cfdd9fc 100644 --- a/src/alloc/GC.cpp +++ b/src/alloc/GC.cpp @@ -587,6 +587,104 @@ namespace xo { || (this->tospace_generation_of(src) != gc::generation_result::tenured)); } + bool + GC::check_write_barrier(IObject * parent, + IObject ** lhs, + bool may_throw_flag) const + { + if (!this->contains(parent)) { + if (may_throw_flag) { + throw std::runtime_error(tostr("GC::check_write_barrier", + ": expected parent object P in GC to-space", + xtag("P", parent))); + } + return false; + } + + std::size_t parent_z = parent->_shallow_size(); + + std::byte * parent_addr = reinterpret_cast(parent); + std::byte * lhs_addr = reinterpret_cast(lhs); + + if ((lhs_addr < parent_addr) || (parent_addr + parent_z < lhs_addr)) { + if (may_throw_flag) { + throw std::runtime_error + (tostr("GC::check_write_barrier", + ": expected lhs address L within address extent z of parent P", + xtag("P", parent), xtag("z", parent_z), + xtag("P+z", parent_addr + parent_z), + xtag("L", lhs))); + } + return false; + } + + IObject * rhs = *lhs; + + auto parent_gen = tospace_generation_of(parent); + auto rhs_gen = tospace_generation_of(rhs); + + switch(parent_gen) { + case generation_result::nursery: + if (is_before_checkpoint(parent)) { + switch(rhs_gen) { + case generation_result::nursery: + if (is_before_checkpoint(rhs)) { + /* no mlog entry needed */ + return true; + } else { + /* need to check mlog */ + ; + } + break; + case generation_result::tenured: + /* no mlog entry needed */ + return true; + case generation_result::not_found: + /* possible non-gc rhs */ + return true; + } + } else { + /* no mlog entry needed */ + return true; + } + break; + case generation_result::tenured: + switch(rhs_gen) { + case generation_result::nursery: + /* need to check mlog */ + break; + case generation_result::tenured: + /* no mlog entry needed */ + return true; + case generation_result::not_found: + /* possible non-gc rhs */ + return true; + } + case generation_result::not_found: + /* already excluded -> impossible */ + assert(false && "already verified parent owned by GC"); + } + + /* control here -> expect mutation log entry. + * search mutation log + verify such entry exists + */ + for (MutationLogEntry & mlog : *(mutation_log_[role2int(role::to_space)])) { + if ((mlog.parent() == parent) && (mlog.lhs() == lhs)) { + return true; + } + mlog.lhs(); + } + + if (may_throw_flag) { + throw std::runtime_error + (tostr("GC::check_write_barrier", + ": expected mlog entry for xgen pointer L->C within parent P", + xtag("P", parent), xtag("L", lhs), xtag("C", rhs), + xtag("gen(P)", parent_gen), xtag("gen(C)", rhs_gen))); + } + return false; + } + void GC::swap_nursery() { @@ -1126,9 +1224,11 @@ namespace xo { scope log(XO_DEBUG(config_.debug_flag_)); if (upto == generation::tenured) { - this->full_gc_forward_mlog(&object_statistics_sae_[gen2int(generation::tenured)]); + this->full_gc_forward_mlog + (&object_statistics_sae_[gen2int(generation::tenured)]); } else { - this->incremental_gc_forward_mlog(&object_statistics_sae_[gen2int(generation::nursery)]); + this->incremental_gc_forward_mlog + (&object_statistics_sae_[gen2int(generation::nursery)]); } } diff --git a/src/alloc/generation.cpp b/src/alloc/generation.cpp index 54c151ac..a0ae9e65 100644 --- a/src/alloc/generation.cpp +++ b/src/alloc/generation.cpp @@ -16,6 +16,15 @@ namespace xo { return "?generation"; } + const char * genresult2str(generation_result x) { + switch (x) { + case generation_result::nursery: return "nursery"; + case generation_result::tenured: return "tenured"; + case generation_result::not_found: return "not-found"; + } + return "?generation_result"; + } + } /*namespace gc*/ } /*namespace xo*/