Program Listing for File allocator.hpp

Return to documentation for file (include/wrench/memory/allocator.hpp)

//==--- wrench/memory/allocator.hpp ------------------------ -*- C++ -*- ---==//
//
//                                  Wrench
//
//                      Copyright (c) 2020 Rob Clucas
//
//  This file is distributed under the MIT License. See LICENSE for details.
//
//==------------------------------------------------------------------------==//
//
//
//==------------------------------------------------------------------------==//

#ifndef WRENCH_MEMORY_ALLOCATOR_HPP
#define WRENCH_MEMORY_ALLOCATOR_HPP

#include "aligned_heap_allocator.hpp"
#include "arena.hpp"
#include "pool_allocator.hpp"
#include <mutex>

namespace wrench {

struct VoidLock {
  auto lock() noexcept -> void {}
  auto unlock() noexcept -> void {}
};

//==--- [forward declarations & aliases] -----------------------------------==//

template <
  typename PrimaryAllocator,
  typename Arena             = HeapArena,
  typename FallbackAllocator = AlignedHeapAllocator,
  typename LockingPolicy     = VoidLock>
class Allocator;

template <
  typename T,
  typename LockingPolicy = VoidLock,
  typename Arena         = HeapArena>
using ObjectPoolAllocator = Allocator<
  PoolAllocator<sizeof(T), std::max(alignof(T), alignof(Freelist)), Freelist>,
  Arena,
  AlignedHeapAllocator,
  LockingPolicy>;

template <typename T, typename Arena = HeapArena>
using ThreadSafeObjectPoolAllocator = Allocator<
  PoolAllocator<
    sizeof(T),
    std::max(alignof(T), alignof(ThreadSafeFreelist)),
    ThreadSafeFreelist>,
  Arena,
  AlignedHeapAllocator,
  VoidLock>;

//==--- [implementation] ---------------------------------------------------==//

template <
  typename PrimaryAllocator,
  typename Arena,
  typename FallbackAllocator,
  typename LockingPolicy>
class Allocator {
  static_assert(
    std::is_trivially_constructible_v<FallbackAllocator>,
    "Fallback allocator must be trivially constructible!");

 public:
  //==--- [constants] ------------------------------------------------------==//

  static constexpr bool contexpr_arena_size = Arena::contexpr_size;

  //==--- [aliases] --------------------------------------------------------==//

  using Guard = std::lock_guard<LockingPolicy>;

  //==--- [construction] ---------------------------------------------------==//

  template <typename... Args>
  Allocator(size_t size, Args&&... args)
  : arena_(size), primary_(arena_, std::forward<Args>(args)...) {}

  ~Allocator() noexcept = default;

  // clang-format off
  Allocator(Allocator&& other) noexcept                   = default;
  auto operator=(Allocator&& other) noexcept -> Allocator& = default;

  //==--- [deleted] --------------------------------------------------------==//

  Allocator(const Allocator&)                    = delete;
  auto operator=(const Allocator&) -> Allocator& = delete;
  // clang-format on

  //==--- [alloc/free interface] -------------------------------------------==//

  auto alloc(size_t size, size_t alignment = alignof(std::max_align_t)) noexcept
    -> void* {
    Guard g(lock_);
    void* ptr = primary_.alloc(size, alignment);
    if (ptr == nullptr) {
      ptr = fallback_.alloc(size, alignment);
    }
    return ptr;
  }

  auto free(void* ptr) noexcept -> void {
    if (ptr == nullptr) {
      return;
    }

    Guard g(lock_);
    if (primary_.owns(ptr)) {
      primary_.free(ptr);
      return;
    }

    fallback_.free(ptr);
  }

  auto free(void* ptr, size_t size) noexcept -> void {
    if (ptr == nullptr) {
      return;
    }

    Guard g(lock_);
    if (primary_.owns(ptr)) {
      primary_.free(ptr, size);
      return;
    }
    fallback_.free(ptr, size);
  }

  auto reset() noexcept -> void {
    Guard g(lock_);
    primary_.reset();
  }

  //==--- [create/recycle interface] ---------------------------------------==//

  template <typename T, typename... Args>
  auto create(Args&&... args) noexcept -> T* {
    constexpr size_t size      = sizeof(T);
    constexpr size_t alignment = alignof(T);
    void* const      ptr       = alloc(size, alignment);

    return new (ptr) T(std::forward<Args>(args)...);
  }

  template <typename T>
  auto recycle(T* ptr) noexcept -> void {
    if (ptr == nullptr) {
      return;
    }

    ptr->~T();
    free(static_cast<void*>(ptr), sizeof(T));
  }

 private:
  Arena             arena_;
  PrimaryAllocator  primary_;
  FallbackAllocator fallback_;
  LockingPolicy     lock_;
};

} // namespace wrench

#endif // WRENCH_MEMORY_ALLOCATOR_HP