+ docs in xo-alloc2/ + misc improvements

This commit is contained in:
Roland Conybeare 2025-12-11 22:18:09 -05:00
commit 5964bcf3d5
22 changed files with 763 additions and 121 deletions

View file

@ -27,6 +27,6 @@ add_subdirectory(utest)
# docs targets depend on other library/utest/exec targets above,
# --> must come after them.
#
#add_subdirectory(docs)
add_subdirectory(docs)
# end CMakeLists.txt

View file

@ -0,0 +1,39 @@
.. _AAllocator-reference:
AAllocator Reference
====================
Abstract interface facet for arena allocator.
Provides simple arena allocation.
Context
-------
.. ditaa::
:--scale: 0.99
+--------------------------------+
| IAllocator_DArena |
+--------------------------------+
| IAllocator_Xfer |
+--------------------------------+
| IAllocator_ImplType |
+--------------+-----------------+
|cBLU | DArena |
| AAllocator +-----------------+
| | ArenaConfig |
+--------------+-----------------+
.. code-block:: cpp
#incldue <xo/alloc2/AAllocator.hpp>
Class
-----
.. doxygenclass:: xo::mm::AAllocator
Methods
-------
.. doxygengroup:: mm-allocator-methods

View file

@ -0,0 +1,35 @@
.. _ArenaConfig-reference:
ArenaConfig Reference
=====================
Configuration for an arena allocator
Context
-------
.. ditaa::
:--scale: 0.99
+--------------------------------+
| IAllocator_DArena |
+--------------------------------+
| IAllocator_Xfer |
+--------------------------------+
| IAllocator_ImplType |
+--------------+-----------------+
| | DArena |
| AAllocator +-----------------+
| | ArenaConfig cBLU|
+--------------+-----------------+
.. code-block:: cpp
#include <xo/alloc2/DArena.hpp>
Class
-----
.. doxygenclass:: xo::mm::ArenaConfig
.. doxygenclass:: mm-arenaconfig-instance-vars

17
docs/CMakeLists.txt Normal file
View file

@ -0,0 +1,17 @@
# xo-alloc2/docs/CMakeLists.txt
xo_doxygen_collect_deps()
xo_docdir_doxygen_config()
xo_docdir_sphinx_config(
index.rst
glossary.rst
implementation.rst
ArenaConfig-reference.rst
DArena-reference.rst
#install.rst
#introduction.rst
#implementation.rst
)
# see xo-reader/doc or xo-unit/doc for working examples
# example.rst install.rst implementation.rst

64
docs/DArena-reference.rst Normal file
View file

@ -0,0 +1,64 @@
.. _DArena-reference:
DArena Reference
================
Native representation for arena allocator
Context
-------
.. ditaa::
:--scale: 0.99
+--------------------------------+
| IAllocator_DArena |
+--------------------------------+
| IAllocator_Xfer |
+--------------------------------+
| IAllocator_ImplType |
+--------------+-----------------+
| | DArena cBLU|
| AAllocator +-----------------+
| | ArenaConfig |
+--------------+-----------------+
.. code-block:: cpp
#include <xo/alloc2/DArena.hpp>
Arena memory layout::
<----------------------------size-------------------------->
<------------committed-----------><-------uncommitted------>
<--allocated-->
XXXXXXXXXXXXXXX___________________..........................
^ ^ ^ ^
lo free limit hi
[X] allocated: in use
[_] committed: physical memory obtained
[.] uncommitted: mapped in virtual memory, not backed by memory
Class
-----
.. doxygenclass:: xo::mm::DArena
Member Variables
----------------
.. doxygengroup:: mm-arena-instance-vars
Type Traits
-----------
.. doxygengroup:: mm-arena-traits
Constructors
------------
.. doxygengroup:: mm-arena-ctors

70
docs/README Normal file
View file

