diff --git a/include/xo/alloc/GC.hpp b/include/xo/alloc/GC.hpp index a0bcef2b..64cf198d 100644 --- a/include/xo/alloc/GC.hpp +++ b/include/xo/alloc/GC.hpp @@ -7,6 +7,7 @@ #include "ListAlloc.hpp" #include "GcStatistics.hpp" +#include "xo/callback/UpCallbackSet.hpp" #include "xo/indentlog/print/array.hpp" #include #include @@ -105,12 +106,31 @@ namespace xo { using MutationLog = std::vector; + /** @class GcCopyCallback + * @brief optional callback to observe individual copy operations during GC + * + * For viz + **/ + class GcCopyCallback { + public: + virtual void notify_gc_copy(std::size_t z, const void * src_addr, const void * dest_addr, + generation src_gen, generation dest_gen) = 0; + /** invoked when added to callback set (i.e. @ref GC::GcCopyCallbackSet) **/ + void notify_add_callback() {} + /** invoked when removed from callback set **/ + void notify_remove_callback() {} + }; + /** @class GC * @brief generational garbage collector * * Works with objects of type @ref xo::Object **/ class GC : public IAlloc { + public: + using CallbackId = xo::fn::CallbackId; + using GcCopyCallbackSet = xo::fn::UpCallbackSet; + public: /** create new GC instance with configuration @p config **/ explicit GC(const Config & config); @@ -160,6 +180,11 @@ namespace xo { * from @c *addr **/ void add_gc_root(Object ** addr); + /** may optionally use this to observe GC copy phase. + * Will be invoked once _per surviving object_, so not cheap. + * Intended for GC visualization. + **/ + CallbackId add_gc_copy_callback(up fn); /** request garbage collection. **/ void request_gc(generation g); /** disable garbage collection until matching call to @ref enable_gc. @@ -335,6 +360,9 @@ namespace xo { /** enabled when 0. disabled when <0 **/ int gc_enabled_ = 0; + + /** for (optional) viz: invoke when copying individual objects **/ + GcCopyCallbackSet gc_copy_cbset_; }; } /*namespace gc*/ diff --git a/src/alloc/CMakeLists.txt b/src/alloc/CMakeLists.txt index 589f3b90..07f87784 100644 --- a/src/alloc/CMakeLists.txt +++ b/src/alloc/CMakeLists.txt @@ -15,5 +15,6 @@ set(SELF_SRCS xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_dependency(${SELF_LIB} reflect) +xo_dependency(${SELF_LIB} callback) #end CMakeLists.txt diff --git a/src/alloc/GC.cpp b/src/alloc/GC.cpp index bcb8118b..d0aaa660 100644 --- a/src/alloc/GC.cpp +++ b/src/alloc/GC.cpp @@ -289,6 +289,12 @@ namespace xo { gc_root_v_.push_back(addr); } + auto + GC::add_gc_copy_callback(up fn) -> CallbackId + { + return gc_copy_cbset_.add_callback(std::move(fn)); + } + void GC::checkpoint() { @@ -322,16 +328,19 @@ namespace xo { { scope log(XO_DEBUG(config_.debug_flag_), xtag("z", z), xtag("+pad", IAlloc::alloc_padding(z))); - generation_result gr = this->fromspace_generation_of(src); + generation_result src_gr = this->fromspace_generation_of(src); std::byte * retval = nullptr; - switch (gr) { + switch (src_gr) { case generation_result::tenured: { log && log("tenured"); retval = this->tenured_to()->alloc(z); + + gc_copy_cbset_.invoke(&GcCopyCallback::notify_gc_copy, + z, src, retval, generation::tenured, generation::tenured); } break; case generation_result::nursery: @@ -347,11 +356,18 @@ namespace xo { assert(this->tospace_generation_of(retval) == generation_result::tenured); + gc_copy_cbset_.invoke(&GcCopyCallback::notify_gc_copy, + z, src, retval, generation::nursery, generation::tenured); + this->gc_statistics_.total_promoted_ += IAlloc::with_padding(z); + } else { log && log("nursery"); retval = this->nursery_to()->alloc(z); + + gc_copy_cbset_.invoke(&GcCopyCallback::notify_gc_copy, + z, src, retval, generation::nursery, generation::nursery); } } break;