Rename ArenaAllocator -> Arena

This commit is contained in:
2025-09-05 17:57:04 -04:00
parent 46fe51c0bb
commit f56ed2bfbe
22 changed files with 267 additions and 279 deletions

View File

@@ -1,10 +1,10 @@
#include "arena_allocator.hpp"
#include "arena.hpp"
#include <cassert>
#include <iomanip>
#include <limits>
#include <vector>
ArenaAllocator::~ArenaAllocator() {
Arena::~Arena() {
while (current_block_) {
Block *prev = current_block_->prev;
std::free(current_block_);
@@ -12,13 +12,13 @@ ArenaAllocator::~ArenaAllocator() {
}
}
ArenaAllocator::ArenaAllocator(ArenaAllocator &&other) noexcept
Arena::Arena(Arena &&other) noexcept
: initial_block_size_(other.initial_block_size_),
current_block_(other.current_block_) {
other.current_block_ = nullptr;
}
ArenaAllocator &ArenaAllocator::operator=(ArenaAllocator &&other) noexcept {
Arena &Arena::operator=(Arena &&other) noexcept {
if (this != &other) {
while (current_block_) {
Block *prev = current_block_->prev;
@@ -34,7 +34,7 @@ ArenaAllocator &ArenaAllocator::operator=(ArenaAllocator &&other) noexcept {
return *this;
}
void ArenaAllocator::reset() {
void Arena::reset() {
if (!current_block_) {
return;
}
@@ -63,8 +63,8 @@ void ArenaAllocator::reset() {
current_block_->offset = 0;
}
void *ArenaAllocator::realloc_raw(void *ptr, uint32_t old_size,
uint32_t new_size, uint32_t alignment) {
void *Arena::realloc_raw(void *ptr, uint32_t old_size, uint32_t new_size,
uint32_t alignment) {
if (ptr == nullptr) {
return allocate_raw(new_size, alignment);
}
@@ -125,8 +125,8 @@ void *ArenaAllocator::realloc_raw(void *ptr, uint32_t old_size,
return new_ptr;
}
void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
bool show_content, size_t content_limit) const {
void Arena::debug_dump(std::ostream &out, bool show_memory_map,
bool show_content, size_t content_limit) const {
out << "=== Arena Debug Dump ===" << std::endl;
if (!current_block_) {
@@ -242,20 +242,20 @@ void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
}
}
void ArenaAllocator::add_block(size_t size) {
void Arena::add_block(size_t size) {
Block *new_block = Block::create(size, current_block_);
current_block_ = new_block;
}
size_t ArenaAllocator::calculate_next_block_size(size_t required_size) const {
size_t Arena::calculate_next_block_size(size_t required_size) const {
size_t doubled_size = (current_block_ ? current_block_->size : 0) * 2;
doubled_size =
std::min<size_t>(doubled_size, std::numeric_limits<uint32_t>::max());
return std::max(required_size, doubled_size);
}
void ArenaAllocator::dump_memory_contents(std::ostream &out, const char *data,
size_t size) {
void Arena::dump_memory_contents(std::ostream &out, const char *data,
size_t size) {
const int bytes_per_line = 16;
for (int64_t offset = 0; offset < static_cast<int64_t>(size);

View File

@@ -17,7 +17,7 @@
/**
* @brief A high-performance arena allocator for bulk allocations.
*
* ArenaAllocator provides extremely fast memory allocation (~1ns per
* Arena provides extremely fast memory allocation (~1ns per
* allocation) by allocating large blocks and serving allocations from them
* sequentially. It's designed for scenarios where many small objects need to be
* allocated and can all be deallocated together.
@@ -39,7 +39,7 @@
*
* ## Usage Examples:
* ```cpp
* ArenaAllocator arena(1024);
* Arena arena(1024);
* void* ptr = arena.allocate_raw(100);
* int* num = arena.construct<int>(42);
* arena.reset(); // Reuse arena memory
@@ -52,13 +52,13 @@
* - Move semantics transfer ownership of all blocks
*
* ## Thread Safety:
* ArenaAllocator is **not thread-safe** - concurrent access from multiple
* Arena is **not thread-safe** - concurrent access from multiple
* threads requires external synchronization. However, this design is
* intentional for performance reasons and the WeaselDB architecture ensures
* thread safety through ownership patterns:
*
* ### Safe Usage Patterns in WeaselDB:
* - **Per-Connection Instances**: Each Connection owns its own ArenaAllocator
* - **Per-Connection Instances**: Each Connection owns its own Arena
* instance, accessed only by the thread that currently owns the connection
* - **Single Owner Principle**: Connection ownership transfers atomically
* between threads using unique_ptr, ensuring only one thread accesses the arena
@@ -81,10 +81,10 @@
* - **No Shared State**: Each arena is completely isolated - no shared data
* between different arena instances
*
* @warning Do not share ArenaAllocator instances between threads. Use separate
* @warning Do not share Arena instances between threads. Use separate
* instances per thread or per logical unit of work.
*/
struct ArenaAllocator {
struct Arena {
private:
/**
* @brief Internal block structure for the intrusive linked list.
@@ -117,19 +117,17 @@ private:
*/
static Block *create(size_t size, Block *prev) {
if (size > std::numeric_limits<uint32_t>::max()) {
std::fprintf(
stderr,
"ArenaAllocator: Block size %zu exceeds maximum uint32_t value\n",
size);
std::fprintf(stderr,
"Arena: Block size %zu exceeds maximum uint32_t value\n",
size);
std::abort();
}
void *memory = std::aligned_alloc(
alignof(Block), align_up(sizeof(Block) + size, alignof(Block)));
if (!memory) {
std::fprintf(
stderr,
"ArenaAllocator: Failed to allocate memory block of size %zu\n",
size);
std::fprintf(stderr,
"Arena: Failed to allocate memory block of size %zu\n",
size);
std::abort();
}
size_t total_size = size + (prev ? prev->total_size : 0);
@@ -142,7 +140,7 @@ private:
public:
/**
* @brief Construct an ArenaAllocator with the specified initial block size.
* @brief Construct an Arena with the specified initial block size.
*
* No memory is allocated until the first allocation request (lazy
* initialization). The initial block size is used for the first block and as
@@ -150,7 +148,7 @@ public:
*
* @param initial_size Size in bytes for the first block (default: 1024)
*/
explicit ArenaAllocator(size_t initial_size = 1024)
explicit Arena(size_t initial_size = 1024)
: initial_block_size_(initial_size), current_block_(nullptr) {}
/**
@@ -159,18 +157,18 @@ public:
* Traverses the intrusive linked list backwards from current_block_,
* freeing each block. This ensures no memory leaks.
*/
~ArenaAllocator();
~Arena();
/// Copy construction is not allowed (would be expensive and error-prone)
ArenaAllocator(const ArenaAllocator &) = delete;
Arena(const Arena &) = delete;
/// Copy assignment is not allowed (would be expensive and error-prone)
ArenaAllocator &operator=(const ArenaAllocator &) = delete;
Arena &operator=(const Arena &) = delete;
/**
* @brief Move constructor - transfers ownership of all blocks.
* @param other The ArenaAllocator to move from (will be left empty)
* @param other The Arena to move from (will be left empty)
*/
ArenaAllocator(ArenaAllocator &&other) noexcept;
Arena(Arena &&other) noexcept;
/**
* @brief Move assignment operator - transfers ownership of all blocks.
@@ -178,10 +176,10 @@ public:
* Frees any existing blocks in this allocator before taking ownership
* of blocks from the other allocator.
*
* @param other The ArenaAllocator to move from (will be left empty)
* @param other The Arena to move from (will be left empty)
* @return Reference to this allocator
*/
ArenaAllocator &operator=(ArenaAllocator &&other) noexcept;
Arena &operator=(Arena &&other) noexcept;
/**
* @brief Allocate raw memory with the specified size and alignment.
@@ -293,7 +291,7 @@ public:
T *realloc(T *ptr, uint32_t old_size, uint32_t new_size) {
if (size_t(new_size) * sizeof(T) > std::numeric_limits<uint32_t>::max()) {
std::fprintf(stderr,
"ArenaAllocator: Reallocation size overflow for type %s "
"Arena: Reallocation size overflow for type %s "
"(new_size=%u, sizeof(T)=%zu)\n",
typeid(T).name(), new_size, sizeof(T));
std::abort();
@@ -306,7 +304,7 @@ public:
* @brief Smart pointer for arena-allocated objects with non-trivial
* destructors.
*
* ArenaAllocator::Ptr calls the destructor but does not free memory (assumes
* Arena::Ptr calls the destructor but does not free memory (assumes
* arena allocation). This provides RAII semantics for objects that need
* cleanup without the overhead of individual memory deallocation.
*
@@ -363,13 +361,13 @@ public:
* This method returns different types based on whether T is trivially
* destructible:
* - For trivially destructible types: returns T* (raw pointer)
* - For non-trivially destructible types: returns ArenaAllocator::Ptr<T>
* - For non-trivially destructible types: returns Arena::Ptr<T>
* (smart pointer that calls destructor)
*
* @tparam T The type of object to construct
* @tparam Args Types of constructor arguments
* @param args Arguments to forward to T's constructor
* @return T* for trivially destructible types, ArenaAllocator::Ptr<T>
* @return T* for trivially destructible types, Arena::Ptr<T>
* otherwise
* @note Prints error to stderr and calls std::abort() if memory allocation
* fails
@@ -414,7 +412,7 @@ public:
template <typename T> T *allocate(uint32_t size) {
static_assert(
std::is_trivially_destructible_v<T>,
"ArenaAllocator::allocate requires trivially destructible types. "
"Arena::allocate requires trivially destructible types. "
"Objects allocated in the arena will not have their destructors "
"called.");
if (size == 0) {
@@ -422,7 +420,7 @@ public:
}
if (size_t(size) * sizeof(T) > std::numeric_limits<uint32_t>::max()) {
std::fprintf(stderr,
"ArenaAllocator: Allocation size overflow for type %s "
"Arena: Allocation size overflow for type %s "
"(size=%u, sizeof(T)=%zu)\n",
typeid(T).name(), size, sizeof(T));
std::abort();
@@ -615,7 +613,7 @@ private:
};
/**
* @brief STL-compatible allocator that uses ArenaAllocator for memory
* @brief STL-compatible allocator that uses Arena for memory
* management.
* @tparam T The type of objects to allocate
*/
@@ -633,7 +631,7 @@ public:
using other = ArenaStlAllocator<U>;
};
explicit ArenaStlAllocator(ArenaAllocator *arena) noexcept : arena_(arena) {}
explicit ArenaStlAllocator(Arena *arena) noexcept : arena_(arena) {}
template <typename U>
ArenaStlAllocator(const ArenaStlAllocator<U> &other) noexcept
@@ -659,7 +657,7 @@ public:
return arena_ != other.arena_;
}
ArenaAllocator *arena_;
Arena *arena_;
template <typename U> friend class ArenaStlAllocator;
};
@@ -669,7 +667,7 @@ public:
/// arena-allocated Uses arena's realloc() for efficient growth without copying
/// when possible
template <typename T> struct ArenaVector {
explicit ArenaVector(ArenaAllocator *arena)
explicit ArenaVector(Arena *arena)
: arena_(arena), data_(nullptr), size_(0), capacity_(0) {}
void push_back(const T &item) {
@@ -713,7 +711,7 @@ private:
capacity_ = new_capacity;
}
ArenaAllocator *arena_;
Arena *arena_;
T *data_;
size_t size_;
size_t capacity_;

View File

@@ -5,7 +5,7 @@
#include <string_view>
#include <vector>
#include "arena_allocator.hpp"
#include "arena.hpp"
/**
* @brief Represents a precondition for optimistic concurrency control.
@@ -63,7 +63,7 @@ struct Operation {
*/
struct CommitRequest {
private:
ArenaAllocator arena_;
Arena arena_;
std::optional<std::string_view> request_id_;
std::string_view leader_id_;
int64_t read_version_ = 0;
@@ -155,7 +155,7 @@ public:
*
* @return Reference to the arena allocator
*/
const ArenaAllocator &arena() const { return arena_; }
const Arena &arena() const { return arena_; }
/**
* @brief Get access to the underlying arena allocator for allocation.
@@ -166,7 +166,7 @@ public:
*
* @return Reference to the arena allocator
*/
ArenaAllocator &arena() { return arena_; }
Arena &arena() { return arena_; }
/**
* @brief Reset the commit request for reuse.

View File

@@ -8,7 +8,7 @@
#include <sys/uio.h>
#include <unistd.h>
#include "arena_allocator.hpp"
#include "arena.hpp"
#include "connection_handler.hpp"
#ifndef __has_feature
@@ -28,7 +28,7 @@
* - RAII cleanup happens if I/O thread doesn't transfer back
*
* Arena allocator thread safety:
* Each Connection contains its own ArenaAllocator instance that is accessed
* Each Connection contains its own Arena instance that is accessed
* exclusively by the thread that currently owns the connection. This ensures
* thread safety without requiring locks:
* - Arena is used by the owning thread for I/O buffers, request parsing, and
@@ -117,7 +117,7 @@ struct Connection {
/**
* @brief Get access to the connection's arena allocator.
*
* Returns a reference to this connection's private ArenaAllocator instance,
* Returns a reference to this connection's private Arena instance,
* which should be used for all temporary allocations during request
* processing. The arena provides extremely fast allocation (~1ns) and
* automatic cleanup when the connection is destroyed or reset.
@@ -135,7 +135,7 @@ struct Connection {
*
* Best practices:
* ```cpp
* ArenaAllocator& arena = conn->get_arena();
* Arena& arena = conn->get_arena();
*
* // Allocate temporary parsing buffers
* char* buffer = arena.allocate<char>(1024);
@@ -147,7 +147,7 @@ struct Connection {
* std::vector<Token, ArenaStlAllocator<Token>> tokens{&arena};
* ```
*/
ArenaAllocator &get_arena() { return arena_; }
Arena &get_arena() { return arena_; }
/**
* @brief Get the unique identifier for this connection.
@@ -345,7 +345,7 @@ private:
const int64_t id_;
const size_t epoll_index_; // Index of the epoll instance this connection uses
struct sockaddr_storage addr_; // sockaddr_storage handles IPv4/IPv6
ArenaAllocator arena_;
Arena arena_;
ConnectionHandler *handler_;
std::weak_ptr<Server> server_; // Weak reference to server for safe cleanup

View File

@@ -952,7 +952,7 @@ void DoubleTerm::write(char *&buf) const { buf = to_chars(buf, nullptr, s); }
} // namespace detail
std::string_view format(ArenaAllocator &arena, const char *fmt, ...) {
std::string_view format(Arena &arena, const char *fmt, ...) {
va_list args;
// Try to format directly into available arena space (single-pass

View File

@@ -7,7 +7,7 @@
#include <string_view>
#include <type_traits>
#include "arena_allocator.hpp"
#include "arena.hpp"
/**
* @brief Runtime printf-style formatting with arena allocation optimization.
@@ -47,7 +47,7 @@
*
* ## Usage Examples:
* ```cpp
* ArenaAllocator arena(1024);
* Arena arena(1024);
*
* // Basic formatting
* auto msg = format(arena, "Hello %s!", "World");
@@ -83,7 +83,7 @@
* const char*
*
* ## Optimization Details:
* The function uses `ArenaAllocator::allocate_remaining_space()` to claim all
* The function uses `Arena::allocate_remaining_space()` to claim all
* available arena space and attempt formatting. If successful, it shrinks the
* allocation to the actual size used. If formatting fails (doesn't fit), it
* falls back to the traditional two-pass approach: measure size, allocate
@@ -92,7 +92,7 @@
* This strategy optimizes for the common case where available arena space is
* sufficient, while maintaining correctness for all cases.
*/
std::string_view format(ArenaAllocator &arena, const char *fmt, ...)
std::string_view format(Arena &arena, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
namespace detail {
@@ -232,7 +232,7 @@ inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); }
*
* ## Usage Examples:
* ```cpp
* ArenaAllocator arena(1024);
* Arena arena(1024);
*
* // String concatenation
* auto result1 = static_format(arena, "Hello ", "World", "!");
@@ -275,7 +275,7 @@ inline constexpr DoubleTerm term(double s) { return DoubleTerm(s); }
* builds
*/
template <class... Ts>
std::string_view static_format(ArenaAllocator &arena, Ts &&...ts) {
std::string_view static_format(Arena &arena, Ts &&...ts) {
constexpr int upper_bound = (decltype(detail::term(ts))::kMaxLength + ...);
char *result = arena.allocate<char>(upper_bound);
char *buf = result;

View File

@@ -6,7 +6,7 @@
#include <strings.h>
#include "api_url_parser.hpp"
#include "arena_allocator.hpp"
#include "arena.hpp"
#include "cpu_work.hpp"
#include "format.hpp"
#include "json_commit_request_parser.hpp"
@@ -37,7 +37,7 @@ auto banned_request_ids_memory_gauge =
.create({});
// HttpConnectionState implementation
HttpConnectionState::HttpConnectionState(ArenaAllocator &arena)
HttpConnectionState::HttpConnectionState(Arena &arena)
: arena(arena), current_header_field_buf(ArenaStlAllocator<char>(&arena)),
current_header_value_buf(ArenaStlAllocator<char>(&arena)) {
llhttp_settings_init(&settings);
@@ -59,7 +59,7 @@ HttpConnectionState::HttpConnectionState(ArenaAllocator &arena)
// HttpHandler implementation
void HttpHandler::on_connection_established(Connection &conn) {
// Allocate HTTP state in connection's arena
ArenaAllocator &arena = conn.get_arena();
Arena &arena = conn.get_arena();
void *mem = arena.allocate_raw(sizeof(HttpConnectionState),
alignof(HttpConnectionState));
auto *state = new (mem) HttpConnectionState(arena);
@@ -70,7 +70,7 @@ void HttpHandler::on_connection_closed(Connection &conn) {
// Arena cleanup happens automatically when connection is destroyed
auto *state = static_cast<HttpConnectionState *>(conn.user_data);
if (state) {
// ArenaAllocator::Ptr automatically calls destructors
// Arena::Ptr automatically calls destructors
state->~HttpConnectionState();
}
conn.user_data = nullptr;
@@ -171,7 +171,7 @@ void HttpHandler::on_data_arrived(std::string_view data,
// If message is complete, route and handle the request
if (state->message_complete) {
// Copy URL to arena for in-place decoding
ArenaAllocator &arena = conn_ptr->get_arena();
Arena &arena = conn_ptr->get_arena();
char *url_buffer = arena.allocate<char>(state->url.size());
std::memcpy(url_buffer, state->url.data(), state->url.size());
@@ -244,7 +244,7 @@ void HttpHandler::handle_post_commit(Connection &conn,
const char *error = state.commit_parser
? state.commit_parser->get_parse_error()
: "No parser initialized";
ArenaAllocator &arena = conn.get_arena();
Arena &arena = conn.get_arena();
std::string_view error_msg =
format(arena, "Parse failed: %s", error ? error : "Unknown error");
send_error_response(conn, 400, error_msg, state.connection_close);
@@ -361,7 +361,7 @@ void HttpHandler::handle_delete_retention(Connection &conn,
void HttpHandler::handle_get_metrics(Connection &conn,
const HttpConnectionState &state) {
metrics_counter.inc();
ArenaAllocator &arena = conn.get_arena();
Arena &arena = conn.get_arena();
auto metrics_span = metric::render(arena);
// Calculate total size for the response body
@@ -418,7 +418,7 @@ void HttpHandler::handle_not_found(Connection &conn,
void HttpHandler::send_response(Connection &conn, int status_code,
std::string_view content_type,
std::string_view body, bool close_connection) {
ArenaAllocator &arena = conn.get_arena();
Arena &arena = conn.get_arena();
auto *state = static_cast<HttpConnectionState *>(conn.user_data);
// Status text
@@ -472,7 +472,7 @@ void HttpHandler::send_json_response(Connection &conn, int status_code,
void HttpHandler::send_error_response(Connection &conn, int status_code,
std::string_view message,
bool close_connection) {
ArenaAllocator &arena = conn.get_arena();
Arena &arena = conn.get_arena();
std::string_view json =
format(arena, R"({"error":"%.*s"})", static_cast<int>(message.size()),
@@ -819,7 +819,7 @@ bool HttpHandler::process_persist_batch(BatchType &batch) {
perfetto::Flow::Global(state->http_request_id));
const CommitRequest &commit_request = *state->commit_request;
ArenaAllocator &arena = commit_entry.connection->get_arena();
Arena &arena = commit_entry.connection->get_arena();
std::string_view response;
// Generate success response with actual assigned version

View File

@@ -9,7 +9,7 @@
#include <llhttp.h>
#include "api_url_parser.hpp"
#include "arena_allocator.hpp"
#include "arena.hpp"
#include "config.hpp"
#include "connection.hpp"
#include "connection_handler.hpp"
@@ -28,7 +28,7 @@ struct RouteMatch;
* Manages llhttp parser state and request data.
*/
struct HttpConnectionState {
ArenaAllocator &arena;
Arena &arena;
llhttp_t parser;
llhttp_settings_t settings;
@@ -56,13 +56,13 @@ struct HttpConnectionState {
0; // X-Request-Id header value (for tracing/logging)
// Streaming parser for POST requests
ArenaAllocator::Ptr<JsonCommitRequestParser> commit_parser;
ArenaAllocator::Ptr<CommitRequest> commit_request;
Arena::Ptr<JsonCommitRequestParser> commit_parser;
Arena::Ptr<CommitRequest> commit_request;
bool parsing_commit = false;
bool basic_validation_passed =
false; // Set to true if basic validation passes
explicit HttpConnectionState(ArenaAllocator &arena);
explicit HttpConnectionState(Arena &arena);
};
/**
@@ -165,7 +165,7 @@ private:
// Arena for banned request IDs and related data structures (sequence thread
// only)
ArenaAllocator banned_request_arena;
Arena banned_request_arena;
using BannedRequestIdSet =
std::unordered_set<std::string_view, std::hash<std::string_view>,
std::equal_to<std::string_view>,

View File

@@ -70,7 +70,7 @@ private:
ArenaString operation_type;
// Constructor to initialize arena-allocated containers
explicit ParserContext(ArenaAllocator *arena)
explicit ParserContext(Arena *arena)
: current_key(ArenaStlAllocator<char>(arena)),
current_string(ArenaStlAllocator<char>(arena)),
current_number(ArenaStlAllocator<char>(arena)),
@@ -79,7 +79,7 @@ private:
has_read_version_been_set = false;
}
void attach_arena(ArenaAllocator *arena) {
void attach_arena(Arena *arena) {
current_key = ArenaString{ArenaStlAllocator<char>(arena)};
current_string = ArenaString{ArenaStlAllocator<char>(arena)};
current_number = ArenaString{ArenaStlAllocator<char>(arena)};

View File

@@ -25,7 +25,7 @@
#include <immintrin.h>
#include <simdutf.h>
#include "arena_allocator.hpp"
#include "arena.hpp"
#include "format.hpp"
// WeaselDB Metrics System Design:
@@ -79,18 +79,18 @@ namespace metric {
// - Content: Thread-specific metric instance state
//
// 3. TEMPORARY ARENAS:
// a) Caller-Provided Arenas (ArenaAllocator& parameters):
// a) Caller-Provided Arenas (Arena& parameters):
// - Lifetime: Controlled by caller (function parameter)
// - Purpose: Output formatting where caller controls result lifetime
// - Owner: Caller owns arena and controls string lifetime
// - Example: render(ArenaAllocator& arena) - caller manages arena
// - Example: render(Arena& arena) - caller manages arena
// lifecycle
//
// b) Stack-Owned Temporary Arenas:
// - Lifetime: Function/scope lifetime (automatic destruction)
// - Purpose: Internal temporary allocations for lookups and processing
// - Owner: Function owns arena on stack, destroyed at scope exit
// - Example: intern_labels() creates ArenaAllocator lookup_arena(1024)
// - Example: intern_labels() creates Arena lookup_arena(1024)
//
// CRITICAL OWNERSHIP RULES:
//
@@ -124,8 +124,7 @@ static void validate_or_abort(bool condition, const char *message,
}
// Helper to copy a string into arena memory
static std::string_view arena_copy_string(std::string_view str,
ArenaAllocator &arena) {
static std::string_view arena_copy_string(std::string_view str, Arena &arena) {
if (str.empty()) {
return std::string_view{};
}
@@ -142,7 +141,7 @@ struct LabelsKey {
// Arena-owning constructor (copies strings into arena and formats as
// Prometheus text)
LabelsKey(std::span<const std::pair<std::string_view, std::string_view>> l,
ArenaAllocator &arena) {
Arena &arena) {
// Copy and validate all label keys and values, sort by key
ArenaVector<std::pair<std::string_view, std::string_view>> labels(&arena);
for (const auto &[key, value] : l) {
@@ -251,7 +250,7 @@ template <> struct Family<Counter>::State {
ArenaStlAllocator<std::pair<const LabelsKey, Counter::State *>>>
instances;
explicit PerThreadState(ArenaAllocator &arena)
explicit PerThreadState(Arena &arena)
: instances(
ArenaStlAllocator<std::pair<const LabelsKey, Counter::State *>>(
&arena)) {}
@@ -271,7 +270,7 @@ template <> struct Family<Counter>::State {
ArenaStlAllocator<std::pair<const LabelsKey, MetricCallback<Counter>>>>
callbacks;
State(ArenaAllocator &arena)
State(Arena &arena)
: global_accumulated_values(
ArenaStlAllocator<std::pair<const LabelsKey, Counter::State *>>(
&arena)),
@@ -293,7 +292,7 @@ template <> struct Family<Gauge>::State {
ArenaStlAllocator<std::pair<const LabelsKey, MetricCallback<Gauge>>>>
callbacks;
State(ArenaAllocator &arena)
State(Arena &arena)
: instances(ArenaStlAllocator<std::pair<const LabelsKey, Gauge::State *>>(
&arena)),
callbacks(ArenaStlAllocator<
@@ -312,7 +311,7 @@ template <> struct Family<Histogram>::State {
ArenaStlAllocator<std::pair<const LabelsKey, Histogram::State *>>>
instances;
explicit PerThreadState(ArenaAllocator &arena)
explicit PerThreadState(Arena &arena)
: instances(
ArenaStlAllocator<std::pair<const LabelsKey, Histogram::State *>>(
&arena)) {}
@@ -326,7 +325,7 @@ template <> struct Family<Histogram>::State {
ArenaStlAllocator<std::pair<const LabelsKey, Histogram::State *>>>
global_accumulated_values;
State(ArenaAllocator &arena)
State(Arena &arena)
: buckets(&arena),
global_accumulated_values(
ArenaStlAllocator<std::pair<const LabelsKey, Histogram::State *>>(
@@ -371,54 +370,47 @@ struct Metric {
static std::mutex mutex;
// Global arena allocator for metric families and persistent global state
static ArenaAllocator &get_global_arena() {
static auto *global_arena =
new ArenaAllocator(64 * 1024); // 64KB initial size
static Arena &get_global_arena() {
static auto *global_arena = new Arena(64 * 1024); // 64KB initial size
return *global_arena;
}
// Function-local statics to avoid static initialization order fiasco
static auto &get_counter_families() {
using FamilyMap =
std::map<std::string_view, ArenaAllocator::Ptr<Family<Counter>::State>,
std::less<std::string_view>,
ArenaStlAllocator<
std::pair<const std::string_view,
ArenaAllocator::Ptr<Family<Counter>::State>>>>;
static FamilyMap *counterFamilies =
new FamilyMap(ArenaStlAllocator<
std::pair<const std::string_view,
ArenaAllocator::Ptr<Family<Counter>::State>>>(
using FamilyMap = std::map<
std::string_view, Arena::Ptr<Family<Counter>::State>,
std::less<std::string_view>,
ArenaStlAllocator<std::pair<const std::string_view,
Arena::Ptr<Family<Counter>::State>>>>;
static FamilyMap *counterFamilies = new FamilyMap(
ArenaStlAllocator<std::pair<const std::string_view,
Arena::Ptr<Family<Counter>::State>>>(
&get_global_arena()));
return *counterFamilies;
}
static auto &get_gauge_families() {
using FamilyMap =
std::map<std::string_view, ArenaAllocator::Ptr<Family<Gauge>::State>,
std::less<std::string_view>,
ArenaStlAllocator<
std::pair<const std::string_view,
ArenaAllocator::Ptr<Family<Gauge>::State>>>>;
using FamilyMap = std::map<
std::string_view, Arena::Ptr<Family<Gauge>::State>,
std::less<std::string_view>,
ArenaStlAllocator<std::pair<const std::string_view,
Arena::Ptr<Family<Gauge>::State>>>>;
static FamilyMap *gaugeFamilies = new FamilyMap(
ArenaStlAllocator<std::pair<const std::string_view,
ArenaAllocator::Ptr<Family<Gauge>::State>>>(
Arena::Ptr<Family<Gauge>::State>>>(
&get_global_arena()));
return *gaugeFamilies;
}
static auto &get_histogram_families() {
using FamilyMap =
std::map<std::string_view,
ArenaAllocator::Ptr<Family<Histogram>::State>,
std::less<std::string_view>,
ArenaStlAllocator<
std::pair<const std::string_view,
ArenaAllocator::Ptr<Family<Histogram>::State>>>>;
static FamilyMap *histogramFamilies =
new FamilyMap(ArenaStlAllocator<
std::pair<const std::string_view,
ArenaAllocator::Ptr<Family<Histogram>::State>>>(
using FamilyMap = std::map<
std::string_view, Arena::Ptr<Family<Histogram>::State>,
std::less<std::string_view>,
ArenaStlAllocator<std::pair<const std::string_view,
Arena::Ptr<Family<Histogram>::State>>>>;
static FamilyMap *histogramFamilies = new FamilyMap(
ArenaStlAllocator<std::pair<const std::string_view,
Arena::Ptr<Family<Histogram>::State>>>(
&get_global_arena()));
return *histogramFamilies;
}
@@ -446,8 +438,7 @@ struct Metric {
// Registry of all thread arenas for memory tracking
static auto &get_thread_arenas() {
using ThreadArenaMap =
std::unordered_map<std::thread::id, ArenaAllocator *>;
using ThreadArenaMap = std::unordered_map<std::thread::id, Arena *>;
static ThreadArenaMap *threadArenas = new ThreadArenaMap();
return *threadArenas;
}
@@ -460,7 +451,7 @@ struct Metric {
// Thread cleanup for per-family thread-local storage
struct ThreadInit {
ArenaAllocator arena;
Arena arena;
ThreadInit() {
// Register this thread's arena for memory tracking
std::unique_lock<std::mutex> _{mutex};
@@ -536,7 +527,7 @@ struct Metric {
static thread_local ThreadInit thread_init;
// Thread-local arena allocator for metric instances
static ArenaAllocator &get_thread_local_arena() { return thread_init.arena; }
static Arena &get_thread_local_arena() { return thread_init.arena; }
// Thread cleanup now handled by ThreadInit RAII
@@ -561,7 +552,7 @@ struct Metric {
// lifetime)
// Create temporary lookup key using stack-allocated arena
ArenaAllocator lookup_arena(1024); // Small arena for lookups only
Arena lookup_arena(1024); // Small arena for lookups only
LabelsKey lookup_key{labels, lookup_arena};
// Use standard hash set lookup - lookup_key memory used transiently only
@@ -736,7 +727,7 @@ struct Metric {
ArenaVector<Counter::State *> thread_states; // Pre-resolved pointers
Counter::State *global_state; // Pre-resolved global state pointer
CounterLabelData(const LabelsKey &key, ArenaAllocator &arena)
CounterLabelData(const LabelsKey &key, Arena &arena)
: labels_key(key), thread_states(&arena), global_state(nullptr) {}
};
@@ -754,7 +745,7 @@ struct Metric {
Histogram::State *global_state; // Pre-resolved global state pointer
size_t bucket_count; // Cache bucket count from family
HistogramLabelData(const LabelsKey &key, ArenaAllocator &arena)
HistogramLabelData(const LabelsKey &key, Arena &arena)
: labels_key(key), thread_states(&arena), global_state(nullptr),
bucket_count(0) {}
};
@@ -764,7 +755,7 @@ struct Metric {
ArenaVector<ArenaVector<CounterLabelData>> counter_data;
ArenaVector<ArenaVector<GaugeLabelData>> gauge_data;
ArenaVector<ArenaVector<HistogramLabelData>> histogram_data;
explicit LabelSets(ArenaAllocator &arena)
explicit LabelSets(Arena &arena)
: counter_data(&arena), gauge_data(&arena), histogram_data(&arena) {}
};
@@ -846,7 +837,7 @@ struct Metric {
// Three-phase rendering system
struct RenderPlan {
ArenaAllocator arena;
Arena arena;
ArenaVector<std::string_view> static_text{&arena};
ArenaVector<RenderInstruction> instructions{&arena};
uint64_t registration_version;
@@ -865,7 +856,7 @@ struct Metric {
// Use temporary arena for formatting static text (will be interned to
// global arena)
ArenaAllocator temp_arena(8192); // 8KB for temporary formatting
Arena temp_arena(8192); // 8KB for temporary formatting
// Helper function to append an additional label to existing Prometheus
// format
@@ -1091,7 +1082,7 @@ struct Metric {
// Phase 2: Execute phase - run instructions and generate dynamic text
static ArenaVector<std::string_view>
execute_render_plan(ArenaAllocator &arena,
execute_render_plan(Arena &arena,
const ArenaVector<RenderInstruction> &instructions) {
ArenaVector<std::string_view> dynamic_text(&arena);
@@ -1191,7 +1182,7 @@ struct Metric {
// Phase 3: Present phase - interleave static and dynamic text
static ArenaVector<std::string_view>
present_render_output(ArenaAllocator &arena,
present_render_output(Arena &arena,
const ArenaVector<std::string_view> &static_text,
const ArenaVector<std::string_view> &dynamic_text) {
ArenaVector<std::string_view> output(&arena);
@@ -1213,7 +1204,7 @@ struct Metric {
}
// Build label sets once for reuse in both phases
static LabelSets build_label_sets(ArenaAllocator &arena) {
static LabelSets build_label_sets(Arena &arena) {
LabelSets label_sets{arena};
// Build counter data with pre-resolved pointers
@@ -1495,7 +1486,7 @@ Family<Gauge> create_gauge(std::string_view name, std::string_view help) {
auto name_view = arena_copy_string(name, global_arena);
auto &familyPtr = Metric::get_gauge_families()[name_view];
if (!familyPtr) {
// Family<T>::State instances use ArenaAllocator::Ptr for automatic cleanup
// Family<T>::State instances use Arena::Ptr for automatic cleanup
familyPtr = global_arena.construct<Family<Gauge>::State>(global_arena);
familyPtr->name = name_view;
familyPtr->help = arena_copy_string(help, global_arena);
@@ -1519,7 +1510,7 @@ Family<Histogram> create_histogram(std::string_view name, std::string_view help,
auto name_view = arena_copy_string(name, global_arena);
auto &family_ptr = Metric::get_histogram_families()[name_view];
if (!family_ptr) {
// Family<T>::State instances use ArenaAllocator::Ptr for automatic cleanup
// Family<T>::State instances use Arena::Ptr for automatic cleanup
family_ptr = global_arena.construct<Family<Histogram>::State>(global_arena);
family_ptr->name = name_view;
family_ptr->help = arena_copy_string(help, global_arena);
@@ -1688,7 +1679,7 @@ static double calculate_metrics_memory_usage() {
}
// New three-phase render implementation
std::span<std::string_view> render(ArenaAllocator &arena) {
std::span<std::string_view> render(Arena &arena) {
// Initialize self-monitoring metrics (before taking global lock)
static auto memory_gauge = []() {
auto gauge = create_gauge("weaseldb_metrics_memory_bytes",

View File

@@ -50,7 +50,7 @@
#include <type_traits>
#include <vector>
#include "arena_allocator.hpp"
#include "arena.hpp"
namespace metric {
@@ -220,7 +220,7 @@ std::vector<double> exponential_buckets(double start, double factor, int count);
// allocated in provided arena for zero-copy efficiency. The caller is
// responsible for the arena's lifecycle. THREAD SAFETY: Serialized by global
// mutex - callbacks need not be thread-safe
std::span<std::string_view> render(ArenaAllocator &arena);
std::span<std::string_view> render(Arena &arena);
// Validation functions for Prometheus compatibility
bool is_valid_metric_name(std::string_view name);