parent
12d2ffdab7
commit
941bafca17
@ -0,0 +1,2 @@ |
|||||||
|
idf_component_register(SRCS "arena.cpp" INCLUDE_DIRS "include" REQUIRES "span") |
||||||
|
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) |
@ -0,0 +1,75 @@ |
|||||||
|
#include "arena.hpp" |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <optional> |
||||||
|
|
||||||
|
#include "esp_heap_caps.h" |
||||||
|
#include "freertos/queue.h" |
||||||
|
#include "span.hpp" |
||||||
|
|
||||||
|
namespace memory { |
||||||
|
|
||||||
|
Arena::Arena(std::size_t block_size, |
||||||
|
std::size_t num_blocks, |
||||||
|
uint32_t alloc_flags) |
||||||
|
: block_size_(block_size) { |
||||||
|
pool_ = static_cast<std::byte*>( |
||||||
|
heap_caps_malloc(block_size * num_blocks, alloc_flags)); |
||||||
|
free_blocks_ = xQueueCreate(num_blocks, sizeof(void*)); |
||||||
|
for (int i = 0; i < num_blocks; i++) { |
||||||
|
std::byte* block = pool_ + (i * block_size); |
||||||
|
xQueueSend(free_blocks_, &block, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Arena::~Arena() { |
||||||
|
// TODO: assert queue is full?
|
||||||
|
vQueueDelete(free_blocks_); |
||||||
|
free(pool_); |
||||||
|
} |
||||||
|
|
||||||
|
auto Arena::Acquire() -> std::optional<ArenaPtr> { |
||||||
|
std::byte* block; |
||||||
|
bool result = xQueueReceive(free_blocks_, &block, 0); |
||||||
|
if (result) { |
||||||
|
ArenaPtr ptr{this, block, block_size_, 0}; |
||||||
|
return ptr; |
||||||
|
} else { |
||||||
|
return {}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
auto Arena::Return(ArenaPtr ptr) -> void { |
||||||
|
assert(ptr.owner == this); |
||||||
|
xQueueSend(free_blocks_, &ptr.start, 0); |
||||||
|
} |
||||||
|
|
||||||
|
auto ArenaRef::Acquire(Arena* a) -> std::optional<ArenaRef> { |
||||||
|
auto ptr = a->Acquire(); |
||||||
|
if (ptr) { |
||||||
|
ArenaRef ref{*ptr}; |
||||||
|
return ref; |
||||||
|
} |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
ArenaRef::ArenaRef(ArenaPtr p) : ptr(p) {} |
||||||
|
|
||||||
|
ArenaRef::ArenaRef(ArenaRef&& other) : ptr(other.Release()) {} |
||||||
|
|
||||||
|
auto ArenaRef::Release() -> ArenaPtr { |
||||||
|
auto ret = ptr; |
||||||
|
ptr.owner = nullptr; |
||||||
|
ptr.start = nullptr; |
||||||
|
ptr.size = 0; |
||||||
|
ptr.used_size = 0; |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
ArenaRef::~ArenaRef() { |
||||||
|
if (ptr.owner != nullptr) { |
||||||
|
ptr.owner->Return(ptr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace memory
|
@ -0,0 +1,85 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <optional> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/queue.h" |
||||||
|
#include "span.hpp" |
||||||
|
#include "sys/_stdint.h" |
||||||
|
|
||||||
|
namespace memory { |
||||||
|
|
||||||
|
class Arena; |
||||||
|
|
||||||
|
/*
|
||||||
|
* A pointer to data that has been given out by an Arena, plus extra accounting |
||||||
|
* information so that it can be returned properly. |
||||||
|
*/ |
||||||
|
struct ArenaPtr { |
||||||
|
Arena* owner; |
||||||
|
std::byte* start; |
||||||
|
std::size_t size; |
||||||
|
// A convenience for keeping track of the subset of the block that has had
|
||||||
|
// data placed within it.
|
||||||
|
std::size_t used_size; |
||||||
|
}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* A basic memory arena. This class mediates access to fixed-size blocks of |
||||||
|
* memory within a larger contiguous block. This is faster than re-allocating |
||||||
|
* smaller blocks every time they're needed, and lets us easily limit the |
||||||
|
* maximum size of the memory used. |
||||||
|
* |
||||||
|
* A single arena instance is safe to be used concurrently by multiple tasks, |
||||||
|
* however there is no built in synchronisation of the underlying memory. |
||||||
|
*/ |
||||||
|
class Arena { |
||||||
|
public: |
||||||
|
Arena(std::size_t block_size, std::size_t num_blocks, uint32_t alloc_flags); |
||||||
|
~Arena(); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempts to receive an allocation from this arena. Returns absent if |
||||||
|
* there are no blocks left. |
||||||
|
*/ |
||||||
|
auto Acquire() -> std::optional<ArenaPtr>; |
||||||
|
|
||||||
|
/* Returns a previously allocated block to this arena. */ |
||||||
|
auto Return(ArenaPtr) -> void; |
||||||
|
|
||||||
|
/* Returns the number of blocks that are currently free. */ |
||||||
|
auto BlocksFree() -> std::size_t; |
||||||
|
|
||||||
|
Arena(const Arena&) = delete; |
||||||
|
Arena& operator=(const Arena&) = delete; |
||||||
|
|
||||||
|
private: |
||||||
|
std::size_t block_size_; |
||||||
|
// The large memory allocation that is divided into blocks.
|
||||||
|
std::byte* pool_; |
||||||
|
// A FreeRTOS queue containing the blocks that are currently unused.
|
||||||
|
QueueHandle_t free_blocks_; |
||||||
|
}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Wrapper around an ArenaPtr that handles acquiring and returning the block |
||||||
|
* through RAII. |
||||||
|
*/ |
||||||
|
class ArenaRef { |
||||||
|
public: |
||||||
|
static auto Acquire(Arena* a) -> std::optional<ArenaRef>; |
||||||
|
explicit ArenaRef(ArenaPtr ptr); |
||||||
|
~ArenaRef(); |
||||||
|
|
||||||
|
auto Release() -> ArenaPtr; |
||||||
|
|
||||||
|
ArenaRef(ArenaRef&&); |
||||||
|
ArenaRef(const ArenaRef&) = delete; |
||||||
|
Arena& operator=(const Arena&) = delete; |
||||||
|
|
||||||
|
ArenaPtr ptr; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace memory
|
Loading…
Reference in new issue