Fixed possible errors in memory allocation greater than 64K.

This commit is contained in:
mutouyun 2024-03-09 19:54:18 +08:00
parent 2a1d8fa5fa
commit 174d5e41df
3 changed files with 63 additions and 18 deletions

View File

@ -42,6 +42,7 @@
# define LIBIMP_CC_MSVC_2015 1900 # define LIBIMP_CC_MSVC_2015 1900
# define LIBIMP_CC_MSVC_2017 1910 # define LIBIMP_CC_MSVC_2017 1910
# define LIBIMP_CC_MSVC_2019 1920 # define LIBIMP_CC_MSVC_2019 1920
# define LIBIMP_CC_MSVC_2022 1930
#elif defined(__GNUC__) #elif defined(__GNUC__)
# define LIBIMP_CC_GNUC __GNUC__ # define LIBIMP_CC_GNUC __GNUC__
# if defined(__clang__) # if defined(__clang__)

View File

@ -25,7 +25,7 @@ LIBPMR_NAMESPACE_BEG_
class LIBIMP_EXPORT block_collector { class LIBIMP_EXPORT block_collector {
public: public:
virtual ~block_collector() noexcept = default; virtual ~block_collector() noexcept = default;
virtual void deallocate(void *p) noexcept = 0; virtual void recycle(void *p, std::size_t bytes, std::size_t alignment) noexcept = 0;
}; };
using get_block_collector_t = block_collector *(*)() noexcept; using get_block_collector_t = block_collector *(*)() noexcept;
@ -60,15 +60,41 @@ constexpr inline std::size_t regular_sizeof() noexcept {
return regular_sizeof_impl(regular_head_size + sizeof(T)); return regular_sizeof_impl(regular_head_size + sizeof(T));
} }
/// \brief Use block pools to handle memory less than 64K.
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
class block_resource_base : public block_pool<BlockSize, BlockPoolExpansion> {
public:
void *allocate(std::size_t /*bytes*/, std::size_t /*alignment*/) noexcept {
return block_pool<BlockSize, BlockPoolExpansion>::allocate();
}
void deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/) noexcept {
block_pool<BlockSize, BlockPoolExpansion>::deallocate(p);
}
};
/// \brief Use `new`/`delete` to handle memory larger than 64K.
template <std::size_t BlockSize>
class block_resource_base<BlockSize, 0> : public new_delete_resource {
public:
void *allocate(std::size_t bytes, std::size_t alignment) noexcept {
return new_delete_resource::allocate(regular_head_size + bytes, alignment);
}
void deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept {
new_delete_resource::deallocate(p, regular_head_size + bytes, alignment);
}
};
/// \brief Defines block pool memory resource based on block pool. /// \brief Defines block pool memory resource based on block pool.
template <std::size_t BlockSize, std::size_t BlockPoolExpansion> template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion> class block_pool_resource : public block_resource_base<BlockSize, BlockPoolExpansion>
, public block_collector { , public block_collector {
using base_t = block_pool<BlockSize, BlockPoolExpansion>; using base_t = block_resource_base<BlockSize, BlockPoolExpansion>;
void deallocate(void *p) noexcept override { void recycle(void *p, std::size_t bytes, std::size_t alignment) noexcept override {
base_t::deallocate(p); base_t::deallocate(p, bytes, alignment);
} }
public: public:
@ -77,22 +103,20 @@ public:
return &instance; return &instance;
} }
using base_t::base_t; void *allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept {
void *p = base_t::allocate(bytes, alignment);
void *allocate(std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
void *p = base_t::allocate();
*static_cast<get_block_collector_t *>(p) = get; *static_cast<get_block_collector_t *>(p) = get;
return static_cast<::LIBIMP::byte *>(p) + regular_head_size; return static_cast<::LIBIMP::byte *>(p) + regular_head_size;
} }
void deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept { void deallocate(void *p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept {
p = static_cast<::LIBIMP::byte *>(p) - regular_head_size; p = static_cast<::LIBIMP::byte *>(p) - regular_head_size;
auto g = *static_cast<get_block_collector_t *>(p); auto g = *static_cast<get_block_collector_t *>(p);
if (g == get) { if (g == get) {
base_t::deallocate(p); base_t::deallocate(p, bytes, alignment);
return; return;
} }
g()->deallocate(p); g()->recycle(p, bytes, alignment);
} }
}; };
@ -115,9 +139,6 @@ struct regular_resource {
} }
}; };
template <std::size_t N>
struct regular_resource<N, 4> : new_delete_resource {};
/// \brief Creates an object based on the specified type and parameters with block pool resource. /// \brief Creates an object based on the specified type and parameters with block pool resource.
/// \note This function is thread-safe. /// \note This function is thread-safe.
template <typename T, typename... A> template <typename T, typename... A>
@ -136,11 +157,11 @@ void delete$(T *p) noexcept {
::LIBIMP::destroy(p); ::LIBIMP::destroy(p);
auto *res = regular_resource<regular_sizeof<T>()>::get(); auto *res = regular_resource<regular_sizeof<T>()>::get();
if (res == nullptr) return; if (res == nullptr) return;
#if defined(LIBIMP_CC_MSVC_2015) #if (LIBIMP_CC_MSVC > LIBIMP_CC_MSVC_2015)
res->deallocate(p, sizeof(T), alignof(T));
#else
// `alignof` of vs2015 requires that type must be able to be instantiated. // `alignof` of vs2015 requires that type must be able to be instantiated.
res->deallocate(p, sizeof(T)); res->deallocate(p, sizeof(T));
#else
res->deallocate(p, sizeof(T), alignof(T));
#endif #endif
} }

View File

@ -93,6 +93,14 @@ private:
int value_; int value_;
}; };
class Derived64K : public Derived {
public:
using Derived::Derived;
private:
std::array<char, 65536> padding_;
};
} // namespace } // namespace
TEST(pmr_new, delete$poly) { TEST(pmr_new, delete$poly) {
@ -110,6 +118,21 @@ TEST(pmr_new, delete$poly) {
ASSERT_EQ(construct_count__, 0); ASSERT_EQ(construct_count__, 0);
} }
TEST(pmr_new, delete$poly64k) {
Base *p = pmr::new$<Derived64K>(-1);
ASSERT_NE(p, nullptr);
ASSERT_EQ(p->get(), -1);
ASSERT_EQ(construct_count__, -1);
pmr::delete$(p);
ASSERT_EQ(construct_count__, 0);
Base *q = pmr::new$<Derived64K>((std::numeric_limits<int>::max)());
ASSERT_EQ(q->get(), (std::numeric_limits<int>::max)());
ASSERT_EQ(construct_count__, (std::numeric_limits<int>::max)());
pmr::delete$(q);
ASSERT_EQ(construct_count__, 0);
}
TEST(pmr_new, delete$null) { TEST(pmr_new, delete$null) {
Base *p = nullptr; Base *p = nullptr;
pmr::delete$(p); pmr::delete$(p);