xo-alloc2: ++ documentation + threshold size for THP feature
This commit is contained in:
parent
992e5a5a8c
commit
289751d3fd
18 changed files with 642 additions and 192 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
61
docs/AllocInfo-reference.rst
Normal file
61
docs/AllocInfo-reference.rst
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,81 @@ Size here is a hard maximum. It cannot be changed for this arena instance.
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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*/
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
34
include/xo/alloc2/print.hpp
Normal file
34
include/xo/alloc2/print.hpp
Normal 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 */
|
||||||
45
src/alloc2/AllocError.cpp
Normal file
45
src/alloc2/AllocError.cpp
Normal 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 */
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,21 +82,25 @@ namespace xo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
/** linux:
|
if (enable_hugepage_flag) {
|
||||||
* opt-in to transparent huge pages (THP)
|
/** linux:
|
||||||
* provided OS configured to support them.
|
* opt-in to transparent huge pages (THP)
|
||||||
* otherwise fallback gracefully.
|
* provided OS configured to support them.
|
||||||
*
|
* otherwise fallback gracefully.
|
||||||
* Huge pages -> use fewer TLB entries + faster
|
*
|
||||||
* shorter path through page table.
|
* 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.
|
* When we commit (i.e. obtain physical memory on page fault),
|
||||||
* Much better than ~500us to commit 512 4k VM pages.
|
* 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.
|
*
|
||||||
**/
|
* But wasted if we don't use the memory.
|
||||||
::madvise(aligned_base, target_z, MADV_HUGEPAGE); // 8.
|
*
|
||||||
|
* Page table has a handful of levels
|
||||||
|
**/
|
||||||
|
::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*/
|
||||||
|
|
|
||||||
|
|
@ -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*/
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue