From 180920968fa81788384226e60e3a90655a66b5e2 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 26 Jan 2025 10:38:07 +0800 Subject: [PATCH] The memory allocator supports runtime dynamic size memory allocation --- include/libipc/imp/uninitialized.h | 5 ++ include/libipc/mem/new.h | 85 +++++++++++++++++++++++------- test/mem/test_mem_new.cpp | 16 ++++++ 3 files changed, 87 insertions(+), 19 deletions(-) diff --git a/include/libipc/imp/uninitialized.h b/include/libipc/imp/uninitialized.h index c5f71bf..01b69c8 100644 --- a/include/libipc/imp/uninitialized.h +++ b/include/libipc/imp/uninitialized.h @@ -53,6 +53,11 @@ void *destroy(T *p) noexcept { return p; } +template <> +inline void *destroy(void *p) noexcept { + return p; +} + template void *destroy(T (*p)[N]) noexcept { if (p == nullptr) return nullptr; diff --git a/include/libipc/mem/new.h b/include/libipc/mem/new.h index 65468a7..cc90f24 100644 --- a/include/libipc/mem/new.h +++ b/include/libipc/mem/new.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "libipc/imp/aligned.h" #include "libipc/imp/uninitialized.h" @@ -29,13 +30,13 @@ public: }; #if defined(LIBIPC_CPP_17) -using get_block_collector_t = block_collector *(*)() noexcept; +using recycle_t = void (*)(void *p, void *o, std::size_t bytes, std::size_t alignment) noexcept; #else -using get_block_collector_t = block_collector *(*)(); +using recycle_t = void (*)(void *p, void *o, std::size_t bytes, std::size_t alignment); #endif static constexpr std::size_t regular_head_size - = round_up(sizeof(get_block_collector_t), alignof(std::max_align_t)); + = round_up(sizeof(recycle_t), alignof(std::max_align_t)); /// \brief Select the incremental level based on the size. constexpr inline std::size_t regular_level(std::size_t s) noexcept { @@ -64,6 +65,11 @@ constexpr inline std::size_t regular_sizeof() noexcept { return regular_sizeof_impl(regular_head_size + sizeof(T)); } +template <> +constexpr inline std::size_t regular_sizeof() noexcept { + return (std::numeric_limits::max)(); +} + /// \brief Use block pools to handle memory less than 64K. template class block_resource_base : public block_pool { @@ -107,20 +113,21 @@ public: return &instance; } + template void *allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept { void *p = base_t::allocate(bytes, alignment); - *static_cast(p) = get; + *static_cast(p) + = [](void *p, void *o, std::size_t bytes, std::size_t alignment) noexcept { + std::ignore = destroy(static_cast(o)); + get()->recycle(p, bytes, alignment); + }; return static_cast(p) + regular_head_size; } void deallocate(void *p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept { - p = static_cast(p) - regular_head_size; - auto g = *static_cast(p); - if (g == get) { - base_t::deallocate(p, bytes, alignment); - return; - } - g()->recycle(p, bytes, alignment); + void *b = static_cast(p) - regular_head_size; + auto *r = static_cast(b); + (*r)(b, p, bytes, alignment); } }; @@ -141,13 +148,59 @@ auto *get_regular_resource() noexcept { return dynamic_cast(block_poll_resource_t::get()); } +namespace detail_new { + +template +struct do_allocate { + template + static T *apply(R *res, A &&... args) noexcept { + LIBIPC_TRY { + return construct(res->template allocate(sizeof(T), alignof(T)), std::forward(args)...); + } LIBIPC_CATCH(...) { + return nullptr; + } + } +}; + +template <> +struct do_allocate { + template + static void *apply(R *res, std::size_t bytes) noexcept { + if (bytes == 0) return nullptr; + return res->template allocate(bytes); + } +}; + +template +struct do_deallocate { + template + static void apply(R *res, T *p) noexcept { +#if (LIBIPC_CC_MSVC > LIBIPC_CC_MSVC_2015) + res->deallocate(p, sizeof(T), alignof(T)); +#else + // `alignof` of vs2015 requires that type must be able to be instantiated. + res->deallocate(p, sizeof(T)); +#endif + } +}; + +template <> +struct do_deallocate { + template + static void apply(R *res, void *p) noexcept { + res->deallocate(p, 0); + } +}; + +} // 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 { auto *res = get_regular_resource(); if (res == nullptr) return nullptr; - return construct(res->allocate(sizeof(T), alignof(T)), std::forward(args)...); + return detail_new::do_allocate::apply(res, std::forward(args)...); } /// \brief Destroys object previously allocated by the `$new` and releases obtained memory area. @@ -156,15 +209,9 @@ T *$new(A &&... args) noexcept { template void $delete(T *p) noexcept { if (p == nullptr) return; - destroy(p); auto *res = get_regular_resource(); if (res == nullptr) return; -#if (LIBIPC_CC_MSVC > LIBIPC_CC_MSVC_2015) - res->deallocate(p, sizeof(T), alignof(T)); -#else - // `alignof` of vs2015 requires that type must be able to be instantiated. - res->deallocate(p, sizeof(T)); -#endif + detail_new::do_deallocate::apply(res, p); } /// \brief The destruction policy used by std::unique_ptr. diff --git a/test/mem/test_mem_new.cpp b/test/mem/test_mem_new.cpp index bb05b6d..2a0432d 100644 --- a/test/mem/test_mem_new.cpp +++ b/test/mem/test_mem_new.cpp @@ -139,6 +139,22 @@ TEST(new, delete_null) { SUCCEED(); } +TEST(new, malloc) { + void *p = ipc::mem::$new(0); + ASSERT_EQ(p, nullptr); + ipc::mem::$delete(p); + p = ipc::mem::$new(1024); + ASSERT_NE(p, nullptr); + ipc::mem::$delete(p); + + p = ipc::mem::$new(-1); + ASSERT_NE(p, nullptr); + ASSERT_EQ(((Derived *)p)->get(), -1); + ASSERT_EQ(construct_count__, -1); + ipc::mem::$delete(p); + ASSERT_EQ(construct_count__, 0); +} + TEST(new, multi_thread) { std::array threads; for (auto &t : threads) {