xo-alloc2: ++ documentation + threshold size for THP feature

This commit is contained in:
Roland Conybeare 2025-12-23 21:06:38 -05:00
commit 289751d3fd
18 changed files with 642 additions and 192 deletions

View file

@ -3,8 +3,15 @@
AAllocator Reference AAllocator Reference
==================== ====================
Abstract interface facet for arena allocator. Abstract interface facet for an allocator.
Provides simple arena allocation.
Base class for runtime polymorphism over allocator implementations,
using faceted object model.
* runtime size consists of vtable pointer only.
* per FOMO prinicples, runtime state is stored separately.
Classes that inherit ``AAllocator`` will not add state
Context Context
------- -------
@ -12,40 +19,24 @@ Context
.. ditaa:: .. ditaa::
:--scale: 0.99 :--scale: 0.99
+-------------------------------+--------------------------------------+ +----------------------+-------------------------+-----------------------------------+
| | IAllocator_DX1Collector | | RAllocator | RAllocIterator | IAllocator_DArena |
| | IAllocIterator_DX1CollectorIterator | | | | IAllocIterator_DArenaIterator |
| IAllocator_DArena | | +----------------------+-------------------------+-----------------------------------+
| IAllocIterator_DArenaIterator +--------------------------------------+ | IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| | DX1Collector | | IAllocator_Any | IAllocIterator_Any | DArenaIterator |
| | DX1CollectorIterator | | IAllocator_Impltype | IAllocIterator_Impltype | |
| | | | | | |
+-------------------------------+---------+----------------------------+ +----------------------+-------------------------+-----------------------------------+
| DArena | | |cBLU AAllocator | AAllocIterator | ArenaConfig |
| DArenaIterator | | +----------------------+-------------------------+-----------------------------------+
+-----------------------------------------+ CollectorConfig | +-----------------+----------------------------------------------+-------------------+
| ArenaConfig | | | | AllocInfo | |
+-----------------------------------------+----------------------------+ | +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
+----------------------------------+-----------------------------------+ | +----------------------------------------------+ |
| RAllocator | RAllocIterator | | | AllocHeader | |
+----------------------------------+-----------------------------------+ +-----------------+----------------------------------------------+-------------------+
| IAllocator_DX1Collector | IAllocIterator_DX1Collector |
| IAllocator_DArena | IAllocIterator_DArena |
+----------------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer |
| IAllocator_Any | IAllocIterator_Any |
+----------------------------------+-----------------------------------+
| AAllocator | AAllocIterator |
+----------------------------------+-----------------------------------+
+---------------+------------------+----------------------+------------+
| | | AllocInfo | |
| generation | +----------------------+ |
| object_age | AllocError | AllocHeaderConfig | cmpresult |
| role | +----------------------+ |
| | | AllocHeader | |
+---------------+------------------+----------------------+------------+
.. code-block:: cpp .. code-block:: cpp

View file

@ -0,0 +1,61 @@
.. _AllocInfo-reference:
AllocInfo Reference
===================
Information, including alloc metadata, pertaining to a particular allocation.
Context
-------
.. ditaa::
:--scale: 0.99
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| IAllocator_Any | IAllocIterator_Any | DArenaIterator |
| IAllocator_Impltype | IAllocIterator_Impltype | |
| | | |
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator | ArenaConfig cBLU |
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
.. code-block:: cpp
#include <xo/alloc2/DArena.hpp>
Class
-----
.. doxygenclass:: xo::mm::AllocInfo
Member Variables
----------------
.. doxygengroup:: mm-allocinfo-instance-vars
Type Traits
-----------
.. doxygengroup:: mm-allocinfo-traits
Constructors
------------
.. doxygengroup:: mm-allocinfo-ctors
Methods
-------
.. doxygengroup:: mm-allocinfo-methods

View file

