xo-arena: + DArena::unmap() + defeat DSElim
All checks were successful
CI / smoke-test (push) Successful in 1s

This commit is contained in:
Roland Conybeare 2026-05-26 02:51:53 +00:00
commit 17dbb78a20
2 changed files with 40 additions and 10 deletions

View file

@ -241,6 +241,22 @@ namespace xo {
**/
void clear() noexcept;
/** release backing memory and reset bookkeeping to the empty state.
*
* Unmaps [@ref lo_, @ref hi_) (if mapped) and zeroes the bookkeeping
* fields {lo_, committed_z_, last_header_, free_, limit_, hi_,
* error_count_, last_error_}. @ref config_ (and page_z_/arena_align_z_)
* are left intact.
*
* Idempotent: a second call is a no-op (lo_ has been cleared).
* Invoked by ~DArena(); also safe to call on a live arena.
*
* Note: application code must not rely on observing the zeroed state
* after destruction (that would be UB) -- the zeroing is defensive,
* to avoid leaving dangling pointers behind.
**/
void unmap() noexcept;
/** swap contents (including configuration) with another arena **/
void swap(DArena & other) noexcept;

View file

@ -13,6 +13,7 @@
#include <xo/indentlog/print/tag.hpp>
#include <cassert>
#include <exception>
#include <new> // for std::launder()
#include <sys/mman.h> // for ::munmap()
#include <unistd.h> // for ::getpagesize()
#include <string.h> // for ::memset()
@ -142,7 +143,8 @@ namespace xo {
return *this;
}
DArena::~DArena()
void
DArena::unmap() noexcept
{
if (lo_) {
//log && log("unmap [lo,hi)",
@ -153,15 +155,27 @@ namespace xo {
::munmap(lo_, hi_ - lo_);
}
// hygiene
lo_ = nullptr;
committed_z_ = 0;
// checkpoint_ = nullptr;
free_ = nullptr;
limit_ = nullptr;
hi_ = nullptr;
error_count_ = 0;
last_error_ = AllocError();
/* Mandatory hygiene: zero the bookkeeping tail so no dangling
* pointers survive (e.g. after ~DArena() runs the dtor of a
* stack-owned arena). config_, page_z_, arena_align_z_ are
* preserved -- only {lo_ .. last_error_} get cleared.
*
* The memset goes through a std::launder'd self pointer so the
* compiler cannot prove it writes to the dying object and elide
* the stores as dead (gcc>=15 does that with a plain member
* assignment / un-laundered memset at -O1). All cleared fields
* are trivially-copyable, so byte-zeroing them is well-defined.
*/
DArena * self = std::launder(this);
byte * tail = reinterpret_cast<byte *>(&self->lo_);
byte * end = reinterpret_cast<byte *>(self) + sizeof(DArena);
::memset(tail, 0, end - tail);
}
DArena::~DArena()
{
this->unmap();
}
auto