mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
Add comments and refine the implementation.
This commit is contained in:
parent
01bbcf7dca
commit
5aaa78a8a5
@ -14,6 +14,7 @@
|
||||
#include "libimp/uninitialized.h"
|
||||
#include "libimp/byte.h"
|
||||
#include "libimp/detect_plat.h"
|
||||
#include "libimp/export.h"
|
||||
|
||||
#include "libpmr/def.h"
|
||||
#include "libpmr/block_pool.h"
|
||||
@ -21,6 +22,7 @@
|
||||
|
||||
LIBPMR_NAMESPACE_BEG_
|
||||
|
||||
/// \brief Select the incremental level based on the size.
|
||||
constexpr inline std::size_t regular_level(std::size_t s) noexcept {
|
||||
return (s <= 128 ) ? 0 :
|
||||
(s <= 1024 ) ? 1 :
|
||||
@ -28,6 +30,7 @@ constexpr inline std::size_t regular_level(std::size_t s) noexcept {
|
||||
(s <= 65536) ? 3 : 4;
|
||||
}
|
||||
|
||||
/// \brief Calculates the appropriate memory block size based on the increment level and size.
|
||||
constexpr inline std::size_t regular_sizeof_impl(std::size_t l, std::size_t s) noexcept {
|
||||
return (l == 0) ? std::max<std::size_t>(::LIBIMP::round_up<std::size_t>(s, 8), regular_head_size) :
|
||||
(l == 1) ? ::LIBIMP::round_up<std::size_t>(s, 128 ) :
|
||||
@ -35,54 +38,51 @@ constexpr inline std::size_t regular_sizeof_impl(std::size_t l, std::size_t s) n
|
||||
(l == 3) ? ::LIBIMP::round_up<std::size_t>(s, 8192) : (std::numeric_limits<std::size_t>::max)();
|
||||
}
|
||||
|
||||
/// \brief Calculates the appropriate memory block size based on the size.
|
||||
constexpr inline std::size_t regular_sizeof(std::size_t s) noexcept {
|
||||
return regular_sizeof_impl(regular_level(s), s);
|
||||
}
|
||||
|
||||
/// \brief Calculates the appropriate memory block size based on the specific type.
|
||||
template <typename T>
|
||||
constexpr inline std::size_t regular_sizeof() noexcept {
|
||||
return regular_sizeof(regular_head_size + sizeof(T));
|
||||
}
|
||||
|
||||
class block_pool_like {
|
||||
/// \brief Defines the memory block collector interface.
|
||||
class LIBIMP_EXPORT block_collector {
|
||||
public:
|
||||
virtual ~block_pool_like() noexcept = default;
|
||||
virtual void *allocate() noexcept = 0;
|
||||
virtual ~block_collector() noexcept = default;
|
||||
virtual void deallocate(void *p) noexcept = 0;
|
||||
};
|
||||
|
||||
inline std::unordered_map<std::size_t, block_pool_like *> &get_block_pool_map() noexcept {
|
||||
thread_local std::unordered_map<std::size_t, block_pool_like *> instances;
|
||||
return instances;
|
||||
}
|
||||
/// \brief Gets all block pools of the thread cache.
|
||||
LIBIMP_EXPORT auto get_thread_block_pool_map() noexcept
|
||||
-> std::unordered_map<std::size_t, block_collector *> &;
|
||||
|
||||
/// \brief Defines block pool memory resource based on block pool.
|
||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||
class block_pool_resource;
|
||||
|
||||
/// \brief Memory block collector of unknown size.
|
||||
/// \note This memory resource is only used to temporarily collect memory blocks
|
||||
/// that cannot find a suitable block pool memory resource.
|
||||
template <>
|
||||
class block_pool_resource<0, 0> : public block_pool<0, 0>
|
||||
, public block_pool_like {
|
||||
|
||||
void *allocate() noexcept override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
, public block_collector {
|
||||
public:
|
||||
void deallocate(void *p) noexcept override {
|
||||
block_pool<0, 0>::deallocate(p);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief A block pool memory resource for a block of memory of a specific size.
|
||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||
class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion>
|
||||
, public block_pool_like {
|
||||
, public block_collector {
|
||||
|
||||
using base_t = block_pool<BlockSize, BlockPoolExpansion>;
|
||||
|
||||
void *allocate() noexcept override {
|
||||
return base_t::allocate();
|
||||
}
|
||||
|
||||
void deallocate(void *p) noexcept override {
|
||||
base_t::deallocate(p);
|
||||
}
|
||||
@ -105,7 +105,9 @@ public:
|
||||
base_t::deallocate(p);
|
||||
return;
|
||||
}
|
||||
auto &map = get_block_pool_map();
|
||||
// When the actual size exceeds the current memory block size,
|
||||
// try to find a suitable pool among all memory block pools for this thread.
|
||||
auto &map = get_thread_block_pool_map();
|
||||
auto it = map.find(r_size);
|
||||
if ((it == map.end()) || (it->second == nullptr)) {
|
||||
block_pool_resource<0, 0> *bp = nullptr;
|
||||
@ -128,13 +130,16 @@ public:
|
||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
||||
auto block_pool_resource<BlockSize, BlockPoolExpansion>::get() noexcept
|
||||
-> block_pool_resource<BlockSize, BlockPoolExpansion> * {
|
||||
auto &map = get_block_pool_map();
|
||||
thread_local block_pool_resource *pi = nullptr;
|
||||
if (pi != nullptr) {
|
||||
return pi;
|
||||
}
|
||||
// Create a new block pool resource for this thread.
|
||||
auto &map = get_thread_block_pool_map();
|
||||
auto it = map.find(BlockSize);
|
||||
if ((it != map.end()) && (it->second != nullptr)) {
|
||||
// If there are existing block pool resources in the thread cache,
|
||||
// a new block pool resource is constructed based on it and the cache is updated.
|
||||
auto *bp = static_cast <block_pool<0, 0> *>(
|
||||
dynamic_cast<block_pool_resource<0, 0> *>(it->second));
|
||||
if (bp == nullptr) {
|
||||
@ -142,34 +147,35 @@ auto block_pool_resource<BlockSize, BlockPoolExpansion>::get() noexcept
|
||||
}
|
||||
thread_local block_pool_resource instance(std::move(*bp));
|
||||
delete static_cast<block_pool_resource<0, 0> *>(bp);
|
||||
pi = &instance;
|
||||
it->second = pi = &instance;
|
||||
return pi;
|
||||
} else {
|
||||
// If there are no existing block pool resources in the thread cache,
|
||||
// the thread local storage instance is constructed and the pointer is cached.
|
||||
thread_local block_pool_resource instance;
|
||||
pi = &instance;
|
||||
LIBIMP_TRY {
|
||||
map.emplace(BlockSize, pi = &instance);
|
||||
return pi;
|
||||
} LIBIMP_CATCH(...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
LIBIMP_TRY {
|
||||
map.emplace(BlockSize, pi);
|
||||
} LIBIMP_CATCH(...) {
|
||||
return nullptr;
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
/// \brief Match the appropriate memory block resources
|
||||
/// according to the size of the specification.
|
||||
template <std::size_t N, std::size_t L = regular_level(N)>
|
||||
class regular_resource : public new_delete_resource {};
|
||||
|
||||
template <std::size_t N>
|
||||
class regular_resource<N, 0> : public block_pool_resource<N, 512> {};
|
||||
|
||||
template <std::size_t N>
|
||||
class regular_resource<N, 1> : public block_pool_resource<N, 256> {};
|
||||
|
||||
template <std::size_t N>
|
||||
class regular_resource<N, 2> : public block_pool_resource<N, 128> {};
|
||||
|
||||
template <std::size_t N>
|
||||
class regular_resource<N, 3> : public block_pool_resource<N, 64> {};
|
||||
/// \brief Different increment levels match different chunk sizes.
|
||||
/// 512 means that 512 consecutive memory blocks are allocated at a time, and the block size is N.
|
||||
template <std::size_t N> class regular_resource<N, 0> : public block_pool_resource<N, 512> {};
|
||||
template <std::size_t N> class regular_resource<N, 1> : public block_pool_resource<N, 256> {};
|
||||
template <std::size_t N> class regular_resource<N, 2> : public block_pool_resource<N, 128> {};
|
||||
template <std::size_t N> class regular_resource<N, 3> : public block_pool_resource<N, 64 > {};
|
||||
|
||||
/// \brief Creates an object based on the specified type and parameters with block pool resource.
|
||||
/// \note This function is thread-safe.
|
||||
template <typename T, typename... A>
|
||||
T *new$(A &&... args) noexcept {
|
||||
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
|
||||
@ -177,6 +183,9 @@ T *new$(A &&... args) noexcept {
|
||||
return ::LIBIMP::construct<T>(mem_res->allocate(sizeof(T), alignof(T)), std::forward<A>(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.
|
||||
template <typename T>
|
||||
void delete$(T *p) noexcept {
|
||||
if (p == nullptr) return;
|
||||
@ -184,6 +193,7 @@ void delete$(T *p) noexcept {
|
||||
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
|
||||
if (mem_res == nullptr) return;
|
||||
#if defined(LIBIMP_CC_MSVC_2015)
|
||||
// `alignof` of vs2015 requires that type must be able to be instantiated.
|
||||
mem_res->deallocate(p, sizeof(T));
|
||||
#else
|
||||
mem_res->deallocate(p, sizeof(T), alignof(T));
|
||||
|
||||
12
src/libpmr/new.cpp
Normal file
12
src/libpmr/new.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
#include "libpmr/new.h"
|
||||
|
||||
LIBPMR_NAMESPACE_BEG_
|
||||
|
||||
auto get_thread_block_pool_map() noexcept
|
||||
-> std::unordered_map<std::size_t, block_collector *> & {
|
||||
thread_local std::unordered_map<std::size_t, block_collector *> instances;
|
||||
return instances;
|
||||
}
|
||||
|
||||
LIBPMR_NAMESPACE_END_
|
||||
Loading…
x
Reference in New Issue
Block a user