@ -5,10 +5,12 @@ xo_docdir_doxygen_config()
xo_docdir_sphinx_config( xo_docdir_sphinx_config(
index.rst index.rst
glossary.rst glossary.rst
examples.rst
implementation.rst implementation.rst
AAllocator-reference.rst AAllocator-reference.rst
ArenaConfig-reference.rst ArenaConfig-reference.rst
DArena-reference.rst DArena-reference.rst
AllocInfo-reference.rst
#install.rst #install.rst
#introduction.rst #introduction.rst
#implementation.rst #implementation.rst

View file

@ -11,17 +11,24 @@ Context
.. ditaa:: .. ditaa::
:--scale: 0.99 :--scale: 0.99
+--------------------------------+ +----------------------+-------------------------+-----------------------------------+
| IAllocator_DArena | | RAllocator | RAllocIterator | IAllocator_DArena |
+--------------------------------+ | | | IAllocIterator_DArenaIterator |
| IAllocator_Xfer | +----------------------+-------------------------+-----------------------------------+
+--------------------------------+ | IAllocator_Xfer | IAllocIterator_Xfer | DArena cBLU |
| IAllocator_ImplType | | IAllocator_Any | IAllocIterator_Any +-----------------------------------+
+--------------+-----------------+ | IAllocator_Impltype | IAllocIterator_Impltype | DArenaIterator |
| | DArena cBLU| | | | |
| AAllocator +-----------------+ +----------------------+-------------------------+-----------------------------------+
| | ArenaConfig | | AAllocator | AAllocIterator | ArenaConfig |
+--------------+-----------------+ +----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
.. code-block:: cpp .. code-block:: cpp
@ -29,11 +36,13 @@ Context
Arena memory layout Arena memory layout
~~~~~~~~~~~~~~~~~~~
.. code-block:: text .. code-block:: text
<----------------------------size--------------------------> <------------------------reserved-------------------------->
<------------committed-----------><-------uncommitted------> <------------committed-----------><-------uncommitted------>
<--allocated--> <--allocated--><----available---->
XXXXXXXXXXXXXXX___________________.......................... XXXXXXXXXXXXXXX___________________..........................
^ ^ ^ ^ ^ ^ ^ ^
@ -44,7 +53,9 @@ Arena memory layout
[.] uncommitted: mapped in virtual memory, not backed by memory [.] uncommitted: mapped in virtual memory, not backed by memory
Allocation layout Representation for a single allocation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: text .. code-block:: text
free_(pre) free_(pre)
@ -60,8 +71,8 @@ Allocation layout
^ | ^ |
last_header_ free_(post) last_header_ free_(post)
[+] guard after each allocation, for simple sanitize checks [+] guard surrounding each allocation, for simple sanitize checks
[0] unused header bits (avail to application) [0] unused header bits (available for application metadata)
[z] record allocation size [z] record allocation size
[@] new allocated memory [@] new allocated memory
[p] padding (to uintptr_t alignment) [p] padding (to uintptr_t alignment)
@ -85,3 +96,8 @@ Constructors
------------ ------------
.. doxygengroup:: mm-arena-ctors .. doxygengroup:: mm-arena-ctors
Methods
-------
.. doxygengroup:: mm-arena-methods

View file

@ -34,7 +34,79 @@ Size here is a hard maximum. It cannot be changed for this arena instance.
arena.reserved(); // 64k arena.reserved(); // 64k
arena.committed(); // 0k arena.committed(); // 0k
arena.allocated(); // ok
arena.available(); // 0k arena.available(); // 0k
Although we know the address range for arena, it doesn't own any physical Although we know the address range for arena, it doesn't own any physical
memory yet. memory yet. Two ways to commit memory:
1. Attempt allocation:
.. code-block:: cpp
std::byte * mem = arena.alloc(5*1024);
if (!mem)
throw std::runtime_error("alloc failed");
arena.reserved(); // 64k
arena.committed(); // 8k - 2 pages
arena.allocateed(); // 5k
arena.available(); // 3k
2. Expand committed memory explicitly:
.. code-block:: cpp
bool ok = arena.expand(5*1024);
assert(ok);
arena.reserved(); // 64k
arena.committed(); // 8k - 2 pages
arena.allocated(); // 0k
arena.available(); // 8k
Examining alloc metadata
------------------------
Given a successful allocation:
.. code-block:: cpp
std::size_t req_z = 5*1024;
std::byte * mem = arena.alloc(req_z);
if (!mem)
throw std::runtime_error("alloc failed");
AllocInfo info = arena.alloc_info(mem);
info.payload(); // [mem, mem + req_z (+ up to 7 bytes padding)]
info.is_valid(); // true
info.guard_lo(); // guard bytes preceding alloc
info.guard_hi(); // guard bytes following alloc
Recycling memory
----------------
.. code-block:: cpp
// arena in non-empty state
arena.reserved(); // 64k
arena.committed(); // 8k - 2 pages
arena.allocateed(); // 5k
arena.available(); // 3k
arena.clear();
arena.reserved() // 64k
arena.committed(); // 8k - 2 pages
arena.allocated(); // 0k
arena.available(); // 8k
Memory released by @ref Darena::clear is still committed.
It's in use as far as operating system is concerned.
To release memory to the operating system, destroy arena:
.. code-block:: cpp
arena.~DArena(); // or just let arena go out of scope

View file

@ -1,7 +1,7 @@
.. _implementation: .. _implementation:
Components Implementation
========== ==============
Library dependency tower for *xo-alloc2* Library dependency tower for *xo-alloc2*
@ -15,36 +15,147 @@ Library dependency tower for *xo-alloc2*
| xo_cmake | | xo_cmake |
+-----------------+ +-----------------+
Abstraction tower for *xo-alloc2* components Abstraction tower for *xo-alloc2* components (simplified)
.. ditaa:: .. ditaa::
:--scale: 0.99 :--scale: 0.99
+--------------------------------+ +----------------+-----------------+-------------------+
| IAllocator_DArena | | | | DArena |
+--------------------------------+ | Allocator | AllocIterator | DArenaIterator |
| IAllocator_Xfer | | | +-------------------+
| IAllocator_Any | | | | ArenaConfig |
+--------------+-----------------+ +----------------+-----------------+-------------------+
| | DArena | | auxiliary types |
| AAllocator +-----------------+ +------------------------------------------------------+
| | ArenaConfig |
+--------------+-----------------+
.. list-table:: Descriptions
Abstraction tower for *xo-alloc2* components (detailed)
.. ditaa::
:--scale: 0.99
+----------------------+-------------------------+-----------------------------------+
| RAllocator | RAllocIterator | IAllocator_DArena |
| | | IAllocIterator_DArenaIterator |
+----------------------+-------------------------+-----------------------------------+
| IAllocator_Xfer | IAllocIterator_Xfer | DArena |
| IAllocator_Any | IAllocIterator_Any | DArenaIterator |
| IAllocator_Impltype | IAllocIterator_Impltype | |
| | | |
+----------------------+-------------------------+-----------------------------------+
| AAllocator | AAllocIterator | ArenaConfig |
+----------------------+-------------------------+-----------------------------------+
+-----------------+----------------------------------------------+-------------------+
| | AllocInfo | |
| +----------------------------------------------+ |
| AllocError | AllocHeaderConfig | cmpresult |
| +----------------------------------------------+ |
| | AllocHeader | |
+-----------------+----------------------------------------------+-------------------+
.. list-table:: Polymorphic Allocator
:header-rows: 1 :header-rows: 1
:widths: 20 90 :widths: 20 90
* - Component * - Class
- Description - Description
* - ``AAllocator`` * - ``AAllocator``
- allocator facet (abstract interface) - Abstract allocator interface for runtime polymorphism
* - ``DArena`` * - ``IAllocator_Any``
- arena representation - Stub allocator interface for uninitialized variant
* - ``IAllocator_ImplType<D>``
- lookup implementation for allocator A
with representation D.
* - ``IAllocator_Xfer<D>`` * - ``IAllocator_Xfer<D>``
- transfer interface. downcast to native state. - Allocator interface template for representation ``D``
* - ``IAllocator_DArena`` * - ``IAllocator_Impltype<D>``
- allocator implementation for ``DArena`` - Lookup allocator interface for representation ``D``
* - ``RAllocator<O>``
- Provide allocator methods for FOMO object ``O``
.. list-table:: Polymorphich Alloc Iterator
:header-rows: 1
:widths: 20 90
* - Class
- Description
* - ``AAllocIterator``
- Abstract interface for iteration over allocs
* - ``IAllocIterator_Any``
- Stub alloc-iterator interface for uninitialized variant
* - ``IAllocIterator_Xfer<D>``
- Alloc-iterator interface template for representation ``D``
* - ``IAllocIterator_Impltype<D>``
- Lookup alloc-iterator interface for representation ``D``
* - ``RAllocIterator<D>``
- Provide alloc-iterator methods for FOMO object ``O``.
.. list-table:: Native Arena Allocator
:header-rows: 1
:widths: 20 90
* - Class
- Description
* - ``ArenaConfig``
- Configuration for a ``DArena`` instance
* - ``DArena``
- VM-aware arena allocator
* - ``DArenaIterator``
- Iterator over ``DArena`` allocations
* - ``IAlllocator_DArena``
- Adapt a ``DArena`` to facet ``AAllocator``
* - ``IAllocIterator_DArenaAllocator``
- Adapt a ``DArenaIterator`` to facet ``AAllocIterator``
.. list-table:: Auxiliary/Support Types
:header-rows: 1
:widths: 20 90
* - Class
- Description
* - ``AllocError``
- Return type for an alloc request, with error details.
* - ``AllocInfo``
- An opaque allocation. Value of an alloc-iterator.
* - ``AllocHeaderConfig``
- Per-allocator configuration of alloc headers
* - ``AllocHeader``
- Per-allocation header (8 bytes)
* - ``cmpresult``
- Result of alloc-iterator comparison
Example Object Diagram
.. uml::
:caption: representation for an arena allocator
:scale: 99%
:align: center
object rarena1<<RAllocator>>
rarena1 : iface = vtable1
rarena1 : data = darena1
object vtable1<<IAllocator_DArena_vtable>>
vtable1 : alloc()
object darena1<<DArena>>
darena1 : config
darena1 : lo
darena1 : hi
darena1 : free
darena1 : limit
darena1 : last_error
rarena1 o-- vtable1
rarena1 o-- darena1
Remarks:
* When we know the allocator representation at compile time (``DArena`` here),
then we also know the interface (``IAllocator_DArena``).
Devirtualization is easy since interface methods are all final.
* Size of a FOMO object is two pointers; it's natural to create such objects
on the fly and pass them by value.
When storing an allocator in another data structure, we only need to use
the RAllocator stack if we want runtime polymorphism for the stored allocator.
Otherwise can store a ``DArena`` instance.

View file

@ -26,6 +26,7 @@ Implemented using FOMO (faceted rust-like object model) from xo-facet
AAllocator-reference AAllocator-reference
ArenaConfig-reference ArenaConfig-reference
DArena-reference DArena-reference
AllocInfo-reference
glossary glossary
genindex genindex
search search

View file

@ -14,7 +14,7 @@ namespace xo {
/** sentinel **/ /** sentinel **/
invalid = -1, invalid = -1,
/** not an error **/ /** not an error **/
none, ok,
/** reserved size exhauged **/ /** reserved size exhauged **/
reserve_exhausted, reserve_exhausted,
/** unable to commit (i.e. mprotect failure) **/ /** unable to commit (i.e. mprotect failure) **/
@ -57,8 +57,10 @@ namespace xo {
committed_z_{com_z}, committed_z_{com_z},
reserved_z_{rsv_z} {} reserved_z_{rsv_z} {}
static const char * error_description(error x);
/** error code **/ /** error code **/
error error_ = error::none; error error_ = error::ok;
/** sequence# of this error. /** sequence# of this error.
* Each error event within an allocator gets next sequence number * Each error event within an allocator gets next sequence number

View file

@ -18,10 +18,18 @@ namespace xo {
* *
**/ **/
struct AllocInfo { struct AllocInfo {
/** @defgroup mm-allocinfo-traits **/
///@{
using size_type = AllocHeader::size_type; using size_type = AllocHeader::size_type;
using byte = std::byte; using byte = std::byte;
using span_type = std::pair<const byte *, const byte *>; using span_type = std::pair<const byte *, const byte *>;
///@}
/** @defgroup mm-allocinfo-ctors **/
///@{
AllocInfo(const AllocHeaderConfig * p_cfg, AllocInfo(const AllocHeaderConfig * p_cfg,
const byte * p_guard_lo, const byte * p_guard_lo,
const AllocHeader * p_hdr, const AllocHeader * p_hdr,
@ -39,6 +47,11 @@ namespace xo {
return AllocInfo(p_cfg, nullptr, nullptr, nullptr); return AllocInfo(p_cfg, nullptr, nullptr, nullptr);
} }
///@}
/** @defgroup mm-allocinfo-methods **/
///@{
/** true for non-sentinel AllocInfo instance **/ /** true for non-sentinel AllocInfo instance **/
bool is_valid() const { return (p_config_ != nullptr) && (p_header_ != nullptr); } bool is_valid() const { return (p_config_ != nullptr) && (p_header_ != nullptr); }
@ -59,10 +72,17 @@ namespace xo {
/** Value (fixed test pattern) of guard byte **/ /** Value (fixed test pattern) of guard byte **/
char guard_byte() const noexcept { return p_config_->guard_byte_; } char guard_byte() const noexcept { return p_config_->guard_byte_; }
///@}
/** @defgroup mm-allocinfo-instance-vars **/
///@{
const AllocHeaderConfig * p_config_ = nullptr; const AllocHeaderConfig * p_config_ = nullptr;
const byte * p_guard_lo_ = nullptr; const byte * p_guard_lo_ = nullptr;
const AllocHeader * p_header_ = nullptr; const AllocHeader * p_header_ = nullptr;
const byte * p_guard_hi_ = nullptr; const byte * p_guard_hi_ = nullptr;
///@}
}; };
} /*namespace mm*/ } /*namespace mm*/
} /*namespace xo*/ } /*namespace xo*/

View file

@ -25,6 +25,9 @@ namespace xo {
/** @class AAllocator /** @class AAllocator
* @brief Abstract facet for allocation * @brief Abstract facet for allocation
* *
* Methods take a opaque data pointer.
* Implementations of AAllocator will downcast to a
* to some specific representation.
**/ **/
struct AAllocator { struct AAllocator {
/** @defgroup mm-allocator-type-traits allocator type traits **/ /** @defgroup mm-allocator-type-traits allocator type traits **/
@ -56,17 +59,16 @@ namespace xo {
/** RTTI: unique id# for actual runtime data representation **/ /** RTTI: unique id# for actual runtime data representation **/
virtual int32_t _typeseq() const noexcept = 0; virtual int32_t _typeseq() const noexcept = 0;
/** optional name for allocator @p d /** optional name for allocator @p d .
* Labeling, for diagnostics. * Allows labeling allocators, for diagnostics/instrumentation.
**/ **/
virtual std::string_view name(Copaque d) const noexcept = 0; virtual std::string_view name(Copaque d) const noexcept = 0;
/** reserved size in bytes for allocator @p d. /** reserved size in bytes for allocator @p d.
* Includes committed + uncommitted memory. * Includes committed + uncommitted memory.
* Cannot be increased.
**/ **/
virtual size_type reserved(Copaque d) const noexcept = 0; virtual size_type reserved(Copaque d) const noexcept = 0;
/** Synonym for @ref committed. /** Synonym for @ref committed.
* Can increase on @ref alloc * Can increase automatically on @ref alloc
**/ **/
virtual size_type size(Copaque d) const noexcept = 0; virtual size_type size(Copaque d) const noexcept = 0;
/** committed size (physical addresses obtained) /** committed size (physical addresses obtained)
@ -74,14 +76,22 @@ namespace xo {
* @ref alloc may auto-increase this * @ref alloc may auto-increase this
**/ **/
virtual size_type committed(Copaque d) const noexcept = 0; virtual size_type committed(Copaque d) const noexcept = 0;
/** unallocated (but committed) size in bytes for allocator @p d **/ /** unallocated (but committed) size in bytes for allocator @p d.
* An alloc request up to this size (including guard / header)
* is guaranteed to succeed.
* An alloc request of more than this size may still succeed,
* if allocator can automatically extend committed memory.
* This is the case for the @ref xo::mm::DArena allocator
**/
virtual size_type available(Copaque d) const noexcept = 0; virtual size_type available(Copaque d) const noexcept = 0;
/** allocated (i.e. in-use) amount in bytes for allocator @p d **/ /** allocated (i.e. currently in-use) amount in bytes for allocator @p d.
* Includes alloc headers and guard regions
**/
virtual size_type allocated(Copaque d) const noexcept = 0; virtual size_type allocated(Copaque d) const noexcept = 0;
/** true iff allocator @p d is responsible for memory at address @p p. /** true iff allocator @p d is responsible for memory at address @p p.
**/ **/
virtual bool contains(Copaque d, const void * p) const noexcept = 0; virtual bool contains(Copaque d, const void * p) const noexcept = 0;
/** report last error **/ /** report details of last error for allocator @p d. **/
virtual AllocError last_error(Copaque d) const noexcept = 0; virtual AllocError last_error(Copaque d) const noexcept = 0;
/** fetch alloc info: given memory @p mem previously obtained /** fetch alloc info: given memory @p mem previously obtained
* from {@ref alloc, @ref super_alloc}, get {tseq, age, size} details * from {@ref alloc, @ref super_alloc}, get {tseq, age, size} details
@ -90,28 +100,23 @@ namespace xo {
* Non-const @p d because may stash error details * Non-const @p d because may stash error details
**/ **/
virtual AllocInfo alloc_info(Copaque d, value_type mem) const noexcept = 0; virtual AllocInfo alloc_info(Copaque d, value_type mem) const noexcept = 0;
/** Ideally we want to control allocator for iterator here. /**
* Awkward to supply to compiler since we don't have obj<AAllocator> yet. * Create an iterator range for allocator @p d.
* OTOH iteration over allocs is a super-niche feature. * An iterator range has begin and end methods, so supports c++ range iteration.
* * Memory for iterator state will be obtained from @p mm.
* Rejected alternatives:
* - put begin/end in separate interface. e.g. extend AAllocator
* - layer of indirection: begin/end return iterator factory.
* Then allocator can be passed to iterator factory separately.
* Helps because factory can be static
* - abandon allocator support in this case. Instead will need to
* reinstate uvt<AAllocIterator> (unique variant), use heap
*
* @p mm is allocator for resulting iterator range
**/ **/
virtual range_type alloc_range(Copaque d, DArena & mm) const noexcept = 0; virtual range_type alloc_range(Copaque d, DArena & mm) const noexcept = 0;
/** expand committed space in arena @p d /** expand committed space in arena @p d
* to size at least @p z * to size at least @p z.
* In practice will round up to a multiple of hugepage size (2MB) * In practice will round up to a multiple of page size (4K) or hugepage size (2MB)
* depending on configuration.
**/ **/
virtual bool expand(Opaque d, std::size_t z) const noexcept = 0; virtual bool expand(Opaque d, std::size_t z) const noexcept = 0;
/** allocate @p z bytes of memory from allocator @p d. **/ /** attempt to allocate @p z bytes of memory from allocator @p d.
* If allocation fails returns nullptr. In this case error details may be retrieved
* using last error
**/
virtual value_type alloc(Opaque d, size_type z) const = 0; virtual value_type alloc(Opaque d, size_type z) const = 0;
/** like @ref alloc, but follow with one or more consecutive /** like @ref alloc, but follow with one or more consecutive
* @ref sub_alloc() calls. This sequence of allocs will share * @ref sub_alloc() calls. This sequence of allocs will share
@ -125,9 +130,11 @@ namespace xo {
* zero @p z * zero @p z
**/ **/
virtual value_type sub_alloc(Opaque d, size_type z, bool complete_flag) const = 0; virtual value_type sub_alloc(Opaque d, size_type z, bool complete_flag) const = 0;
/** reset allocator @p d to empty state **/ /** reset allocator @p d to empty state. **/
virtual void clear(Opaque d) const = 0; virtual void clear(Opaque d) const = 0;
/** destruct allocator @p d **/ /** Destruct allocator @p d.
* Releases allocator memory to operating system.
**/
virtual void destruct_data(Opaque d) const = 0; virtual void destruct_data(Opaque d) const = 0;
///@} ///@}

View file

@ -47,9 +47,15 @@ namespace xo {
/** @brief mode argument for @ref _alloc **/ /** @brief mode argument for @ref _alloc **/
enum class alloc_mode : uint8_t { enum class alloc_mode : uint8_t {
/** ordinary alloc. Most common mode **/
standard, standard,
/** begin a sequence of suballocs that share a single alloc header **/
super, super,
/** make a subsidiary allocation on behalf of a preceding super alloc.
* Will be followed by at least one more suballoc call.
**/
sub_incomplete, sub_incomplete,
/** make a subsidiary allocation that completes preceding super alloc. **/
sub_complete, sub_complete,
}; };
@ -64,7 +70,11 @@ namespace xo {
/** null ctor **/ /** null ctor **/
DArena() = default; DArena() = default;
/** ctor from already-mapped (but not committed) address range **/ /** ctor from already-mapped (but not committed) address range **/
DArena(const ArenaConfig & cfg, size_type page_z, value_type lo, value_type hi); DArena(const ArenaConfig & cfg,
size_type page_z,
size_type arena_align_z,
value_type lo,
value_type hi);
/** DArena is not copyable **/ /** DArena is not copyable **/
DArena(const DArena & other) = delete; DArena(const DArena & other) = delete;
/** move ctor **/ /** move ctor **/
@ -80,18 +90,49 @@ namespace xo {
/** @defgroup mm-arena-methods **/ /** @defgroup mm-arena-methods **/
///@{ ///@{
/** Reserved memory, in bytes. This is the maximum size of this arena. **/
size_type reserved() const noexcept { return hi_ - lo_; } size_type reserved() const noexcept { return hi_ - lo_; }
/** Allocated memory in bytes: memory consumed by allocs from this arena,
* including administrative overhead (alloc headers + guard bytes)
**/
size_type allocated() const noexcept { return free_ - lo_; } size_type allocated() const noexcept { return free_ - lo_; }
/** Committed memory in bytes: amount of memory actually backed by physical memory **/
size_type committed() const noexcept { return committed_z_; } size_type committed() const noexcept { return committed_z_; }
/** Available committed memory.
* This is the amount of memory guaranteed to be usable for future allocs from this arena.
**/
size_type available() const noexcept { return limit_ - free_; } size_type available() const noexcept { return limit_ - free_; }
/** True iff address @p addr is owned by this arena,
* i.e. falls within [@ref lo_, @ref hi_)
**/
bool contains(const void * addr) const noexcept { return (lo_ <= addr) && (addr < hi_); } bool contains(const void * addr) const noexcept { return (lo_ <= addr) && (addr < hi_); }
/** obtain uncommitted contiguous memory range comprising /** obtain uncommitted contiguous memory range comprising
* a whole multiple of @p hugepage_z bytes, of at least size @p req_z, * a whole multiple of @p align_z bytes, of at least size @p req_z,
* aligned on a @p hugepage_z boundary * aligned on a @p align_z boundary. Uncommitted memory is not (yet)
* backed by physical memory.
*
* If @p enable_hugepage_flag is true and THP
* (transparent huge pages) are available, use THP for arena memory.
* This relieves TLB and page table memory when @p req_z is a lot larger than
* page size (likely 4KB). Cost is that arena will consum physical memory in unit
* of @p align_z. Arena may waste up to @p align_z bytes of memory as a result.
*
* If @p enable_hugepage_flag is true, @p align_z should be huge page size
* (probably 2MB) for optimal performance.
*
* At present the THP feature is not supported on OSX.
* May be supportable through mach_vm_allocate().
*
* Note that we reject MAP_HUGETLB|MAP_HUGE_2MB flags to mmap here,
* since requires previously-reserved memory in /proc/sys/vm/nr_hugepages.
*
* @return pair giving reserved memory address range [lo,hi)
**/ **/
static range_type map_aligned_range(size_type req_z, size_type hugepage_z); static range_type map_aligned_range(size_type req_z,
size_type align_z,
bool enable_hugepage_flag);
/** true if arena is mapped i.e. has a reserved address range **/ /** true if arena is mapped i.e. has a reserved address range **/
bool is_mapped() const noexcept { return (lo_ != nullptr) && (hi_ != nullptr); } bool is_mapped() const noexcept { return (lo_ != nullptr) && (hi_ != nullptr); }
@ -177,6 +218,9 @@ namespace xo {
/** size of a VM page (obtained automatically via getpagesize()). Likely 4k **/ /** size of a VM page (obtained automatically via getpagesize()). Likely 4k **/
size_type page_z_ = 0; size_type page_z_ = 0;
/** alignment for this arena. In practice will be either page_z_ or cfg.hugepage_z_ **/
size_type arena_align_z_ = 0;
/** arena owns memory in range [@ref lo_, @ref hi_) /** arena owns memory in range [@ref lo_, @ref hi_)
**/ **/
std::byte * lo_ = nullptr; std::byte * lo_ = nullptr;

View file

@ -0,0 +1,34 @@
/** @file print.hpp
*
* @author Roland Conybeare, Dec 2025
**/
#pragma once
#include "AllocError.hpp"
#include <xo/indentlog/print/tag.hpp>
#include <iostream>
namespace xo {
namespace mm {
inline std::ostream &
operator<<(std::ostream & os, const error & x) {
os << AllocError::error_description(x);
return os;
}
inline std::ostream &
operator<<(std::ostream & os, const AllocError & x) {
os << "<AllocError"
<< xtag("error", x.error_)
<< xtag("seq", x.error_seq_)
<< xtag("req_z", x.request_z_)
<< xtag("commit_z", x.committed_z_)
<< xtag("resv_z", x.reserved_z_)
<< ">";
return os;
}
}
}
/* end print.hpp */

View file

45
src/alloc2/AllocError.cpp Normal file
View file

@ -0,0 +1,45 @@
/** @file AllocError.cpp
*
* @author Roland Conybeare, Dec 2025
**/
#include "AllocError.hpp"
namespace xo {
namespace mm {
const char *
AllocError::error_description(error x)
{
switch (x) {
case error::invalid:
break;
case error::ok:
return "ok";
case error::reserve_exhausted:
return "reserve-exhausted";
case error::commit_failed:
return "commit-failed";
case error::header_size_mask:
return "header-size-mask";
case error::orphan_sub_alloc:
return "orphan-sub-alloc";
case error::alloc_info_disabled:
return "alloc-info-disabled";
case error::alloc_info_address:
return "alloc-info-address";
case error::alloc_iterator_not_supported:
return "alloc-iterator-not-supported";
case error::alloc_iterator_deref:
return "alloc-iterator-deref";
case error::alloc_iterator_next:
return "alloc-iterator-next";
}
return "?error";
}
} /*namespace mm*/
} /*namespace xo*/
/* end AllocError.cpp */

View file

@ -3,6 +3,7 @@
set(SELF_LIB xo_alloc2) set(SELF_LIB xo_alloc2)
set(SELF_SRCS set(SELF_SRCS
AllocError.cpp
AllocInfo.cpp AllocInfo.cpp
cmpresult.cpp cmpresult.cpp

View file

@ -19,56 +19,30 @@ namespace xo {
using std::size_t; using std::size_t;
namespace mm { namespace mm {
/** 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 auto
DArena::map_aligned_range(size_t req_z, size_t hugepage_z) -> range_type DArena::map_aligned_range(size_t req_z,
size_t align_z,
bool enable_hugepage_flag) -> range_type
{ {
// 1. round up to multiple of hugepage_z // 1. round up to multiple of align_z
size_t target_z = padding::with_padding(req_z, hugepage_z); // 4. size_t target_z = padding::with_padding(req_z, align_z); // 4.
// 2. mmap() will give us page-aligned memory, // 2. mmap() will give us page-aligned memory,
// but not hugepage-aligned. // but not hugepage-aligned.
// //
// Over-request by hugepage_z to ensure // Over-request by align_z to ensure
// hugepage-aligned subrange of size target_z // aligned subrange of size target_z
// //
byte * base = (byte *)(::mmap(nullptr, byte * base = (byte *)(::mmap(nullptr,
target_z + hugepage_z, target_z + align_z,
PROT_NONE, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0)); -1, 0));
// on mmap success: upper limit of mapped address range // on mmap success: upper limit of mapped address range
byte * hi = base + (target_z + hugepage_z); byte * hi = base + (target_z + align_z);
// lowest hugepage-aligned address in [base, hi) // lowest hugepage-aligned address in [base, hi)
byte * aligned_base = (byte *)(padding::with_padding((size_t)base, hugepage_z)); byte * aligned_base = (byte *)(padding::with_padding((size_t)base, align_z));
// end of hugeppage-aligned range starting at aligned_base // end of hugeppage-aligned range starting at aligned_base
byte * aligned_hi = aligned_base + target_z; byte * aligned_hi = aligned_base + target_z;
@ -88,9 +62,9 @@ namespace xo {
xtag("size", req_z))); xtag("size", req_z)));
} }
assert((size_t)aligned_base % hugepage_z == 0); assert((size_t)aligned_base % align_z == 0);
assert(aligned_base >= base); assert(aligned_base >= base);
assert(aligned_base < base + hugepage_z); assert(aligned_base < base + align_z);
} }
// 4. release unaligned prefix // 4. release unaligned prefix
@ -108,6 +82,7 @@ namespace xo {
} }
#ifdef __linux__ #ifdef __linux__
if (enable_hugepage_flag) {
/** linux: /** linux:
* opt-in to transparent huge pages (THP) * opt-in to transparent huge pages (THP)
* provided OS configured to support them. * provided OS configured to support them.
@ -121,8 +96,11 @@ namespace xo {
* Much better than ~500us to commit 512 4k VM pages. * Much better than ~500us to commit 512 4k VM pages.
* *
* But wasted if we don't use the memory. * But wasted if we don't use the memory.
*
* Page table has a handful of levels
**/ **/
::madvise(aligned_base, target_z, MADV_HUGEPAGE); // 8. ::madvise(aligned_base, target_z, MADV_HUGEPAGE); // 8.
}
#endif #endif
return std::make_pair(aligned_base, aligned_hi); return std::make_pair(aligned_base, aligned_hi);
@ -133,7 +111,20 @@ namespace xo {
{ {
//scope log(XO_DEBUG(debug_flag), xtag("name", name)); //scope log(XO_DEBUG(debug_flag), xtag("name", name));
auto [lo, hi] = map_aligned_range(cfg.size_, cfg.hugepage_z_); /* vm page size. 4KB, probably */
size_t page_z = getpagesize();
bool enable_hugepage_flag = (cfg.size_ >= cfg.hugepage_z_);
/* Align start of arena memory on this boundary.
* Will use THP (transparent huge pages) if available
* and arena size is at least as large as hugepage size (2MB, probably)
*/
size_t align_z = (enable_hugepage_flag ? cfg.hugepage_z_ : page_z);
auto [lo, hi] = map_aligned_range(cfg.size_,
align_z,
enable_hugepage_flag);
if (!lo) { if (!lo) {
// control here implies mmap() failed silently // control here implies mmap() failed silently
@ -142,8 +133,6 @@ namespace xo {
xtag("size", cfg.size_))); xtag("size", cfg.size_)));
} }
size_t page_z = getpagesize();
#ifdef NOPE #ifdef NOPE
log && log(xtag("lo", (void*)lo_), log && log(xtag("lo", (void*)lo_),
@ -151,14 +140,16 @@ namespace xo {
xtag("hugepage_z", hugepage_z_)); xtag("hugepage_z", hugepage_z_));
#endif #endif
return DArena(cfg, page_z, lo, hi); return DArena(cfg, page_z, align_z, lo, hi);
} /*map*/ } /*map*/
DArena::DArena(const ArenaConfig & cfg, DArena::DArena(const ArenaConfig & cfg,
size_type page_z, size_type page_z,
size_type arena_align_z,
byte * lo, byte * lo,
byte * hi) : config_{cfg}, byte * hi) : config_{cfg},
page_z_{page_z}, page_z_{page_z},
arena_align_z_{arena_align_z},
lo_{lo}, lo_{lo},
committed_z_{0}, committed_z_{0},
free_{lo}, free_{lo},
@ -177,6 +168,7 @@ namespace xo {
DArena::DArena(DArena && other) { DArena::DArena(DArena && other) {
config_ = other.config_; config_ = other.config_;
page_z_ = other.page_z_; page_z_ = other.page_z_;
arena_align_z_ = other.arena_align_z_;
lo_ = other.lo_; lo_ = other.lo_;
committed_z_ = other.committed_z_; committed_z_ = other.committed_z_;
free_ = other.free_; free_ = other.free_;
@ -200,6 +192,7 @@ namespace xo {
{ {
config_ = other.config_; config_ = other.config_;
page_z_ = other.page_z_; page_z_ = other.page_z_;
arena_align_z_ = other.arena_align_z_;
lo_ = other.lo_; lo_ = other.lo_;
committed_z_ = other.committed_z_; committed_z_ = other.committed_z_;
free_ = other.free_; free_ = other.free_;
@ -529,23 +522,25 @@ namespace xo {
* *
*/ */
std::size_t aligned_target_z = padding::with_padding(target_z, config_.hugepage_z_); std::size_t aligned_target_z = padding::with_padding(target_z, arena_align_z_);
std::byte * commit_start = limit_; // = lo_ + committed_z_; std::byte * commit_start = limit_; // = lo_ + committed_z_;
std::size_t add_commit_z = aligned_target_z - committed_z_; std::size_t add_commit_z = aligned_target_z - committed_z_;
assert(limit_ == lo_ + committed_z_); assert(limit_ == lo_ + committed_z_);
// log && log(xtag("aligned_offset_z", aligned_offset_z),
// xtag("add_commit_z", add_commit_z));
// log && log("expand committed range",
// xtag("commit_start", commit_start),
// xtag("add_commit_z", add_commit_z),
// xtag("commit_end", commit_start + add_commit_z));
if (::mprotect(commit_start, if (::mprotect(commit_start,
add_commit_z, add_commit_z,
PROT_READ | PROT_WRITE) != 0) [[unlikely]] PROT_READ | PROT_WRITE) != 0) [[unlikely]]
{ {
if (log) {
log("commit failed!");
log(xtag("aligned_target_z", aligned_target_z),
xtag("commit_start", commit_start),
xtag("add_commit_z", add_commit_z),
xtag("commit_end", commit_start + add_commit_z)
);
}
capture_error(error::commit_failed, add_commit_z); capture_error(error::commit_failed, add_commit_z);
return false; return false;
} }
@ -563,8 +558,8 @@ namespace xo {
free_ += config_.header_.guard_z_; free_ += config_.header_.guard_z_;
} }
assert(committed_z_ % config_.hugepage_z_ == 0); assert(committed_z_ % arena_align_z_ == 0);
assert(reinterpret_cast<size_t>(limit_) % config_.hugepage_z_ == 0); assert(reinterpret_cast<size_t>(limit_) % arena_align_z_ == 0);
return true; return true;
} /*expand*/ } /*expand*/

View file

@ -8,9 +8,10 @@
//#include "xo/alloc2/DArena.hpp" //#include "xo/alloc2/DArena.hpp"
#include "xo/alloc2/arena/IAllocator_DArena.hpp" #include "xo/alloc2/arena/IAllocator_DArena.hpp"
//#include "xo/alloc2/alloc/RAllocator.hpp" //#include "xo/alloc2/alloc/RAllocator.hpp"
#include "xo/alloc2/print.hpp"
#include "xo/alloc2/padding.hpp" #include "xo/alloc2/padding.hpp"
#include "xo/indentlog/scope.hpp" #include <xo/facet/obj.hpp>
#include "xo/facet/obj.hpp" #include <xo/indentlog/scope.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
namespace xo { namespace xo {
@ -37,12 +38,55 @@ namespace xo {
REQUIRE(IAllocator_Xfer<DArena, IAllocator_DArena>::_valid); REQUIRE(IAllocator_Xfer<DArena, IAllocator_DArena>::_valid);
} }
TEST_CASE("DArena", "[alloc2][DArena]") TEST_CASE("DArena-tiny", "[alloc2][DArena]")
{ {
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
.size_ = 1 }; .size_ = 1 };
DArena arena = DArena::map(cfg); 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_) % arena.page_z_ == 0);
REQUIRE(arena.lo_ + cfg.size_ <= arena.hi_);
/* verify arena.lo_ is aligned on a page boundary */
REQUIRE(((size_t)(arena.lo_) & (arena.page_z_ - 1)) == 0);
/* verify arena.hi_ is aligned on a hugepage boundary */
REQUIRE(((size_t)(arena.hi_) & (arena.page_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("DArena-medium", "[alloc2][DArena]")
{
ArenaConfig cfg { .name_ = "testarena",
.size_ = 10*1024*1024 };
DArena arena = DArena::map(cfg);
REQUIRE(arena.config_.name_ == cfg.name_); REQUIRE(arena.config_.name_ == cfg.name_);
REQUIRE(arena.lo_ != nullptr); REQUIRE(arena.lo_ != nullptr);
REQUIRE(arena.free_ == arena.lo_); REQUIRE(arena.free_ == arena.lo_);
@ -52,7 +96,7 @@ namespace xo {
REQUIRE(((size_t)arena.hi_ - (size_t)arena.lo_) % cfg.hugepage_z_ == 0); REQUIRE(((size_t)arena.hi_ - (size_t)arena.lo_) % cfg.hugepage_z_ == 0);
REQUIRE(arena.lo_ + cfg.size_ <= arena.hi_); REQUIRE(arena.lo_ + cfg.size_ <= arena.hi_);
/* verify arena.lo_ is aligned on a hugepage boundary */ /* verify arena.lo_ is aligned on a page boundary */
REQUIRE(((size_t)(arena.lo_) & (cfg.hugepage_z_ - 1)) == 0); REQUIRE(((size_t)(arena.lo_) & (cfg.hugepage_z_ - 1)) == 0);
/* verify arena.hi_ is aligned on a hugepage boundary */ /* verify arena.hi_ is aligned on a hugepage boundary */
@ -103,8 +147,8 @@ namespace xo {
== xo::facet::FacetImplType<AAllocator, DArena>::s_typeseq); == xo::facet::FacetImplType<AAllocator, DArena>::s_typeseq);
REQUIRE(a1o.name() == cfg.name_); REQUIRE(a1o.name() == cfg.name_);
REQUIRE(a1o.reserved() >= cfg.size_); REQUIRE(a1o.reserved() >= cfg.size_);
REQUIRE(a1o.reserved() < cfg.size_ + cfg.hugepage_z_); REQUIRE(a1o.reserved() < cfg.size_ + a1o.data()->page_z_);
REQUIRE(a1o.reserved() % cfg.hugepage_z_ == 0); REQUIRE(a1o.reserved() % a1o.data()->page_z_ == 0);
REQUIRE(a1o.size() == 0); REQUIRE(a1o.size() == 0);
REQUIRE(a1o.committed() == 0); REQUIRE(a1o.committed() == 0);
REQUIRE(a1o.allocated() == 0); REQUIRE(a1o.allocated() == 0);
@ -123,11 +167,15 @@ namespace xo {
REQUIRE(a1o.allocated() == 0); REQUIRE(a1o.allocated() == 0);
size_t z2 = 512; size_t z2 = 512;
REQUIRE(a1o.expand(z2)); bool ok = a1o.expand(z2);
REQUIRE(a1o.reserved() % cfg.hugepage_z_ == 0); INFO(xtag("last_error", a1o.last_error()));
REQUIRE(ok);
REQUIRE(a1o.reserved() % a1o.data()->page_z_ == 0);
REQUIRE(a1o.committed() >= z2); REQUIRE(a1o.committed() >= z2);
REQUIRE(a1o.committed() % cfg.hugepage_z_ == 0); REQUIRE(a1o.committed() % a1o.data()->page_z_ == 0);
/* .size() is synonym for .committed() */ /* .size() is synonym for .committed() */
REQUIRE(a1o.size() == a1o.committed()); REQUIRE(a1o.size() == a1o.committed());
REQUIRE(a1o.available() >= z2); REQUIRE(a1o.available() >= z2);
@ -154,7 +202,7 @@ namespace xo {
byte * m0 = a1o.alloc(1); byte * m0 = a1o.alloc(1);
REQUIRE(m0); REQUIRE(m0);
REQUIRE(a1o.last_error().error_ == error::none); REQUIRE(a1o.last_error().error_ == error::ok);
REQUIRE(a1o.last_error().error_seq_ == 0); REQUIRE(a1o.last_error().error_seq_ == 0);
REQUIRE(a1o.allocated() >= z0); REQUIRE(a1o.allocated() >= z0);
REQUIRE(a1o.allocated() < z0 + padding::c_alloc_alignment ); REQUIRE(a1o.allocated() < z0 + padding::c_alloc_alignment );
@ -166,7 +214,7 @@ namespace xo {
byte * m1 = a1o.alloc(z1); byte * m1 = a1o.alloc(z1);
REQUIRE(m1); REQUIRE(m1);
REQUIRE(a1o.last_error().error_ == error::none); REQUIRE(a1o.last_error().error_ == error::ok);
REQUIRE(a1o.last_error().error_seq_ == 0); REQUIRE(a1o.last_error().error_seq_ == 0);
REQUIRE(a1o.allocated() >= z0 + z1); REQUIRE(a1o.allocated() >= z0 + z1);
REQUIRE(a1o.allocated() < z0 + z1 + 2 * padding::c_alloc_alignment ); REQUIRE(a1o.allocated() < z0 + z1 + 2 * padding::c_alloc_alignment );
@ -209,7 +257,7 @@ namespace xo {
REQUIRE(a1o.contains(header)); REQUIRE(a1o.contains(header));
REQUIRE(cfg.header_.size(*header) == padding::with_padding(z0)); REQUIRE(cfg.header_.size(*header) == padding::with_padding(z0));
//REQUIRE(((*header) & cfg.header_size_mask_) == padding::with_padding(z0)); //REQUIRE(((*header) & cfg.header_size_mask_) == padding::with_padding(z0));
REQUIRE(a1o.last_error().error_ == error::none); REQUIRE(a1o.last_error().error_ == error::ok);
REQUIRE(a1o.last_error().error_seq_ == 0); REQUIRE(a1o.last_error().error_seq_ == 0);
REQUIRE(a1o.allocated() >= z0); REQUIRE(a1o.allocated() >= z0);
REQUIRE(a1o.allocated() < sizeof(AAllocator::header_type) + z0 + padding::c_alloc_alignment ); REQUIRE(a1o.allocated() < sizeof(AAllocator::header_type) + z0 + padding::c_alloc_alignment );
@ -265,7 +313,7 @@ namespace xo {
REQUIRE(cfg.header_.size(*header) == padding::with_padding(z0)); REQUIRE(cfg.header_.size(*header) == padding::with_padding(z0));
//REQUIRE(((*header) & cfg.header_size_mask_) == padding::with_padding(z0)); //REQUIRE(((*header) & cfg.header_size_mask_) == padding::with_padding(z0));
REQUIRE(a1o.last_error().error_ == error::none); REQUIRE(a1o.last_error().error_ == error::ok);
REQUIRE(a1o.last_error().error_seq_ == 0); REQUIRE(a1o.last_error().error_seq_ == 0);
REQUIRE(a1o.allocated() == (cfg.header_.guard_z_ REQUIRE(a1o.allocated() == (cfg.header_.guard_z_
@ -306,7 +354,7 @@ namespace xo {
REQUIRE(err.request_z_ >= z0); REQUIRE(err.request_z_ >= z0);
REQUIRE(err.request_z_ < z0 + padding::c_alloc_alignment); REQUIRE(err.request_z_ < z0 + padding::c_alloc_alignment);
REQUIRE(err.committed_z_ == 0); REQUIRE(err.committed_z_ == 0);
REQUIRE(err.reserved_z_ == cfg.hugepage_z_); REQUIRE(err.reserved_z_ == arena.reserved());
} }
} /*namespace ut*/ } /*namespace ut*/
} /*namespace xo*/ } /*namespace xo*/

View file

@ -80,7 +80,7 @@ namespace utest {
REQUIRE_ORFAIL(ok_flag, catch_flag, mem != nullptr); REQUIRE_ORFAIL(ok_flag, catch_flag, mem != nullptr);
REQUIRE_ORFAIL(ok_flag, catch_flag, mm.contains(mem)); REQUIRE_ORFAIL(ok_flag, catch_flag, mm.contains(mem));
REQUIRE_ORFAIL(ok_flag, catch_flag, mm.last_error().error_seq_ == 0); REQUIRE_ORFAIL(ok_flag, catch_flag, mm.last_error().error_seq_ == 0);
REQUIRE_ORFAIL(ok_flag, catch_flag, mm.last_error().error_ == xo::mm::error::none); REQUIRE_ORFAIL(ok_flag, catch_flag, mm.last_error().error_ == xo::mm::error::ok);
{ {
auto ix = allocs_by_lo_map.lower_bound(mem); auto ix = allocs_by_lo_map.lower_bound(mem);