+ docs in xo-alloc2/ + misc improvements

This commit is contained in:
Roland Conybeare 2025-12-11 22:18:09 -05:00
commit f52184f84c
29 changed files with 868 additions and 178 deletions

View file

@ -128,6 +128,6 @@ add_subdirectory(xo-imgui)
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# documentation. must follow add_subdirectory() for satellite projects # documentation. must follow add_subdirectory() for satellite projects
xo_umbrella_doxygen_deps(xo_facet xo_alloc indentlog xo_flatstring xo_ratio xo_unit xo_tokenizer xo_reader xo_interpreter xo_jit) xo_umbrella_doxygen_deps(xo_facet xo_alloc xo_alloc2 indentlog xo_flatstring xo_ratio xo_unit xo_tokenizer xo_reader xo_interpreter xo_jit)
xo_umbrella_doxygen_config() xo_umbrella_doxygen_config()
xo_umbrella_sphinx_config(index.rst docs/install.rst docs/glossary.rst) xo_umbrella_sphinx_config(index.rst docs/install.rst docs/glossary.rst)

29
LICENSE Normal file
View file

@ -0,0 +1,29 @@
Copyright (c) 2024 Roland Conybeare <git3ub@nym.hush.com>, All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of
external contributions to this project including patches, pull requests, etc.

View file

@ -187,6 +187,40 @@ $ cmake -B .build -S . -DXO_ENABLE_VULKAN=1 -DXO_ENABLE_EXAMPLES=1 -DCMAKE_INSTA
Currently not supporting a nix sandbox build for xo-imgui Currently not supporting a nix sandbox build for xo-imgui
## Directory Layout
(not in alphabetical order)
```
xo-umbrella2/
|
+- CMakeLists.txt top-level cmake config
+- cmake
| \- xo-bootstrap-macros.cmake configure xo cmake support for build
+- compile_commands.json symlink to path/to/build/compile_commands.json for LSP
|
+- conf.py sphinx config for project documentation
+- index.rst root of xo-umbrella2 doc tree
+- Doxyfile.in doxygen config template; cmake will prepare Doxyfile in build dir
+- _static/ static inputs to sphinx
|
+- etc
| +- hostegl/ sample video driver symlinks for WSL2
| \- hostubuntu/ sample video driver symlinks for ubuntu
|
+- default.nix top-level nix build (works w/ stock nixpkgs 25.05)
+- pkgs/ per-satellite nix builds. see xo-umbrella2/default.nix
| +- xo-callback.nix nix build for xo-umbrella2/xo-callback
| ..etc..
| \- xo-webutil.nix
|
+- xo-alloc/ xo-alloc subproject. independent git repo, using git subtree
+- xo-alloc2/ xo-alloc2 subproject.
..etc..
\- xo-webutil/
```
## To view docs from WSL ## To view docs from WSL
1. find wsl IP address 1. find wsl IP address

View file

@ -6,7 +6,7 @@
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'xo documentation' project = 'xo umbrella2 documentation'
copyright = '2025, Roland Conybeare' copyright = '2025, Roland Conybeare'
author = 'Roland Conybeare' author = 'Roland Conybeare'
@ -17,7 +17,7 @@ author = 'Roland Conybeare'
extensions = [ "breathe", extensions = [ "breathe",
"sphinx.ext.mathjax", # inline math "sphinx.ext.mathjax", # inline math
"sphinx.ext.autodoc", # generate info from docstrings "sphinx.ext.autodoc", # generate info from docstrings
# "sphinxcontrib.ditaa", # diagrams-through-ascii-art "sphinxcontrib.ditaa", # diagrams-through-ascii-art
"sphinxcontrib.plantuml", # text -> uml diagrams "sphinxcontrib.plantuml", # text -> uml diagrams
] ]

View file

@ -13,6 +13,7 @@ Some features: kalman filters, stochastic processes, complex event processing, s
docs/install docs/install
xo-facet/docs/index xo-facet/docs/index
xo-alloc2/docs/index
xo-alloc/docs/index xo-alloc/docs/index
xo-indentlog/docs/index xo-indentlog/docs/index
xo-flatstring/docs/index xo-flatstring/docs/index

View file

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

View file

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

View file

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

View file

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

View file

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

70
xo-alloc2/docs/README Normal file
View file

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

1
xo-alloc2/docs/_static/README vendored Normal file
View file

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

View file

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 303 KiB

Before After
Before After

39
xo-alloc2/docs/conf.py Normal file
View file

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

View file

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

View file

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

