xo-alloc / xo-object: utest coverage + assorted bugfixes

This commit is contained in:
Roland Conybeare 2025-08-07 18:32:14 -05:00
commit bd00826448
34 changed files with 1069 additions and 326 deletions

View file

@ -13,36 +13,19 @@
namespace xo {
namespace gc {
ArenaAlloc::ArenaAlloc(const std::string & name,
#ifdef REDLINE_MEMORY
std::size_t rz,
#endif
std::size_t z, bool debug_flag)
{
this->name_ = name;
#ifdef REDLINE_MEMORY
this->lo_ = (new std::byte [rz + z]);
#else
this->lo_ = (new std::byte [z]);
#endif
this->checkpoint_ = lo_;
this->free_ptr_ = lo_;
this->limit_ = lo_ + z;
#ifdef REDLINE_MEMORY
this->redline_z_ = rz;
this->hi_ = limit_ + rz;
#else
this->hi_ = limit_;
#endif
this->debug_flag_ = debug_flag;
if (!lo_) {
#ifdef REDLINE_MEMORY
throw std::runtime_error(tostr("ArenaAlloc: allocation failed",
xtag("size", rz + z)));
#else
throw std::runtime_error(tostr("ArenaAlloc: allocation failed",
xtag("size", z)));
#endif
}
}
@ -56,24 +39,15 @@ namespace xo {
this->checkpoint_ = nullptr;
this->free_ptr_ = nullptr;
this->limit_ = nullptr;
#ifdef REDLINE_MEMORY
this->redline_z_ = 0;
#endif
this->hi_ = nullptr;
this->debug_flag_ = false;
}
up<ArenaAlloc>
ArenaAlloc::make(const std::string & name,
#ifdef REDLINE_MEMORY
std::size_t rz,
#endif
std::size_t z, bool debug_flag)
{
return up<ArenaAlloc>(new ArenaAlloc(name,
#ifdef REDLINE_MEMORY
rz,
#endif
z, debug_flag));
}
@ -97,22 +71,36 @@ namespace xo {
ArenaAlloc::capture_object_statistics(capture_phase phase,
ObjectStatistics * p_dest) const
{
scope log(XO_DEBUG(debug_flag_),
xtag("name", name_),
xtag("capacity", limit_ - lo_),
xtag("alloc", free_ptr_ - lo_),
xtag("lo", (void*)lo_),
xtag("free_ptr", (void*)free_ptr_));
using xo::reflect::TaggedPtr;
std::byte * p = lo_;
while (p < free_ptr_) {
Object * obj = reinterpret_cast<Object *>(p);
TaggedPtr tp = obj->self_tp();
std::size_t z = obj->_shallow_size();
Object * obj = reinterpret_cast<Object *>(p);
TaggedPtr tp = obj->self_tp();
std::size_t z = obj->_shallow_size();
std::uint32_t id = tp.td()->id().id();
log && log(xtag("obj", (void*)obj),
xtag("z", z),
xtag("typeid", id));
if (p_dest->per_type_stats_v_.size() < id + 1)
p_dest->per_type_stats_v_.resize(id + 1);
PerObjectTypeStatistics & dest = p_dest->per_type_stats_v_.at(id);
dest.td_ = tp.td();
log && log(xtag("td", tp.td()->short_name()));
switch (phase) {
case capture_phase::sab:
++dest.scanned_n_;
@ -130,6 +118,11 @@ namespace xo {
assert(p == free_ptr_);
}
const std::string &
ArenaAlloc::name() const {
return name_;
}
std::size_t
ArenaAlloc::size() const {
return limit_ - lo_;
@ -167,15 +160,17 @@ namespace xo {
return free_ptr_ - checkpoint_;
}
bool
ArenaAlloc::debug_flag() const
{
return debug_flag_;
}
void
ArenaAlloc::clear()
{
this->set_free_ptr(lo_);
#ifdef REDLINE_MEMORY
this->limit_ = hi_ - redline_z_;
#else
this->limit_ = hi_;
#endif
}
void
@ -204,7 +199,7 @@ namespace xo {
std::byte * retval = this->free_ptr_;
log && log(xtag("self", name_), xtag("z0", z0), xtag("+pad", dz), xtag("z1", z1));
log && log(xtag("self", name_), xtag("z0", z0), xtag("+pad", dz), xtag("z1", z1), xtag("avail", this->available()));
if (free_ptr_ + z1 > limit_) {
return nullptr;
@ -215,13 +210,6 @@ namespace xo {
return retval;
}
#ifdef REDLINE_MEMORY
void
ArenaAlloc::release_redline_memory() {
this->limit_ = this->hi_;
}
#endif
} /*namespace gc*/
} /*namespace xo*/

View file

@ -10,6 +10,7 @@ set(SELF_SRCS
ObjectStatistics.cpp
Object.cpp
Forwarding1.cpp
generation.cpp
)
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})

View file

@ -26,7 +26,7 @@ namespace xo {
void
Forwarding1::display(std::ostream & os) const
{
os << "<fwd" << xtag("dest", (void*)dest_.ptr()) << ">";
os << "<fwd" << xtag("dest-td", dest_->self_tp().td()->short_name()) << ">";
}
Object *

View file

@ -175,7 +175,8 @@ namespace xo {
GcStatisticsExt retval = GcStatisticsExt(this->native_gc_statistics());
retval.nursery_z_ = nursery_[role2int(role::to_space)]->size();
retval.nursery_before_checkpoint_z_ = nursery_[role2int(role::to_space)]->before_checkpoint();
retval.nursery_before_checkpoint_z_ = this->nursery_to()->before_checkpoint();
retval.nursery_after_checkpoint_z_ = this->nursery_to()->after_checkpoint();
retval.tenured_z_ = tenured_[role2int(role::to_space)]->size();
return retval;
@ -254,21 +255,9 @@ namespace xo {
{
std::byte * x = nursery_[role2int(role::to_space)]->alloc(z);
if (!x) {
/* ListAlloc won't fail -- instead will increase heap size */
/* ListAlloc won't fail unless we exhaust memory -- instead will increase heap size */
this->request_gc(generation::nursery);
#ifdef REDLINE_MEMORY
if (incr_gc_pending_ || full_gc_pending_)
nursery_[role2int(role::to_space)]->release_redline_memory();
/* try (just once) more, maybe request fits in redline space */
x = nursery_[role2int(role::to_space)]->alloc(z);
#endif
assert(x);
}
assert(x);
return x;
}
@ -308,18 +297,6 @@ namespace xo {
log && log("nursery");
retval = nursery_[role2int(role::to_space)]->alloc(z);
if (!retval) {
/* nursery space exhausted !? */
this->request_gc(generation::nursery);
#ifdef REDLINE_MEMORY
nursery_[role2int(role::to_space)]->release_redline_memory();
#endif
retval = nursery_[role2int(role::to_space)]->alloc(z);
}
}
}
break;
@ -393,14 +370,6 @@ namespace xo {
}
}
#ifdef REDLINE_MEMORY
void
GC::release_redline_memory()
{
// not supported feature for GC
}
#endif
void
GC::swap_nursery()
{
@ -428,40 +397,62 @@ namespace xo {
void
GC::swap_spaces(generation target)
{
scope log(XO_DEBUG(this->debug_flag()));
scope log(XO_DEBUG(this->debug_flag()), xtag("upto", target));
// will be copying into the memory regions currently labelled FromSpace
/* gc will copy some to-be-determined amount in [0..promote_z]
from nursery->tenured generation.
*/
std::size_t promote_z = nursery_[role2int(role::to_space)]->before_checkpoint();
std::size_t max_promote_z = nursery_[role2int(role::to_space)]->before_checkpoint();
log && log(xtag("max_promote_z", max_promote_z));
if (target == generation::tenured) {
/* gc on tenured generation may need this much space */
std::size_t tenured_z = (tenured_[role2int(role::to_space)]->allocated()
+ promote_z
+ full_gc_threshold_);
std::size_t need_tenured_z = (tenured_[role2int(role::to_space)]->allocated()
+ max_promote_z
+ full_gc_threshold_);
tenured_[role2int(role::from_space)]->reset(tenured_z);
log && log("need_tenured_z", need_tenured_z);
tenured_from()->reset(need_tenured_z);
this->swap_tenured();
} else {
if (tenured_[role2int(role::to_space)]->available() < promote_z) {
tenured_[role2int(role::to_space)]->expand(promote_z);
std::size_t avail_tenured_z = tenured_[role2int(role::to_space)]->available();
log && log(xtag("avail_tenured_z", avail_tenured_z));
if (avail_tenured_z < max_promote_z) {
ListAlloc * tenured_to = this->tenured_to();
tenured_to->expand(max_promote_z, tenured_to->name() + "+");
}
}
nursery_[role2int(role::from_space)]->reset(nursery_[role2int(role::to_space)]->allocated()
- promote_z
+ incr_gc_threshold_);
/* subtracting max_promote_z is correct here, since anything not promoted is garbage */
std::size_t need_nursery_z = (nursery(role::to_space)->allocated()
- max_promote_z
+ incr_gc_threshold_);
log && log(xtag("need_nursery_z", need_nursery_z));
/* (from-space is about to become to-space, to receive surviving nursery objects) */
nursery(role::from_space)->reset(need_nursery_z);
this->swap_nursery();
this->swap_mutation_log();
log && log(xtag("nursery.from", nursery_[role2int(role::from_space)]->name()));
log && log(xtag("nursery.to", nursery_[role2int(role::to_space) ]->name()));
log && log(xtag("tenured.from", tenured_[role2int(role::from_space)]->name()));
log && log(xtag("tenured.to", tenured_[role2int(role::to_space) ]->name()));
ListAlloc * N_from = nursery(role::from_space);
log && log(xtag("nursery.from", N_from->name()), xtag("size", N_from->size()));
ListAlloc * N_to = nursery(role::to_space);
log && log(xtag("nursery.to", N_to->name()), xtag("size", N_to->size()));
ListAlloc * T_from = tenured(role::from_space);
log && log(xtag("tenured.from", T_from->name()), xtag("size", T_from->size()));
ListAlloc * T_to = tenured(role::to_space);
log && log(xtag("tenured.to", T_to->name()), xtag("size", T_to->size()));
} /*swap_spaces*/
@ -504,8 +495,12 @@ namespace xo {
void
GC::copy_globals(generation upto)
{
scope log(XO_DEBUG(config_.debug_flag_),
xtag("roots", gc_root_v_.size()));
for (Object ** pp_root : gc_root_v_) {
this->copy_object(pp_root, upto, &object_statistics_sae_[gen2int(upto)]);
this->copy_object(pp_root, upto,
&object_statistics_sae_[gen2int(upto)]);
}
}
@ -750,28 +745,30 @@ namespace xo {
{
scope log(XO_DEBUG(config_.debug_flag_));
std::size_t N_allocated = nursery_[role2int(role::from_space)]->after_checkpoint();
std::size_t T_allocated = tenured_[role2int(role::from_space)]->after_checkpoint();
std::size_t N_allocated = nursery_from()->after_checkpoint();
std::size_t T_allocated = tenured_from()->after_checkpoint();
std::size_t N_before_gc = nursery_[role2int(role::from_space)]->allocated();
std::size_t T_before_gc = tenured_[role2int(role::from_space)]->allocated();
std::size_t N_before_gc = nursery_from()->allocated();
std::size_t T_before_gc = tenured_from()->allocated();
std::size_t N_after_gc = nursery_[role2int(role::to_space)]->allocated();
std::size_t T_after_gc = tenured_[role2int(role::to_space)]->allocated();
std::size_t N_after_gc = nursery_to()->allocated();
std::size_t T_after_gc = tenured_to()->allocated();
//std::byte * N_free_ptr = nursery_[role2int(role::to_space)]->free_ptr();
std::size_t promote_z = gc_statistics_.total_promoted_ - gc_statistics_.total_promoted_sab_;
std::size_t promote_z = (gc_statistics_.total_promoted_
- gc_statistics_.total_promoted_sab_);
this->nursery_[role2int(role::from_space)]->reset(0);
this->tenured_[role2int(role::from_space)]->reset(0);
/* Don't reset from-space here, it's unnecessary.
* Would be permissible, but interferes with GC object modelling in
* xo-object/utest/GC.test.cpp
*/
//this->nursery_[role2int(role::from_space)]->reset(0);
//this->tenured_[role2int(role::from_space)]->reset(0);
/* objects currenty in to-space nursery have survived one collection */
this->nursery_[role2int(role::to_space)]->checkpoint();
// nursery_[role2int(role::to_space)]->set_redline(nursery_[role2int(role::to_space)]->allocated() + incr_gc_threshold_)
this->nursery_to()->checkpoint();
if (upto == generation::tenured)
this->tenured_[role2int(role::to_space)]->checkpoint();
this->tenured_to()->checkpoint();
if (log) {
log(xtag("N_allocated", N_allocated));
@ -819,7 +816,7 @@ namespace xo {
this->capture_object_statistics(upto, capture_phase::sab);
log && log("step 1 : swap to/from roles");
log && log("step 1 : swap to/from roles");
this->swap_spaces(upto);
@ -829,15 +826,15 @@ namespace xo {
log && log("step 2b: TODO: copy pinned");
log && log("step 3 : forward mutation log");
log && log("step 3 : forward mutation log");
this->forward_mutation_log(upto);
log && log("step 4 : TODO: notify destructor log");
log && log("step 4 : TODO: notify destructor log");
log && log("step 5 : TODO: keep reachable weak pointers");
log && log("step 5 : TODO: keep reachable weak pointers");
log && log("step 6 : cleanup");
log && log("step 6 : cleanup");
this->cleanup_phase(upto);

View file

@ -32,12 +32,12 @@ namespace xo {
PerGenerationStatistics::display(std::ostream & os) const
{
os << "<PerGenerationStatistics"
<< rtag("used", used_z_)
<< rtag("n_gc", n_gc_)
<< rtag("new_alloc_z", new_alloc_z_)
<< rtag("scanned_z", scanned_z_)
<< rtag("survive_z", survive_z_)
<< rtag("promote_z", promote_z_)
<< xrtag("used", used_z_)
<< xrtag("n_gc", n_gc_)
<< xrtag("new_alloc_z", new_alloc_z_)
<< xrtag("scanned_z", scanned_z_)
<< xrtag("survive_z", survive_z_)
<< xrtag("promote_z", promote_z_)
<< ">";
}
@ -62,9 +62,9 @@ namespace xo {
GcStatistics::display(std::ostream & os) const
{
os << "<GcStatistics"
<< rtag("gen_v", gen_v_)
<< rtag("total_allocated", total_allocated_)
<< rtag("total_promoted_sab", total_promoted_sab_)
<< xrtag("gen_v", gen_v_)
<< xrtag("total_allocated", total_allocated_)
<< xrtag("total_promoted_sab", total_promoted_sab_)
// total_promoted
// n_mtuation
// n_logged_mutation
@ -78,17 +78,17 @@ namespace xo {
GcStatisticsExt::display(std::ostream & os) const
{
os << "<GcStatisticsExt"
<< rtag("gen_v", gen_v_)
<< rtag("total_allocated", total_allocated_)
<< rtag("total_promoted_sab", total_promoted_)
<< rtag("nursery_z", nursery_z_)
<< rtag("nursery_before_ckp_z", nursery_before_checkpoint_z_)
<< rtag("nursery_after_ckp_z", nursery_after_checkpoint_z_)
<< rtag("tenured_z", tenured_z_)
<< rtag("n_mutation", n_mutation_)
<< rtag("n_logged_mutation", n_logged_mutation_)
<< rtag("n_xgen_mutation", n_xgen_mutation_)
<< rtag("n_xkcp_mutation", n_xckp_mutation_)
<< xrtag("gen_v", gen_v_)
<< xrtag("total_allocated", total_allocated_)
<< xrtag("total_promoted_sab", total_promoted_)
<< xrtag("nursery_z", nursery_z_)
<< xrtag("nursery_before_ckp_z", nursery_before_checkpoint_z_)
<< xrtag("nursery_after_ckp_z", nursery_after_checkpoint_z_)
<< xrtag("tenured_z", tenured_z_)
<< xrtag("n_mutation", n_mutation_)
<< xrtag("n_logged_mutation", n_logged_mutation_)
<< xrtag("n_xgen_mutation", n_xgen_mutation_)
<< xrtag("n_xckp_mutation", n_xckp_mutation_)
// << xtag("per_type_stats", per_type_stats_)
<< ">";
}

View file

@ -5,6 +5,7 @@
#include "ListAlloc.hpp"
#include "ArenaAlloc.hpp"
#include "xo/indentlog/scope.hpp"
#include <cassert>
#include <cstddef>
@ -13,9 +14,6 @@ namespace xo {
ListAlloc::ListAlloc(std::unique_ptr<ArenaAlloc> hd,
ArenaAlloc * marked,
std::size_t cz, std::size_t nz, std::size_t tz,
#ifdef REDLINE_MEMORY
bool use_redline,
#endif
bool debug_flag)
: start_z_{cz},
hd_{std::move(hd)},
@ -24,9 +22,6 @@ namespace xo {
current_z_{cz},
next_z_{nz},
total_z_{tz},
#ifdef REDLINE_MEMOORY
use_redline_{use_redline},
#endif
debug_flag_{debug_flag}
{}
@ -39,9 +34,6 @@ namespace xo {
ListAlloc::make(const std::string & name, std::size_t cz, std::size_t nz, bool debug_flag)
{
std::unique_ptr<ArenaAlloc> hd{ArenaAlloc::make(name,
#ifdef REDLINE_MEMORY
0,
#endif
cz, debug_flag)};
if (!hd)
@ -52,9 +44,6 @@ namespace xo {
up<ListAlloc> retval{new ListAlloc(std::move(hd),
marked,
cz, nz, cz,
#ifdef REDLINE_MEMORY
false /*!use_redline*/,
#endif
debug_flag)};
return retval;
@ -134,34 +123,31 @@ namespace xo {
bool
ListAlloc::is_before_checkpoint(const void * x) const {
if (!marked_)
return false;
return true;
if ((marked_ == hd_.get()) && hd_->contains(x))
return hd_->is_before_checkpoint(x);
if (marked_ && marked_->contains(x))
return marked_->is_before_checkpoint(x);
/*
* 1. allocs in full_l_ appear in youngest-to-oldest order
* 2. allocators that appear before marked_ in full_l_ count as 'after checkpoint'
* 3. allocators that appear after marked_ in full_l_ count as 'before checkpoint'
* 1. allocs in full_l_ appear in oldest-to-youngest order
* 2. allocators that appear before marked_ in full_l_ count as 'before checkpoint'
* 3. allocators that appear after marked_ in full_l_ count as 'after checkpoint'
*/
bool younger_than_marked = true;
bool older_than_marked = true;
for (const auto & alloc : full_l_) {
if (younger_than_marked) {
if (older_than_marked) {
if (alloc.get() == marked_) {
/* nothing else to test on this iteration,
* already checked .marked_ specifically
*/
younger_than_marked = false;
break;
} else {
/* after checkpoint */
/* before checkpoint */
if (alloc->contains(x))
return false;
return true;
}
} else {
if (alloc->contains(x))
return true;
}
}
@ -171,55 +157,63 @@ namespace xo {
std::size_t
ListAlloc::before_checkpoint() const
{
scope log(XO_DEBUG(false && debug_flag_), xtag("marked", marked_ ? marked_->name() : ""));
if (marked_) {
if (full_l_.empty()) {
assert(marked_ == hd_.get());
return marked_->before_checkpoint();
} else {
std::size_t z = 0;
/* control here: .marked & .full_l non-empty. */
if (hd_.get() == marked_) {
z += hd_->before_checkpoint();
/* anything in .full_l is older than marked .hd */
for (const auto & alloc : full_l_) {
z += alloc->allocated();
}
return z;
} else {
/* messiest case: .marked is true,
* and not the youngest arena
*/
/* full_l always in increasing time order: oldest-to-youngest order */
size_t i_alloc = 0;
for (const auto & alloc : full_l_) {
log && log(xtag("i_alloc", i_alloc),
xtag("alloc", alloc->name()),
xtag("z", z));
if (alloc.get() == marked_) {
log && log("marked", xtag("+z", marked_->before_checkpoint()));
z += marked_->before_checkpoint();
break;
} else {
log && log("older than marked", xtag("+z", alloc->allocated()));
z += alloc->allocated();
}
++i_alloc;
}
}
return z;
}
} else {
/* count everything allocated */
/* count *everything* allocated */
return this->allocated();
}
std::size_t z = 0;
/* control here: .marked & .full_l non-empty. */
if (hd_.get() == marked_) {
z += hd_->before_checkpoint();
/* anything in .full_l older than marked .hd */
for (const auto & alloc : full_l_) {
z += alloc->allocated();
}
return z;
} else {
/* messiest case: .marked is true,
* and not the youngest arena
*/
bool younger_than_marked = true;
for (const auto & alloc : full_l_) {
if (younger_than_marked) {
if (alloc.get() == marked_) {
younger_than_marked = false;
z += marked_->before_checkpoint();
} else {
;
}
} else {
z += alloc->allocated();
}
}
}
return z;
}
std::size_t
ListAlloc::after_checkpoint() const
{
scope log(XO_DEBUG(false && debug_flag_), xtag("marked", marked_ ? marked_->name() : ""));
if (!marked_)
return 0;
@ -229,25 +223,44 @@ namespace xo {
return marked_->after_checkpoint();
}
bool younger_than_marked = true;
bool older_than_marked = true;
std::size_t z = 0;
std::size_t i_alloc = 0;
for (const auto & alloc : full_l_) {
if (younger_than_marked) {
log && log(xtag("i_alloc", i_alloc),
xtag("alloc", alloc->name()),
xtag("z", z));
if (older_than_marked) {
if (alloc.get() == marked_) {
younger_than_marked = false;
log && log("marked", xtag("+z", marked_->after_checkpoint()));
older_than_marked = false;
z += marked_->after_checkpoint();
break;
} else {
z += alloc->allocated();
}
} else {
/* younger than marked */
log && log("younger", xtag("+z", alloc->allocated()));
z += alloc->allocated();
}
++i_alloc;
}
/** head must be included, since it's always the youngest bucket **/
z += hd_->after_checkpoint();
log && log("z", z);
return z;
}
bool
ListAlloc::debug_flag() const {
return debug_flag_;
}
void
ListAlloc::clear() {
// general hygiene
@ -258,26 +271,17 @@ namespace xo {
current_z_ = 0;
next_z_ = 0;
total_z_ = 0;
#ifdef REDLINE_MEMORY
use_redline_ = false;
#endif
}
bool
ListAlloc::reset(std::size_t z)
{
#ifdef REDLINE_MEMORY
// warning: hd_->size() does not include redline memory
hd_->release_redline_memory();
#endif
scope log(XO_DEBUG(debug_flag_), xtag("z", z));
bool recycle_head_bucket = hd_ && (z <= hd_->size());
this->full_l_.clear();
this->marked_ = nullptr;
#ifdef REDLINE_MEMORY
this->redlined_flag_ = false;
#endif
if (recycle_head_bucket) {
this->hd_->clear();
@ -285,16 +289,22 @@ namespace xo {
return true;
} else {
std::string old_name = this->hd_->name();
this->hd_.reset(nullptr);
this->total_z_ = 0;
return this->expand(z);
return this->expand(z, old_name + "+");
}
}
bool
ListAlloc::expand(std::size_t z)
ListAlloc::expand(std::size_t z, const std::string & name)
{
scope log(XO_DEBUG(debug_flag_), xtag("name", name));
//log && log("before", xtag("before_ckp", this->before_checkpoint()));
std::size_t cz = current_z_;
std::size_t nz = next_z_;
std::size_t tz;
@ -305,12 +315,9 @@ namespace xo {
nz = tz;
} while (cz < z);
std::string name = hd_->name() + "+exp";
log && log("expand to", xtag("cz", cz));
std::unique_ptr<ArenaAlloc> new_alloc = ArenaAlloc::make(name,
#ifdef REDLINE_MEMORY
0,
#endif
cz, debug_flag_);
if (!new_alloc)
@ -320,41 +327,43 @@ namespace xo {
this->next_z_ = nz;
this->total_z_ += cz;
if (hd_)
this->full_l_.push_back(std::move(hd_));
this->hd_ = std::move(new_alloc);
//log && log("after", xtag("before_ckp", this->before_checkpoint()));
return true;
}
void
ListAlloc::checkpoint() {
scope log(XO_DEBUG(debug_flag_));
hd_->checkpoint();
this->marked_ = hd_.get();
log && log(xtag("hd", (void*)hd_.get()), xtag("marked", (void*)marked_));
}
std::byte *
ListAlloc::alloc(std::size_t z) {
scope log(XO_DEBUG(debug_flag_));
std::byte * retval = hd_->alloc(z);
if (retval)
return retval;
if (this->expand(z))
log && log("space exhausted -> expand");
if (this->expand(z, hd_->name() + "+"))
return hd_->alloc(z);
return nullptr;
}
#ifdef REDLINE_MEMORY
void
ListAlloc::release_redline_memory()
{
if (use_redline_)
redlined_flag_ = true;
this->hd_->release_redline_memory();
}
#endif
} /*namespace gc*/
} /*namespace xo*/

View file

@ -14,13 +14,13 @@ namespace xo {
{
os << "<PerObjectTypeStatistics";
if (td_)
os << rtag("td", td_->short_name());
os << xrtag("td", td_->short_name());
else
os << rtag("td", "nullptr");
os << rtag("scanned_n", scanned_n_)
<< rtag("scanned_z", scanned_z_)
<< rtag("survive_n", survive_n_)
<< rtag("survive_z", survive_z_)
os << xrtag("td", "nullptr");
os << xrtag("scanned_n", scanned_n_)
<< xrtag("scanned_z", scanned_z_)
<< xrtag("survive_n", survive_n_)
<< xrtag("survive_z", survive_z_)
<< ">";
}

View file

@ -0,0 +1,22 @@
/* generation.cpp
*
* author: Roland Conybeare, Aug 2025
*/
#include "generation.hpp"
namespace xo {
namespace gc {
const char * gen2str(generation x) {
switch (x) {
case generation::nursery: return "nursery";
case generation::tenured: return "tenured";
case generation::N: break;
}
return "?generation";
}
} /*namespace gc*/
} /*namespace xo*/
/* generation.cpp */