parent
2849399d54
commit
f6d0642109
@ -1,86 +0,0 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#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() { |
||||
// We shouldn't have any blocks in use when destroying an arena.
|
||||
assert(uxQueueSpacesAvailable(free_blocks_) == 0); |
||||
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 Arena::BlocksFree() -> std::size_t { |
||||
return uxQueueMessagesWaiting(free_blocks_); |
||||
} |
||||
|
||||
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
|
@ -1,91 +0,0 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#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
|
@ -0,0 +1,42 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <memory_resource> |
||||
#include <string> |
||||
|
||||
#include <esp_heap_caps.h> |
||||
#include <stdint.h> |
||||
|
||||
namespace memory { |
||||
|
||||
enum class Capabilities : uint32_t { |
||||
kDefault = MALLOC_CAP_DEFAULT, |
||||
kInternal = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT, |
||||
kDma = MALLOC_CAP_DMA, |
||||
kSpiRam = MALLOC_CAP_SPIRAM, |
||||
}; |
||||
|
||||
class Resource : public std::pmr::memory_resource { |
||||
public: |
||||
explicit Resource(Capabilities caps) : caps_(caps) {} |
||||
|
||||
private: |
||||
Capabilities caps_; |
||||
|
||||
void* do_allocate(std::size_t bytes, std::size_t alignment) override; |
||||
|
||||
void do_deallocate(void* p, |
||||
std::size_t bytes, |
||||
std::size_t alignment) override; |
||||
|
||||
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override; |
||||
}; |
||||
|
||||
extern Resource kSpiRamResource; |
||||
|
||||
} // namespace memory
|
@ -0,0 +1,34 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "memory_resource.hpp" |
||||
|
||||
#include <memory_resource> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include <esp_heap_caps.h> |
||||
#include <stdint.h> |
||||
|
||||
namespace memory { |
||||
|
||||
Resource kSpiRamResource{Capabilities::kSpiRam}; |
||||
|
||||
void* Resource::do_allocate(std::size_t bytes, std::size_t alignment) { |
||||
return heap_caps_malloc(bytes, std::to_underlying(caps_)); |
||||
} |
||||
|
||||
void Resource::do_deallocate(void* p, |
||||
std::size_t bytes, |
||||
std::size_t alignment) { |
||||
heap_caps_free(p); |
||||
} |
||||
|
||||
bool Resource::do_is_equal(const std::pmr::memory_resource& other) const { |
||||
return this == &other; |
||||
} |
||||
|
||||
} // namespace memory
|
Loading…
Reference in new issue