@ -0,0 +1,70 @@
build
+-----------------------------------------------+
| cmake |
| CMakeLists.txt |
| $PREFIX/share/cmake/xo_macros/xo_cxx.cmake |
+-----------------------------------------------+
|
| +----------------------+
+------------------------------------------------->| .build/docs/Doxyfile |
| +----------------------+
| |
| /------------/
| |
| v
| +---------------------------------------+ +-----------------+
+---->| doxygen |--->| .build/docs/dox |
| | $PREFIX/share/xo-macros/Doxyfile.in | | +- html/ |
| +---------------------------------------+ | +- xml/ |
| +-----------------+
| |
| /------------/
| |
| v
| +---------------------------------------+ +--------------------+
\---->| sphinx |--->| .build/docs/sphinx |
| +- conf.py | | +- html/ |
| +- _static/ | +--------------------+
| +- *.rst |
+---------------------------------------+
files
README this file
CMakeLists.txt build entry point
conf.py sphinx config
_static static files for sphinx
map
index.rst
+- install.rst
+- examples.rst
+- unit-quantities.rst
+- classes.rst
+- glossary.rst
...
examples
.. doxygenclass:: ${c++ class name}
:project:
:path:
:members:
:protected-members:
:private-members:
:undoc-members:
:member-groups:
:members-only:
:outline:
:no-link:
:allow-dot-graphs:
.. doxygendefine:: ${c preprocessor define}
.. doxygenconcept:: ${c++ concept definition}
.. doxygenenum:: ${c++ enum definition}
.. doxygenfunction:: ${c++ function name}

1
docs/_static/README vendored Normal file
View file

@ -0,0 +1 @@
add any static {.html, .js, ..} files for sphinx to pickup here

BIN
docs/_static/img/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

39
docs/conf.py Normal file
View file

@ -0,0 +1,39 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'xo alloc2 documentation'
copyright = '2025, Roland Conybeare'
author = 'Roland Conybeare'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
#extensions = []
extensions = [ "breathe",
"sphinx.ext.mathjax", # inline math
"sphinx.ext.autodoc", # generate info from docstrings
"sphinxcontrib.ditaa", # diagrams-through-ascii-art
"sphinxcontrib.plantuml" # text -> uml diagrams
]
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
# match project name in Doxyfile.in
breathe_default_project = "xodoxxml"
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
#html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
html_favicon = '_static/img/favicon.ico'

24
docs/glossary.rst Normal file
View file

@ -0,0 +1,24 @@
.. _glossary:
Glossary
--------
.. glossary::
FOMO
| facet object model
page
| a (4k) page of virtual memory.
| O/S manages virtual memory in chunks of this size.
hugepage
| large (2MB) VM page; use to reduce page fault expense and TLB pressure.
THP
| transparent huge pages
TLB
| translation lookaside buffer
VM
| virtual memory

51
docs/implementation.rst Normal file
View file

@ -0,0 +1,51 @@
.. _implementation:
Components
==========
Library dependency tower for *xo-alloc2*
.. ditaa::
+-----------------+
| xo_alloc2 |
+-----------------+
| xo_facet |
+-----------------+
| xo_cmake |
+-----------------+
Abstraction tower for *xo-alloc2* components
.. ditaa::
:--scale: 0.99
+--------------------------------+
| IAllocator_DArena |
+--------------------------------+
| IAllocator_Xfer |
+--------------------------------+
| IAllocator_ImplType |
+--------------+-----------------+
| | DArena |
| AAllocator +-----------------+
| | ArenaConfig |
+--------------+-----------------+
.. list-table:: Descriptions
:header-rows: 1
:widths: 20 90
* - Component
- Description
* - ``AAllocator``
- allocator facet (abstract interface)
* - ``DArena``
- arena representation
* - ``IAllocator_ImplType<D>``
- lookup implementation for allocator A
with representation D.
* - ``IAllocator_Xfer<D>``
- transfer interface. downcast to native state.
* - ``IAllocator_DArena``
- allocator implementation for ``DArena``

