xo-alloc2: capture error info, retire exceptions in alloc path
This commit is contained in:
parent
2538a3e7d8
commit
29ecc2dc38
6 changed files with 103 additions and 48 deletions
|
|
@ -14,6 +14,42 @@ namespace xo {
|
|||
using Copaque = const void *;
|
||||
using Opaque = void *;
|
||||
|
||||
enum class error : int32_t {
|
||||
/** sentinel **/
|
||||
invalid = -1,
|
||||
/** not an error **/
|
||||
none,
|
||||
/** reserved size exhauged **/
|
||||
reserve_exhausted,
|
||||
/** unable to commit (i.e. mprotect failure) **/
|
||||
commit_failed,
|
||||
};
|
||||
|
||||
struct AllocatorError {
|
||||
using size_type = std::size_t;
|
||||
using value_type = std::byte*;
|
||||
|
||||
AllocatorError() = default;
|
||||
explicit AllocatorError(error err) : error_{err} {}
|
||||
AllocatorError(error err,
|
||||
size_type req_z,
|
||||
size_type com_z,
|
||||
size_type rsv_z) : error_{err},
|
||||
request_z_{req_z},
|
||||
committed_z_{com_z},
|
||||
reserved_z_{rsv_z} {}
|
||||
|
||||
/** error code **/
|
||||
error error_ = error::none;
|
||||
|
||||
/** reqeust size assoc'd with errror **/
|
||||
size_type request_z_ = 0;
|
||||
/** committed allocator memory at time of error **/
|
||||
size_type committed_z_ = 0;
|
||||
/** reserved allocator memory at time of error **/
|
||||
size_type reserved_z_ = 0;
|
||||
};
|
||||
|
||||
/** @class AAllocator
|
||||
* @brief Abstract facet for allocation
|
||||
*
|
||||
|
|
@ -41,32 +77,32 @@ namespace xo {
|
|||
///@{
|
||||
|
||||
/** RTTI: unique id# for actual runtime data representation **/
|
||||
virtual int32_t _typeseq() const = 0;
|
||||
virtual int32_t _typeseq() const noexcept = 0;
|
||||
/** optional name for allocator @p d
|
||||
* Labeling, for diagnostics.
|
||||
**/
|
||||
virtual const std::string & name(Copaque d) const = 0;
|
||||
virtual const std::string & name(Copaque d) const noexcept = 0;
|
||||
/** reserved size in bytes for allocator @p d.
|
||||
* Includes committed + uncommitted memory.
|
||||
* Cannot be increased.
|
||||
**/
|
||||
virtual size_type reserved(Copaque d) const = 0;
|
||||
virtual size_type reserved(Copaque d) const noexcept = 0;
|
||||
/** Synonym for @ref committed.
|
||||
* Can increase on @ref alloc
|
||||
**/
|
||||
virtual size_type size(Copaque d) const = 0;
|
||||
virtual size_type size(Copaque d) const noexcept = 0;
|
||||
/** committed size (physical addresses obtained)
|
||||
* for allocator @p d.
|
||||
* @ref alloc may auto-increase this
|
||||
**/
|
||||
virtual size_type committed(Copaque d) const = 0;
|
||||
virtual size_type committed(Copaque d) const noexcept = 0;
|
||||
/** unallocated (but committed) size in bytes for allocator @p d **/
|
||||
virtual size_type available(Copaque d) const = 0;
|
||||
virtual size_type available(Copaque d) const noexcept = 0;
|
||||
/** allocated (i.e. in-use) amount in bytes for allocator @p d **/
|
||||
virtual size_type allocated(Copaque d) const = 0;
|
||||
virtual size_type allocated(Copaque d) const noexcept = 0;
|
||||
/** true iff allocator @p d is responsible for memory at address @p p.
|
||||
**/
|
||||
virtual bool contains(Copaque d, const void * p) const = 0;
|
||||
virtual bool contains(Copaque d, const void * p) const noexcept = 0;
|
||||
|
||||
/** expand committed space in arena @p d
|
||||
* to size at least @p z
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace xo {
|
|||
* @brief represent arena allocator state
|
||||
*
|
||||
* Provides minimal RAII functionality around memory mapping.
|
||||
* For allocation see @ref IAllocator_DArena
|
||||
* For allocation implementation see @ref IAllocator_DArena
|
||||
**/
|
||||
struct DArena {
|
||||
/*
|
||||
|
|
@ -92,7 +92,7 @@ namespace xo {
|
|||
ArenaConfig config_;
|
||||
|
||||
/** size of a VM page (obtained automatically via getpagesize()). Likely 4k **/
|
||||
std::size_t page_z_ = 0;
|
||||
size_type page_z_ = 0;
|
||||
|
||||
/** arena owns memory in range [@ref lo_, @ref hi_)
|
||||
**/
|
||||
|
|
@ -101,7 +101,7 @@ namespace xo {
|
|||
/** prefix of this size is committed.
|
||||
* Remainder mapped but uncommitted.
|
||||
**/
|
||||
std::size_t committed_z_ = 0;
|
||||
size_type committed_z_ = 0;
|
||||
|
||||
/** free pointer.
|
||||
* Memory in range [@ref lo_, @ref free_) current in use
|
||||
|
|
@ -119,6 +119,12 @@ namespace xo {
|
|||
**/
|
||||
std::byte * hi_ = nullptr;
|
||||
|
||||
/** count runtime errors. Each error updates @ref last_error_ **/
|
||||
uint32_t error_count_ = 0;
|
||||
|
||||
/** capture some error details if/when error **/
|
||||
AllocatorError last_error_;
|
||||
|
||||
///@}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ namespace xo {
|
|||
using size_type = std::size_t;
|
||||
|
||||
// from AAllocator
|
||||
int32_t _typeseq() const override { return s_typeseq; }
|
||||
int32_t _typeseq() const noexcept override { return s_typeseq; }
|
||||
|
||||
[[noreturn]] const std::string & name(Copaque) const override { _fatal(); }
|
||||
[[noreturn]] size_type reserved(Copaque) const override { _fatal(); }
|
||||
[[noreturn]] size_type size(Copaque) const override { _fatal(); }
|
||||
[[noreturn]] size_type committed(Copaque) const override { _fatal(); }
|
||||
[[noreturn]] size_type available(Copaque) const override { _fatal(); }
|
||||
[[noreturn]] size_type allocated(Copaque) const override { _fatal(); }
|
||||
[[noreturn]] bool contains(Copaque, const void *) const override { _fatal(); }
|
||||
[[noreturn]] const std::string & name(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] size_type reserved(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] size_type size(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] size_type committed(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] size_type available(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] size_type allocated(Copaque) const noexcept override { _fatal(); }
|
||||
[[noreturn]] bool contains(Copaque, const void *) const noexcept override { _fatal(); }
|
||||
|
||||
[[noreturn]] bool expand(Opaque, std::size_t) const override { _fatal(); }
|
||||
[[noreturn]] std::byte * alloc(Opaque, std::size_t) const override { _fatal(); }
|
||||
|
|
|
|||
|
|
@ -24,27 +24,27 @@ namespace xo {
|
|||
static DRepr & _dcast(Opaque d) { return *(DRepr *)d; }
|
||||
|
||||
// from AAllocator
|
||||
int32_t _typeseq() const override { return s_typeseq; }
|
||||
const std::string & name(Copaque d) const override {
|
||||
int32_t _typeseq() const noexcept override { return s_typeseq; }
|
||||
const std::string & name(Copaque d) const noexcept override {
|
||||
return Impl::name(_dcast(d));
|
||||
}
|
||||
size_type reserved(Copaque d) const override {
|
||||
size_type reserved(Copaque d) const noexcept override {
|
||||
return Impl::reserved(_dcast(d));
|
||||
}
|
||||
size_type size(Copaque d) const override {
|
||||
size_type size(Copaque d) const noexcept override {
|
||||
return Impl::size(_dcast(d));
|
||||
}
|
||||
size_type committed(Copaque d) const override {
|
||||
size_type committed(Copaque d) const noexcept override {
|
||||
return Impl::committed(_dcast(d));
|
||||
}
|
||||
size_type available(Copaque d) const override {
|
||||
size_type available(Copaque d) const noexcept override {
|
||||
return I::available(_dcast(d));
|
||||
}
|
||||
size_type allocated(Copaque d) const override {
|
||||
size_type allocated(Copaque d) const noexcept override {
|
||||
return I::allocated(_dcast(d));
|
||||
}
|
||||
|
||||
bool contains(Copaque d, const void * p) const override {
|
||||
bool contains(Copaque d, const void * p) const noexcept override {
|
||||
return Impl::contains(_dcast(d), p);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "xo/alloc2/AAllocator.hpp"
|
||||
#include "xo/alloc2/DArena.hpp"
|
||||
#include "xo/alloc2/padding.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
#include <cassert>
|
||||
#include <sys/mman.h> // for ::munmap()
|
||||
#include <unistd.h> // for ::getpagesize()
|
||||
|
|
@ -81,11 +82,8 @@ namespace xo {
|
|||
// 3. assess mmap success
|
||||
{
|
||||
if (base == MAP_FAILED) {
|
||||
assert(false);
|
||||
#ifdef NOPE
|
||||
throw std::runtime_error(tostr("ArenaAlloc: uncommitted allocation failed",
|
||||
xtag("size", z)));
|
||||
#endif
|
||||
xtag("size", req_z)));
|
||||
}
|
||||
|
||||
assert((size_t)aligned_base % hugepage_z == 0);
|
||||
|
|
@ -138,11 +136,8 @@ namespace xo {
|
|||
if (!lo) {
|
||||
// control here implies mmap() failed silently
|
||||
|
||||
assert(false);
|
||||
#ifdef NOPE
|
||||
throw std::runtime_error(tostr("ArenaAlloc: allocation failed",
|
||||
xtag("size", z)));
|
||||
#endif
|
||||
xtag("size", cfg.size_)));
|
||||
}
|
||||
|
||||
size_t page_z = getpagesize();
|
||||
|
|
@ -166,7 +161,9 @@ namespace xo {
|
|||
committed_z_{0},
|
||||
free_{lo},
|
||||
limit_{lo},
|
||||
hi_{hi}
|
||||
hi_{hi},
|
||||
error_count_{0},
|
||||
last_error_{}
|
||||
{
|
||||
//retval.checkpoint_ = lo_;
|
||||
}
|
||||
|
|
@ -179,6 +176,8 @@ namespace xo {
|
|||
free_ = other.free_;
|
||||
limit_ = other.limit_;
|
||||
hi_ = other.hi_;
|
||||
error_count_ = other.error_count_;
|
||||
last_error_ = other.last_error_;
|
||||
|
||||
other.config_ = ArenaConfig();
|
||||
other.lo_ = nullptr;
|
||||
|
|
@ -186,6 +185,8 @@ namespace xo {
|
|||
other.free_ = nullptr;
|
||||
other.limit_ = nullptr;
|
||||
other.hi_ = nullptr;
|
||||
other.error_count_ = 0;
|
||||
other.last_error_ = AllocatorError();
|
||||
}
|
||||
|
||||
DArena::~DArena()
|
||||
|
|
@ -200,12 +201,14 @@ namespace xo {
|
|||
}
|
||||
|
||||
// hygiene
|
||||
this->lo_ = nullptr;
|
||||
this->committed_z_ = 0;
|
||||
lo_ = nullptr;
|
||||
committed_z_ = 0;
|
||||
// checkpoint_ = nullptr;
|
||||
this->free_ = nullptr;
|
||||
this->limit_ = nullptr;
|
||||
this->hi_ = nullptr;
|
||||
free_ = nullptr;
|
||||
limit_ = nullptr;
|
||||
hi_ = nullptr;
|
||||
error_count_ = 0;
|
||||
last_error_ = AllocatorError();
|
||||
}
|
||||
}
|
||||
} /*namespace xo*/
|
||||
|
|
|
|||
|
|
@ -66,10 +66,16 @@ namespace xo {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (s.lo_ + target_z > s.hi_) {
|
||||
if (s.lo_ + target_z > s.hi_) [[unlikely]] {
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocatorError(error::reserve_exhausted,
|
||||
target_z, s.committed_z_, reserved(s));
|
||||
|
||||
#ifdef OBSOLETE
|
||||
throw std::runtime_error(tostr("ArenaAlloc::expand: requested size exceeds reserved size",
|
||||
xtag("requested", target_z),
|
||||
xtag("reserved", reserved(s))));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -107,11 +113,15 @@ namespace xo {
|
|||
// xtag("add_commit_z", add_commit_z),
|
||||
// xtag("commit_end", commit_start + add_commit_z));
|
||||
|
||||
if (::mprotect(commit_start, add_commit_z, PROT_READ | PROT_WRITE) != 0) {
|
||||
assert(false);
|
||||
// throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure",
|
||||
// xtag("committed_z", committed_z_),
|
||||
// xtag("add_commit_z", add_commit_z)));
|
||||
if (::mprotect(commit_start, add_commit_z, PROT_READ | PROT_WRITE) != 0) [[unlikely]] {
|
||||
++(s.error_count_);
|
||||
s.last_error_ = AllocatorError(error::commit_failed,
|
||||
add_commit_z, s.committed_z_, reserved(s));
|
||||
#ifdef OBSOLETE
|
||||
throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure",
|
||||
xtag("committed_z", s.committed_z_),
|
||||
xtag("add_commit_z", add_commit_z)));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue