Skip to content

Instantly share code, notes, and snippets.

@unrays
Created May 22, 2026 19:52
Show Gist options
  • Select an option

  • Save unrays/f311c653d7f57b458d87c405e352602f to your computer and use it in GitHub Desktop.

Select an option

Save unrays/f311c653d7f57b458d87c405e352602f to your computer and use it in GitHub Desktop.
Custom memory_resource implementation with an atomic monotonic buffer allocator supporting aligned, lock-free allocations on a preallocated memory region.
// Copyright (c) May 2026 Félix-Olivier Dumas. All rights reserved.
// Licensed under the terms described in the LICENSE file
#pragma once
#include <cstddef>
#include <atomic>
#include <stdexcept>
namespace exotic::memory {
class memory_resource {
public:
virtual ~memory_resource() = default;
public:
[[nodiscard]] void* allocate(std::size_t bytes, std::size_t alignment) noexcept {
if (alignment == 0 || (alignment & (alignment - 1)) != 0) [[unlikely]]
throw std::invalid_argument("alignment must be a power of two");
return do_allocate(bytes, alignment);
}
void deallocate(void* p, std::size_t bytes, std::size_t alignment) noexcept {
if (alignment == 0 || (alignment & (alignment - 1)) != 0) [[unlikely]]
throw std::invalid_argument("alignment must be a power of two");
do_deallocate(p, bytes, alignment);
}
bool is_equal(const memory_resource& other) const noexcept {
return do_is_equal(other);
}
protected:
virtual void* do_allocate(std::size_t bytes, std::size_t alignment) = 0;
virtual void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) = 0;
virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
};
struct monotonic_atomic_buffer : public memory_resource {
public:
explicit monotonic_atomic_buffer(std::size_t p_capacity)
: capacity_(p_capacity)
, offset_(0)
, buffer_(new std::byte[p_capacity])
{}
monotonic_atomic_buffer(const monotonic_atomic_buffer&) = delete;
monotonic_atomic_buffer& operator=(const monotonic_atomic_buffer&) = delete;
~monotonic_atomic_buffer() noexcept override {
delete[] buffer_;
}
protected:
void* do_allocate(std::size_t bytes, std::size_t alignment) override {
std::size_t current = offset_.load(std::memory_order_relaxed);
while (true) {
std::size_t aligned = (current + (alignment - 1)) & ~(alignment - 1);
std::size_t next = aligned + bytes;
if (next > capacity_)
return nullptr;
if (offset_.compare_exchange_weak(
current,
next,
std::memory_order_relaxed)) {
return buffer_ + aligned;
}
}
}
void do_deallocate(void*, std::size_t, std::size_t) override {}
bool do_is_equal(const memory_resource& other) const noexcept override {
return this == &other;
}
public:
[[nodiscard]] constexpr std::size_t capacity() const noexcept { return capacity_; }
[[nodiscard]] std::size_t size() const noexcept { return offset_.load(std::memory_order_relaxed); }
private:
std::byte* buffer_;
std::atomic<std::size_t> offset_;
std::size_t capacity_;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment