xo-alloc2 : work on X1Collector unit test [WIP]

This commit is contained in:
Roland Conybeare 2025-12-15 22:43:21 -05:00
commit 1fd5d544f2
14 changed files with 542 additions and 41 deletions

View file

@ -10,6 +10,17 @@
namespace xo {
namespace mm {
/** **/
struct AllocHeader {
using repr_type = std::uint64_t;
explicit AllocHeader(repr_type x) : repr_{x} {}
repr_type repr_;
};
static_assert(sizeof(AllocHeader) == sizeof(AllocHeader::repr_type));
/** @class DArena
*
* @brief represent arena allocator state
@ -35,8 +46,10 @@ namespace xo {
/** @brief an amount of memory **/
using size_type = std::size_t;
/** @brief allocation pointer; use for allocation results **/
using value_type = std::byte*;
/** @brief a contiguous memory range **/
using range_type = std::pair<std::byte*,std::byte*>;
using range_type = std::pair<value_type, value_type>;
/** @brief type for allocation header (if enabled) **/
using header_type = std::uint64_t;
@ -51,7 +64,7 @@ namespace xo {
/** null ctor **/
DArena() = default;
/** ctor from already-mapped (but not committed) address range **/
DArena(const ArenaConfig & cfg, size_type page_z, std::byte * lo, std::byte * hi);
DArena(const ArenaConfig & cfg, size_type page_z, value_type lo, value_type hi);
/** DArena is not copyable **/
DArena(const DArena & other) = delete;
/** move ctor **/
@ -67,12 +80,12 @@ namespace xo {
/** @defgroup mm-arena-methods **/
///@{
size_type reserved() const { return hi_ - lo_; }
size_type allocated() const { return free_ - lo_; }
size_type committed() const { return committed_z_; }
size_type available() const { return limit_ - free_; }
size_type reserved() const noexcept { return hi_ - lo_; }
size_type allocated() const noexcept { return free_ - lo_; }
size_type committed() const noexcept { return committed_z_; }
size_type available() const noexcept { return limit_ - free_; }
bool contains(void * addr) const { return (lo_ <= addr) && (addr < hi_); }
bool contains(const void * addr) const noexcept { return (lo_ <= addr) && (addr < hi_); }
/** obtain uncommitted contiguous memory range comprising
* a whole multiple of @p hugepage_z bytes, of at least size @p req_z,
@ -81,10 +94,17 @@ namespace xo {
static range_type map_aligned_range(size_type req_z, size_type hugepage_z);
/** true if arena is mapped i.e. has a reserved address range **/
bool is_mapped() const { return (lo_ != nullptr) && (hi_ != nullptr); }
bool is_mapped() const noexcept { return (lo_ != nullptr) && (hi_ != nullptr); }
/** get header from allocated object address **/
header_type * obj2hdr(void * obj);
header_type * obj2hdr(void * obj) noexcept;
/** discard all allocated memory, return to empty state
* Promise:
* - committed memory unchanged
* - available memory = committed memory
**/
void clear() noexcept;
///@}

View file

@ -64,6 +64,12 @@ namespace xo {
constexpr std::uint64_t tseq_mask_unshifted() const;
constexpr std::uint64_t tseq_mask_shifted() const;
public:
// ----- Instance Variables -----
/** optional name, for diagnostics **/
std::string name_;
/** Configuration for collector spaces.
* Will have at least {nursery,tenured} x {from,to} spaces.
* Not using name_ member.
@ -135,32 +141,75 @@ namespace xo {
// ----- DX1Collector -----
struct DX1Collector {
using size_type = DArena::size_type;
using value_type = DArena::value_type;
using header_type = DArena::header_type;
explicit DX1Collector(const CollectorConfig & cfg);
const DArena * get_space(role r, generation g) const { return space_[r][g]; }
DArena * get_space(role r, generation g) { return space_[r][g]; }
DArena * from_space(generation g) { return get_space(role::from_space(), g); }
DArena * to_space(generation g) { return get_space(role::to_space(), g); }
const DArena * get_space(role r, generation g) const noexcept { return space_[r][g]; }
DArena * get_space(role r, generation g) noexcept { return space_[r][g]; }
DArena * from_space(generation g) noexcept { return get_space(role::from_space(), g); }
DArena * to_space(generation g) noexcept { return get_space(role::to_space(), g); }
DArena * new_space() noexcept { return to_space(generation{0}); }
/** total reserved memory in bytes, across all {role, generation} **/
size_type reserved_total() const noexcept;
/** total size in bytes (same as committed_total()) **/
size_type size_total() const noexcept;
/** total committed memory in bytes, across all {role, generation} **/
size_type committed_total() const noexcept;
/** total available memory in bytes, across all {role, generation} **/
size_type available_total() const noexcept;
/** total allocated memory in bytes, across all {role, generation} **/
size_type allocated_total() const noexcept;
/** true iff address @p addr allocated from this collector
* in role @p r (according to current GC state)
**/
bool contains(role r, void * addr) const;
bool contains(role r, const void * addr) const noexcept;
/** return details from last error (will be in gen0 to-space) **/
AllocatorError last_error() const noexcept;
/** get allocation size from header **/
std::size_t header2size(header_type hdr) const;
std::size_t header2size(header_type hdr) const noexcept;
/** get generation counter from alloc header **/
generation header2gen(header_type hdr) const;
generation header2gen(header_type hdr) const noexcept;
/** get tseq from alloc header **/
uint32_t header2tseq(header_type hdr) const;
uint32_t header2tseq(header_type hdr) const noexcept;
/** true iff original alloc has been replaced by a forwarding pointer **/
bool is_forwarding_header(header_type hdr) const;
bool is_forwarding_header(header_type hdr) const noexcept;
// ----- allocation -----
/** simple allocation. new allocs always in gen0 to-space **/
value_type alloc(size_type z) noexcept;
/** compound allocation. To be followed immediately by:
* 1. zero or more calls to sub_alloc(zi, complete=false), then
* 2. exactly one call to sub_alloc(zi, complete=true)
* all the allocs in a compound allocation share the same
* allocation header. End state is equivalent to a single
* allocation with size z + sum(zi).
* New allocs always in gen0 to-space
**/
value_type super_alloc(size_type z) noexcept;
/** sub-allocation with preceding compound allocation.
* New allocs always in gen0 to-space
**/
value_type sub_alloc(size_type z, bool complete) noexcept;
/** expand gen0 committed size to at least @p z.
**/
bool expand(size_type z) noexcept;
// ----- book-keeping -----
/** reverse to-space and from-space roles for generation g **/
void reverse_roles(generation g);
void reverse_roles(generation g) noexcept;
/** discard all allocated memory **/
void clear() noexcept;
public:
/** garbage collector configuration **/

View file

@ -5,8 +5,7 @@
#pragma once
#include "alloc2/alloc/Allocator.hpp"
#include "alloc2/alloc/IAllocator_Xfer.hpp"
#include "Allocator.hpp"
#include "DX1Collector.hpp"
namespace xo {
@ -38,6 +37,7 @@ namespace xo {
static std::string_view name(const DX1Collector &) noexcept;
/** reserved memory across all {roles, generations} **/
static size_type reserved(const DX1Collector &) noexcept;
/** 'size'. synonym for committed size **/
static size_type size(const DX1Collector &) noexcept;
/** committed size accross all {roles, generations} **/
static size_type committed(const DX1Collector &) noexcept;
@ -50,14 +50,13 @@ namespace xo {
/** report last error, if any, for collector @p d **/
static AllocatorError last_error(const DX1Collector &) noexcept;
/** always alloc in gen0 to-space **/
static value_type alloc(DX1Collector & d, size_type z) noexcept;
static value_type super_alloc(DX1Collector & d, size_type z) noexcept;
static value_type sub_alloc(DX1Collector & d, size_type z, bool complete) noexcept;
/** expand gen0 spaces (both from-space and to-space) **/
static bool expand(DX1Collector & d, size_type z) noexcept;
/** always alloc in gen0 to-space **//
static value_type alloc(DX1Collector & d, size_type z);
static value_type super_alloc(DX1Collector & d, size_type z);
static value_type sub_alloc(DX1Collector & d, size_type z, bool complete);
/** reset to empty state; clears all generations **/
static void clear(DX1Collector & d);
/** invoke destructor **/

View file

@ -21,6 +21,8 @@ namespace xo {
explicit constexpr generation(value_type x) : value_{x} {}
static generation nursery() { return generation{0}; }
constexpr operator value_type() const { return value_; }
generation & operator++() { ++value_; return *this; }

View file

@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <cstdint>
namespace xo {
@ -19,6 +20,11 @@ namespace xo {
static constexpr role to_space() { return role{0}; }
static constexpr role from_space() { return role{1}; }
static constexpr std::array<role, c_n_role> all() { return {{to_space(), from_space()}}; }
static constexpr role begin() { return role{0}; }
static constexpr role end() { return role{2}; }
operator value_type() const { return role_; }
value_type role_ = 0;