28
xo-alloc2/docs/index.rst Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,57 +19,39 @@ Abstraction tower for *xo-facet* components.
:--scale: 0.85 :--scale: 0.85
+--------------------------------+ +--------------------------------+
| obj(A,D) | | obj(A,D) |
+--------------------------------+ +--------------------------------+
| RRouter(A,D) | | RRouter(A,D) |
+--------------------------------+ +--------------------------------+
| OObject(A,D) | | OObject(A,D) |
+--------------------------------+ +--------------------------------+
| FacetImplmentationType(A,D) | | FacetImplType(A,D) |
+----------------+---------------+ +----------------+---------------+
| facet [A] | data [D] | | facet [A] | data [D] |
+----------------+---------------+ +----------------+---------------+
Decorated with sample method calls, to reveal type recovery
.. ditaa::
:--scale: 0.85
+--------------------------------+
| obj(A,D) | x.foo()
+--------------------------------+
| RRouter(A,D) | x.foo()
+--------------------------------+
| OObject(A,D) | x.iface_.foo(x.data_)
+--------------------------------+
| FacetImplmentationType(A,D) | x.foo(void*data)
+----------------+---------------+
| facet A | data D | virtual x.foo(void* data)
+----------------+---------------+
.. list-table:: Descriptions .. list-table:: Descriptions
:header-rows: 1 :header-rows: 1
:widths: 30 30 60 :widths: 18 30 50
* - Component * - Component
- Use - Use
- Description - Description
* - obj<A,D> * - ``obj<A,D>``
- x.foo() - ``x.foo()``
- convenience wrapper with interface A, with state D* - convenience wrapper with interface A, with state D*
* - RRouter<A,D> * - ``RRouter<A,D>``
- x.foo() - ``x.foo()``
- auto injects data pointer - auto injects data pointer
* - OObject<A,D> * - ``OObject<A,D>``
- x.iface()->foo(x.data(), ..) - ``x.iface()->foo(x.data(), ..)``
- fat object pointer. combine i/face + data pointer. - fat object pointer. combine i/face + data pointer.
* - FacetImplementationType<A,D> * - ``FacetImplType<A,D>``
- x.foo(void* data, ..) - ``x.foo(void* data, ..)``
- implement facet for a particular state datatype; - implement facet for a particular state datatype;
explicit type-erased state explicit type-erased state
* - facet * - facet
- x.foo(void* data, ..)=0 - ``x.foo(void* data, ..)=0``
- fully abstract interface; explicit type-erased state - fully abstract interface; explicit type-erased state
.. uml:: .. uml::

View file

@ -4,27 +4,29 @@
set(SELF_EXE utest.tree) set(SELF_EXE utest.tree)
set(SELF_SOURCE_FILES tree_utest_main.cpp redblacktree.cpp bplustree.cpp RedBlackTree-gc.test.cpp) set(SELF_SOURCE_FILES tree_utest_main.cpp redblacktree.cpp bplustree.cpp RedBlackTree-gc.test.cpp)
add_executable(${SELF_EXE} ${SELF_SOURCE_FILES}) if (ENABLE_TESTING)
xo_include_options2(${SELF_EXE}) add_executable(${SELF_EXE} ${SELF_SOURCE_FILES})
xo_include_options2(${SELF_EXE})
add_test(NAME ${SELF_EXE} COMMAND ${SELF_EXE}) add_test(NAME ${SELF_EXE} COMMAND ${SELF_EXE})
# need coverage on unit test b/c header-only libraries # need coverage on unit test b/c header-only libraries
#target_code_coverage(${SELF_EXE} EXCLUDE */nix/store* utest/*) #target_code_coverage(${SELF_EXE} EXCLUDE */nix/store* utest/*)
#target_code_coverage(${SELF_EXE} AUTO ALL) #target_code_coverage(${SELF_EXE} AUTO ALL)
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# internal dependencies: refcnt, ... # internal dependencies: refcnt, ...
xo_self_dependency(${SELF_EXE} xo_ordinaltree) xo_self_dependency(${SELF_EXE} xo_ordinaltree)
xo_dependency(${SELF_EXE} xo_object) xo_dependency(${SELF_EXE} xo_object)
xo_dependency(${SELF_EXE} xo_alloc) xo_dependency(${SELF_EXE} xo_alloc)
xo_dependency(${SELF_EXE} refcnt) xo_dependency(${SELF_EXE} refcnt)
xo_dependency(${SELF_EXE} indentlog) xo_dependency(${SELF_EXE} indentlog)
xo_dependency(${SELF_EXE} randomgen) xo_dependency(${SELF_EXE} randomgen)
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# 3rd part dependency: catch2: # 3rd part dependency: catch2:
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2) xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
endif()
# end ordinaltree/utest/CMakeLists.txt # end ordinaltree/utest/CMakeLists.txt