28
docs/index.rst Normal file
View file

@ -0,0 +1,28 @@
# xo-alloc2 documentation master file
xo-alloc2 documentation
=======================
xo-alloc2 is intended to provide fast vm-aware arena allocation.
Next-generation version of xo-alloc.
At present (Dec 2025) xo-alloc is fully functional,
while xo-alloc2 is aspirational.
Features:
* allocates uncommitted virtual memory, and commits on demand.
* ses THP (Transparent Huge Pages) when available.
Implemented using FOMO (faceted rust-like object model) from xo-facet
.. toctree::
:maxdepth: 2
:caption: xo-alloc2 contents
implementation
AAllocator-reference
ArenaConfig-reference
DArena-reference
glossary
genindex
search

View file

@ -14,52 +14,86 @@ namespace xo {
using Copaque = const void *;
using Opaque = void *;
/** Abstract facet for allocation
/** @class AAllocator
* @brief Abstract facet for allocation
*
* <----------------------------size-------------------------->
* <------------committed-----------><-------uncommitted------>
* <--allocated-->
*
* XXXXXXXXXXXXXXX___________________..........................
*
* allocated: in use
* committed: physical memory obtained
* uncommitted: mapped in virtual memory, not backed by memory
**/
struct AAllocator {
/** RTTI: unique id# for actual runtime data repr **/
/*
* <----------------------------size-------------------------->
* <------------committed-----------><-------uncommitted------>
* <--allocated-->
*
* XXXXXXXXXXXXXXX___________________..........................
*
* allocated: in use
* committed: physical memory obtained
* uncommitted: mapped in virtual memory, not backed by memory
*/
/** @defgroup mm-allocator-methods Allocator methods **/
///@{
/** RTTI: unique id# for actual runtime data representation **/
virtual int32_t _typeseq() = 0;
/** optional name for this allocator.
/** optional name for allocator @p d
* Labeling, for diagnostics.
**/
virtual const std::string & name(Copaque d) = 0;
/** allocator size in bytes (up to reserved limit)
* includes allocated and uncomitted memory
* for allocator @p d.
* Includes allocated and uncomitted memory
**/
virtual std::size_t size(Copaque d) = 0;
/** committed size (physical addresses obtained)
* for allocator @p d.
**/
virtual std::size_t committed(Copaque d) = 0;
/** true iff pointer @p in range of this allocator
/** true iff allocator @p d is responsible for memory at address @p p.
**/
virtual bool contains(Copaque d, const void * p) = 0;
/** allocate @p z bytes of memory. **/
/** allocate @p z bytes of memory from allocator @p d. **/
virtual std::byte * alloc(Opaque d, std::size_t z) = 0;
/** reset allocator to empty state **/
/** reset allocator @p d to empty state **/
virtual void clear(Opaque d) = 0;
/** **/
/** destruct allocator @p d **/
virtual void destruct_data(Opaque d) = 0;
};
///@}
}; /*AAllocator*/
template <typename DRepr>
struct IAllocator_Impl;
template <typename DRepr>
using IAllocator_ImplType = IAllocator_Impl<DRepr>::ImplType;
struct IAllocator_Any : public AAllocator {
using Impl = Allocator_ImplType<xo::facet::DVariantPlaceholder>;
// from AAllocator
int32_t _typeseq() override { return s_typeseq; }
const std::string & name(Copaque d) override { assert(false); static std::string x; return x; }
std::size_t size(Copaque d) override { assert(false); return 0ul; }
std::size_t committed(Copaque d) override { assert(false); reutrn 0ul; }
bool contains(Copaque d, const void * p) override { assert(false); return false; }
std::byte * alloc(Opaque d, std::size_t z) override { assert(false); return nullptr; }
void clear(Opaque d) override { assert(false); }
void destruct_data(Opaque d) override { assert(false); }
static int32_t s_typeseq;
static bool _valid;
}
/** @class IAllocator_Xfer
**/
template <typename DRepr>
struct IAllocator_Xfer : public AAllocator {
// parallel interface to AAllocator, with specific data type
using Impl = IAllocator_Impl<DRepr>;
using Impl = IAllocator_ImplType<DRepr>;
static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; }

View file

@ -10,12 +10,17 @@
namespace xo {
namespace mm {
/** ArenaConfig
/** @class ArenaConfig
*
* @brief configuration for a @ref DArena instance
**/
struct ArenaConfig {
/** @defgroup mm-arenaconfig-instance-vars ArenaConfig members **/
///@{
/** optional name, for diagnostics **/
std::string name_;
/** arena size -- hard max = reserved virtual memory **/
/** desired arena size -- hard max = reserved virtual memory **/
std::size_t size_;
/** hugepage size -- using huge pages relieves some TLB pressure
* (provided you use their full extent :)
@ -23,31 +28,70 @@ namespace xo {
std::size_t hugepage_z_ = 2 * 1024 * 1024;
/** true to enable debug logging **/
bool debug_flag_ = false;
///@}
};
/** Arena allocator state
/** @class DArena
*
* <----------------------------size-------------------------->
* <------------committed-----------><-------uncommitted------>
* <--allocated-->
* @brief represent arena allocator state
*
* XXXXXXXXXXXXXXX___________________..........................
*
* allocated: in use
* committed: physical memory obtained
* uncommitted: mapped in virtual memory, not backed by memory
* Provides minimal RAII functionality around memory mapping.
* For allocation see @ref IAllocator_DArena
**/
struct DArena {
/** [lo, hi) already-mapped address range **/
DArena(const ArenaConfig & cfg,
std::byte * lo,
std::byte * hi);
/*
* <----------------------------size-------------------------->
* <------------committed-----------><-------uncommitted------>
* <--allocated-->
*
* XXXXXXXXXXXXXXX___________________..........................
*
* [X] allocated: in use
* [_] committed: physical memory obtained
* [.] uncommitted: mapped in virtual memory, not backed by memory
*/
/** @defgroup mm-arena-traits arena type traits **/
///@{
/** @brief an amount of memory **/
using size_type = std::size_t;
/** @brief a contiguous memory range **/
using range_type = std::pair<std::byte*,std::byte*>;
///@}
/** @defgroup mm-arena-ctors arena constructors and destructors **/
///@{
/** create arena per configuration @p cfg. **/
static DArena map(const ArenaConfig & cfg);
/** ctor from already-mapped (but not committed) address range **/
DArena(const ArenaConfig & cfg, size_type page_z, std::byte * lo, std::byte * hi);
/** DArena is not copyable **/
DArena(const DArena & other) = delete;
/** move ctor **/
DArena(DArena && other);
/** dtor releases mapped memory **/
~DArena();
///@}
/** obtain uncommitted contiguous memory range comprising
* a whole multiple of @p hugepage_z bytes, of at least size @p req_z,
* aligned on a @p hugepage_z boundary
**/
static range_type map_aligned_range(size_type req_z, size_type hugepage_z);
/** @defgroup mm-arena-instance-vars **/
///@{
/** arena configuration **/
ArenaConfig config_;
/** size of a VM page (via getpagesize()). Likely 4k **/
/** size of a VM page (obtained automatically via getpagesize()). Likely 4k **/
std::size_t page_z_ = 0;
/** arena owns memory in range [@ref lo_, @ref hi_)
@ -74,6 +118,8 @@ namespace xo {
* Memory in range [@ref limit_, @ref hi_) is uncommitted
**/
std::byte * hi_ = nullptr;
///@}
};
} /*namespace mm*/

View file

@ -9,12 +9,8 @@
namespace xo {
namespace mm {
template <>
struct IAllocator_Impl<DArena> {
static const std::string & name(const DArena & s) {
return s.name_;
}
struct IAllocator_DArena {
static const std::string & name(const DArena & s);
static std::size_t size(const DArena & s);
static std::size_t committed(const DArena & s);
static bool contains(const DArena & s, const void * p);
@ -23,6 +19,10 @@ namespace xo {
static void destruct_data(DArena & s);
};
template <>
struct IAllocator_Impl<DArena> {
using ImplType = IAllocator_DArena;
};
} /*namespace mm*/
} /*namespace xo*/

View file

@ -0,0 +1,38 @@
/** @file RAllocator.hpp
*
* @author Roland Conybeare, Dec 2025
**/
#pragma once
#include "xo/facet/RRouter.hpp"
namespace xo {
namespace mm {
/** @class RAllocator **/
template <typename Object>
struct RAllocator : public Object {
using ObjectType = Object;
RAllocator() {}
RAllocator(Object::DataPtr data) : Object{std::move(data)} {}
int32_t _typeseq() const { return Object::iface()->_typeseq(); }
static bool _valid;
};
template <typename Object>
bool
RAllocator<Object>::_valid = facet::valid_object_router<Object>();
}
namespace facet {
template <typename Object>
struct RoutingFor<xo::mm::AAllocator, Object> {
using RoutingType = xo::mm::RAllocator<Object>;
};
}
}
/* end RAllocator.hpp */

19
src/alloc2/AAllocator.cpp Normal file
View file

@ -0,0 +1,19 @@
/** @file AAllocator.cpp **/
#include "xo/alloc2/AAllocator.hpp"
namespace xo {
using xo::facet::DVariantPlaceholder;
using xo::facet::typeseq;
using xo::facet::valid_facet_implementation;
namespace mm {
int32_t
IAllocator_Any::s_typeseq = typeseq::id<DVariantPlaceholder>;
bool
IAllocator_Any::_valid = valid_facet_implementation<AAllocator, IAllocator_Any>;
}
}
/* end AAlocator.cpp */

View file

@ -2,6 +2,7 @@
set(SELF_LIB xo_alloc2)
set(SELF_SRCS
AAllocator.cpp
DArena.cpp
IAllocator_DArena.cpp
)

View file

@ -9,107 +9,135 @@
#include <cassert>
#include <sys/mman.h> // for ::munmap()
#include <unistd.h> // for ::getpagesize()
#include <string.h> // for ::memset()
namespace xo {
using std::byte;
using std::size_t;
namespace mm {
DArena::DArena(const ArenaConfig & cfg,
std::byte * lo,
std::byte * hi
)
/** Map a contiguous uncommitted memory range comprising
* a whole multiple of @p hugepage_z, with at least
* @p req_z bytes.
*
* Memory will also be aligned on @p hugepage_z boundary
* (2MB in practice)
*
* - @p req_z is rounded up to a multiple of @p hugepage_z
* - Resulting uncommitted address range not backed by
* physical memory.
* - since hugpage-aligned, can find base of mapped range
* by masking off the bottom log2(align_z) bits.
* May rely on this for GC metadata
* - opt-in to transparent huge pages (THP).
* Reduces page-fault time by a lot, in return for
* lower VM granularity
* - rejecting inferior MAP_HUGETLB|MAP_HUGE_2MB flags on ::mmap here:
* - requires previously-reserved memory in /proc/sys/vm/nr_hugepages
* - reserved pages permenently resident in RAM, never swapped
* - memory cost incurred even if no application is using said pages
*
* TODO: for OSX -> need something else here.
* MAP_ALIGNED_SUPER with mmap() and/or
* use mach_vm_allocate()
*
* @return pair giving mapped address range [lo, hi)
**/
auto
DArena::map_aligned_range(size_t req_z, size_t hugepage_z) -> range_type
{
//scope log(XO_DEBUG(debug_flag), xtag("name", name));
// 1. round up to multiple of hugepage_z
size_t target_z = padding::with_padding(req_z, hugepage_z); // 4.
this->page_z_ = getpagesize();
// 1. need k pagetable entries where k is lub {k | k * .page_z >= z}
// 2. base will be aligned with .page_z but likely not with .hugepage_z
// 3. bad to have misalignment, because misaligned {prefix, suffix} of [base, base+z)
// will use 4k pages instead of 2mb pages
// 2. mmap() will give us page-aligned memory,
// but not hugepage-aligned.
//
// strategy:
// 4. round up z to multiple of hugepage_z_
// 5. over-request so reserved range contains an aligned subrange of size z
// 6. unmap misaligned prefix
// 7. unmap misaligned suffix.
// 8. enable huge pages for now-aligned remainder of reserved range
// Over-request by hugepage_z to ensure
// hugepage-aligned subrange of size target_z
//
// Z. note: rejecting inferior MAP_HUGETLB|MAP_HUGE_2MB flags on ::mmap here:
// Za. requires previously-reserved memory in /proc/sys/vm/nr_hugepages
// Zb. reserved pages permenently resident in RAM, never swapped
// Zc. memory cost incurred even if no application is using said pages
byte * base = (byte *)(::mmap(nullptr,
target_z + hugepage_z,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0));
std::size_t z = cfg.size_;
z = padding::with_padding(z, config_.hugepage_z_); // 4.
// 5.
byte * base = reinterpret_cast<byte *>(
::mmap(nullptr,
z + config_.hugepage_z_,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0));
// on mmap success: upper limit of mapped address range
byte * hi = base + (target_z + hugepage_z);
// lowest hugepage-aligned address in [base, hi)
byte * aligned_base = (byte *)(padding::with_padding((size_t)base, hugepage_z));
// end of hugeppage-aligned range starting at aligned_base
byte * aligned_hi = aligned_base + target_z;
#ifdef NOT_YET
log && log("acquired memory [lo,hi) using mmap",
xtag("lo", base),
xtag("z", z),
xtag("hi", reinterpret_cast<byte *>(base) + z));
xtag("req_z", req_z),
xtag("target_z", target_z),
xtag("hi", (byte *)(base) + z));
#endif
if (base == MAP_FAILED) {
assert(false);
// 3. assess mmap success
{
if (base == MAP_FAILED) {
assert(false);
#ifdef NOPE
throw std::runtime_error(tostr("ArenaAlloc: uncommitted allocation failed",
xtag("size", z)));
throw std::runtime_error(tostr("ArenaAlloc: uncommitted allocation failed",
xtag("size", z)));
#endif
}
assert((size_t)aligned_base % hugepage_z == 0);
assert(aligned_base >= base);
assert(aligned_base < base + hugepage_z);
}
byte * aligned_base = reinterpret_cast<byte *>
(padding::with_padding(reinterpret_cast<size_t>(base),
config_.hugepage_z_));
assert(reinterpret_cast<size_t>(aligned_base) % config_.hugepage_z_ == 0);
assert(aligned_base >= base);
assert(aligned_base < base + config_.hugepage_z_);
// 4. release unaligned prefix
if (base < aligned_base) {
size_t prefix = aligned_base - base;
size_t ua_prefix = aligned_base - base;
::munmap(base, prefix); // 6.
::munmap(base, ua_prefix);
}
byte * aligned_hi = aligned_base + z;
byte * hi = base + z + config_.hugepage_z_;
// 5. release unaligned suffix
if (aligned_hi < hi) {
size_t suffix = hi - aligned_hi;
::munmap(aligned_hi, suffix); // 7.
::munmap(aligned_hi, suffix);
}
#ifdef __linux__
/** opt-in to huge pages, provided they're available.
* otherwise fallback gracefully
/** linux:
* opt-in to transparent huge pages (THP)
* provided OS configured to support them.
* otherwise fallback gracefully.
*
* Huge pages -> use fewer TLB entries + faster
* shorter path through page table.
*
* When we commit (i.e. obtain physical memory on page fault),
* typically expect to pay ~1us per superpage.
* Much better than ~500us to commit 512 4k VM pages.
*
* But wasted if we don't use the memory.
**/
::madvise(aligned_base, z, MADV_HUGEPAGE); // 8.
::madvise(aligned_base, target_z, MADV_HUGEPAGE); // 8.
#endif
// TODO: for OSX -> need something else here.
// MAP_ALIGNED_SUPER with mmap() and/or
// use mach_vm_allocate()
//
this->lo_ = aligned_base;
this->committed_z_ = 0;
//this->checkpoint_ = lo_;
this->free_ = lo_;
this->limit_ = lo_;
this->hi_ = lo_ + z;
return std::make_pair(aligned_base, aligned_hi);
}
DArena
DArena::map(const ArenaConfig & cfg)
{
//scope log(XO_DEBUG(debug_flag), xtag("name", name));
auto [lo, hi] = map_aligned_range(cfg.size_, cfg.hugepage_z_);
if (!lo) {
// control here implies mmap() failed silently
if (!lo_) {
assert(false);
#ifdef NOPE
throw std::runtime_error(tostr("ArenaAlloc: allocation failed",
@ -117,11 +145,47 @@ namespace xo {
#endif
}
size_t page_z = getpagesize();
#ifdef NOPE
log && log(xtag("lo", (void*)lo_),
xtag("page_z", page_z_),
xtag("hugepage_z", hugepage_z_));
#endif
return DArena(cfg, page_z, lo, hi);
} /*map*/
DArena::DArena(const ArenaConfig & cfg,
size_type page_z,
byte * lo,
byte * hi) : config_{cfg},
page_z_{page_z},
lo_{lo},
committed_z_{0},
free_{lo},
limit_{lo},
hi_{hi}
{
//retval.checkpoint_ = lo_;
}
DArena::DArena(DArena && other) {
config_ = other.config_;
page_z_ = other.page_z_;
lo_ = other.lo_;
committed_z_ = other.committed_z_;
free_ = other.free_;
limit_ = other.limit_;
hi_ = other.hi_;
other.config_ = ArenaConfig();
other.lo_ = nullptr;
other.committed_z_ = 0;
other.free_ = nullptr;
other.limit_ = nullptr;
other.hi_ = nullptr;
}
DArena::~DArena()
@ -136,12 +200,12 @@ namespace xo {
}
// hygiene
lo_ = nullptr;
committed_z_ = 0;
this->lo_ = nullptr;
this->committed_z_ = 0;
// checkpoint_ = nullptr;
free_ = nullptr;
limit_ = nullptr;
hi_ = nullptr;
this->free_ = nullptr;
this->limit_ = nullptr;
this->hi_ = nullptr;
}
}
} /*namespace xo*/

View file

@ -9,25 +9,30 @@
namespace xo {
namespace mm {
const std::string &
IAllocator_DArena::name(const DArena & s) {
return s.config_.name_;
}
std::size_t
IAllocator_Impl<DArena>::size(const DArena & s) {
IAllocator_DArena::size(const DArena & s) {
return s.limit_ - s.lo_;
}
std::size_t
IAllocator_Impl<DArena>::committed(const DArena & s) {
IAllocator_DArena::committed(const DArena & s) {
return s.committed_z_;
}
bool
IAllocator_Impl<DArena>::contains(const DArena & s,
IAllocator_DArena::contains(const DArena & s,
const void * p)
{
return (s.lo_ <= p) && (p < s.hi_);
}
std::byte *
IAllocator_Impl<DArena>::alloc(const DArena & s,
IAllocator_DArena::alloc(const DArena & s,
std::size_t z)
{
(void)s;
@ -39,14 +44,14 @@ namespace xo {
}
void
IAllocator_Impl<DArena>::clear(DArena & s)
IAllocator_DArena::clear(DArena & s)
{
s.free_ = s.lo_;
//s.checkpoint_ = s.lo_;
}
void
IAllocator_Impl<DArena>::destruct_data(DArena & s)
IAllocator_DArena::destruct_data(DArena & s)
{
s.~DArena();
}

View file

@ -9,6 +9,7 @@ set(UTEST_SRCS
if (ENABLE_TESTING)
xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS})
xo_self_dependency(${UTEST_EXE} xo_alloc2)
xo_headeronly_dependency(${UTEST_EXE} xo_facet)
xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2)
endif()

View file

@ -6,12 +6,19 @@
#include "xo/alloc2/AAllocator.hpp"
#include "xo/alloc2/DArena.hpp"
#include "xo/alloc2/IAllocator_DArena.hpp"
#include "xo/alloc2/RAllocator.hpp"
#include "xo/alloc2/padding.hpp"
#include "xo/facet/obj.hpp"
#include <catch2/catch.hpp>
namespace xo {
using xo::mm::AAllocator;
using xo::mm::IAllocator_Xfer;
using xo::mm::DArena;
using xo::mm::ArenaConfig;
using xo::facet::obj;
using std::byte;
using std::size_t;
namespace ut {
TEST_CASE("IAllocator_Xfer_DArena", "[alloc2]")
@ -20,6 +27,65 @@ namespace xo {
REQUIRE(IAllocator_Xfer<DArena>::_valid);
}
TEST_CASE("DArena", "[alloc2][DArena]")
{
ArenaConfig cfg { .name_ = "testarena",
.size_ = 1 };
DArena arena = DArena::map(cfg);
REQUIRE(arena.config_.name_ == cfg.name_);
REQUIRE(arena.lo_ != nullptr);
REQUIRE(arena.free_ == arena.lo_);
REQUIRE(arena.limit_ == arena.lo_);
REQUIRE(arena.hi_ != nullptr);
REQUIRE(arena.hi_ > arena.lo_);
REQUIRE(((size_t)arena.hi_ - (size_t)arena.lo_) % cfg.hugepage_z_ == 0);
REQUIRE(arena.lo_ + cfg.size_ <= arena.hi_);
/* verify arena.lo_ is aligned on a hugepage boundary */
REQUIRE(((size_t)(arena.lo_) & (cfg.hugepage_z_ - 1)) == 0);
/* verify arena.hi_ is aligned on a hugepage boundary */
REQUIRE(((size_t)(arena.hi_) & (cfg.hugepage_z_ - 1)) == 0);
byte * lo = arena.lo_;
byte * free = arena.free_;
byte * limit = arena.limit_;
byte * hi = arena.hi_;
size_t committed_z = arena.committed_z_;
DArena arena2 = std::move(arena);
REQUIRE(arena.lo_ == nullptr);
REQUIRE(arena.free_ == nullptr);
REQUIRE(arena.limit_ == nullptr);
REQUIRE(arena.hi_ == nullptr);
REQUIRE(arena.committed_z_ == 0);
REQUIRE(arena.lo_ == nullptr);
REQUIRE(arena2.lo_ == lo);
REQUIRE(arena2.free_ == free);
REQUIRE(arena2.limit_ == limit);
REQUIRE(arena2.hi_ == hi);
REQUIRE(arena2.committed_z_ == committed_z);
}
TEST_CASE("allocator-any-1", "[alloc2][AAllocator]")
{
#ifdef NOPE
ArenaConfig cfg { .name_ = "testarena",
.size_ = 1 };
DArena arena = DArena::map(cfg);
#endif
/* empty allocator */
obj<AAllocator> alloc1;
REQUIRE(!alloc1);
REQUIRE(alloc1.iface() != nullptr);
REQUIRE(alloc1.data() == nullptr);
}
} /*namespace ut*/
} /*namespace xo*/