/** * \file libipc/mem.h * \author mutouyun (orz@orzz.org) * \brief Global memory management. */ #pragma once #include #include #include #include #include #include "libipc/imp/aligned.h" #include "libipc/imp/uninitialized.h" #include "libipc/imp/byte.h" #include "libipc/imp/detect_plat.h" #include "libipc/imp/export.h" #include "libipc/mem/memory_resource.h" #include "libipc/mem/block_pool.h" namespace ipc { namespace mem { /// \brief Defines the memory block collector interface. class LIBIPC_EXPORT block_collector { public: virtual ~block_collector() noexcept = default; virtual void *allocate(std::size_t /*bytes*/) noexcept = 0; virtual void deallocate(void */*p*/, std::size_t /*bytes*/) noexcept = 0; }; /// \brief Matches the appropriate memory block resource based on a specified size. LIBIPC_EXPORT block_collector &get_regular_resource(std::size_t s) noexcept; /// \brief Allocates storage with a size of at least bytes bytes. LIBIPC_EXPORT void *alloc(std::size_t bytes) noexcept; LIBIPC_EXPORT void free (void *p, std::size_t bytes) noexcept; namespace detail_new { #if defined(LIBIPC_CPP_17) using recycle_t = void (*)(void *p) noexcept; #else using recycle_t = void (*)(void *p); #endif static constexpr std::size_t recycler_size = round_up(sizeof(recycle_t), alignof(std::size_t)); static constexpr std::size_t allocated_size = sizeof(std::size_t); static constexpr std::size_t regular_head_size = round_up(recycler_size + allocated_size, alignof(std::max_align_t)); template struct do_allocate { template static T *apply(A &&... args) noexcept { void *b = mem::alloc(regular_head_size + sizeof(T)); auto *p = static_cast(b) + regular_head_size; LIBIPC_TRY { T *t = construct(p, std::forward(args)...); *reinterpret_cast(b) = [](void *p) noexcept { mem::free(static_cast(destroy(static_cast(p))) - regular_head_size , regular_head_size + sizeof(T)); }; return t; } LIBIPC_CATCH(...) { return nullptr; } } }; template <> struct do_allocate { static void *apply(std::size_t bytes) noexcept { if (bytes == 0) return nullptr; std::size_t rbz = regular_head_size + bytes; void *b = mem::alloc(rbz); *reinterpret_cast(b) = [](void *p) noexcept { auto *b = static_cast(p) - regular_head_size; mem::free(b, *reinterpret_cast(b + recycler_size)); }; auto *z = static_cast(b) + recycler_size; *reinterpret_cast(z) = rbz; return static_cast(b) + regular_head_size; } }; } // namespace detail_new /// \brief Creates an object based on the specified type and parameters with block pool resource. /// \note This function is thread-safe. template T *$new(A &&... args) noexcept { return detail_new::do_allocate::apply(std::forward(args)...); } /// \brief Destroys object previously allocated by the `$new` and releases obtained memory area. /// \note This function is thread-safe. If the pointer type passed in is different from `$new`, /// additional performance penalties may be incurred. inline void $delete(void *p) noexcept { if (p == nullptr) return; auto *r = reinterpret_cast(static_cast(p) - detail_new::regular_head_size); (*r)(p); } /// \brief The destruction policy used by std::unique_ptr. /// \see https://en.cppreference.com/w/cpp/memory/default_delete struct deleter { template void operator()(T *p) const noexcept { $delete(p); } }; } // namespace mem } // namespace ipc