Skip to content

Commit 2f79716

Browse files
committed
Update libpmr code
1 parent fc60c75 commit 2f79716

File tree

4 files changed

+311
-15
lines changed

4 files changed

+311
-15
lines changed

include/libpmr/block_pool.h

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <new>
1111
#include <array>
1212
#include <deque>
13+
#include <utility>
1314

1415
#include "libimp/byte.h"
1516
#include "libimp/export.h"
@@ -37,7 +38,7 @@ union block {
3738

3839
/**
3940
* \brief A fixed-length memory block central cache pool.
40-
* \tparam BlockSize specifies the memory block size
41+
* \tparam BlockT specifies the memory block type
4142
*/
4243
template <typename BlockT, std::size_t BlockPoolExpansion>
4344
class central_cache_pool {
@@ -66,7 +67,7 @@ class central_cache_pool {
6667
if (chunk == nullptr) {
6768
return nullptr;
6869
}
69-
for (std::size_t i = 0; i < block_pool_expansion - 1; ++i) {
70+
for (std::size_t i = 0; i < BlockPoolExpansion - 1; ++i) {
7071
(*chunk)[i].next = &(*chunk)[i + 1];
7172
}
7273
chunk->back().next = nullptr;
@@ -91,12 +92,103 @@ class central_cache_pool {
9192
}
9293
};
9394

95+
/// \brief A fixed-length memory block central cache pool with no default expansion size.
96+
template <typename BlockT>
97+
class central_cache_pool<BlockT, 0> {
98+
99+
/// \brief The block type, which should be a union of a pointer and a storage.
100+
using block_t = BlockT;
101+
/// \brief The node type, which is used to store the block pointer.
102+
using node_t = typename ::LIBCONCUR::intrusive_stack<block_t *>::node;
103+
104+
/// \brief The central cache stack.
105+
::LIBCONCUR::intrusive_stack<block_t *> cached_;
106+
::LIBCONCUR::intrusive_stack<block_t *> aqueired_;
107+
108+
central_cache_pool() noexcept = default;
109+
110+
public:
111+
block_t *aqueire() noexcept {
112+
auto *n = cached_.pop();
113+
if (n != nullptr) {
114+
aqueired_.push(n);
115+
return n->value;
116+
}
117+
// For pools with no default expansion size,
118+
// the central cache pool is only buffered, not allocated.
119+
return nullptr;
120+
}
121+
122+
void release(block_t *p) noexcept {
123+
if (p == nullptr) return;
124+
auto *a = aqueired_.pop();
125+
if (a == nullptr) {
126+
a = central_cache_allocator().construct<node_t>();
127+
if (a == nullptr) return;
128+
}
129+
a->value = p;
130+
cached_.push(a);
131+
}
132+
133+
/// \brief Get the singleton instance.
134+
static central_cache_pool &instance() noexcept {
135+
static central_cache_pool pool;
136+
return pool;
137+
}
138+
};
139+
94140
/**
95141
* \brief Fixed-length memory block pool.
96142
* \tparam BlockSize specifies the memory block size
97143
* \tparam BlockPoolExpansion specifies the default number of blocks to expand when the block pool is exhausted
98144
*/
99145
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
146+
class block_pool;
147+
148+
/// \brief General-purpose block pool for any size of memory block.
149+
/// \note This block pool can only be used to deallocate a group of memory blocks of unknown but consistent size,
150+
/// and cannot be used for memory block allocation.
151+
template <>
152+
class block_pool<0, 0> {
153+
154+
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
155+
friend class block_pool;
156+
157+
/// \brief The block type.
158+
struct block_t {
159+
block_t *next;
160+
};
161+
162+
/// \brief The central cache pool type.
163+
using central_cache_pool_t = central_cache_pool<block_t, 0>;
164+
165+
public:
166+
constexpr static std::size_t block_size = 0;
167+
168+
block_pool() noexcept : cursor_(central_cache_pool_t::instance().aqueire()) {}
169+
~block_pool() noexcept {
170+
central_cache_pool_t::instance().release(cursor_);
171+
}
172+
173+
block_pool(block_pool const &) = delete;
174+
block_pool& operator=(block_pool const &) = delete;
175+
176+
block_pool(block_pool &&rhs) noexcept : cursor_(std::exchange(rhs.cursor_, nullptr)) {}
177+
block_pool &operator=(block_pool &&) noexcept = delete;
178+
179+
void deallocate(void *p) noexcept {
180+
if (p == nullptr) return;
181+
block_t *b = static_cast<block_t *>(p);
182+
b->next = cursor_;
183+
cursor_ = b;
184+
}
185+
186+
private:
187+
block_t *cursor_;
188+
};
189+
190+
/// \brief A block pool for a block of memory of a specific size.
191+
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
100192
class block_pool {
101193

102194
/// \brief The block type.
@@ -120,6 +212,15 @@ class block_pool {
120212
block_pool(block_pool const &) = delete;
121213
block_pool& operator=(block_pool const &) = delete;
122214

215+
block_pool(block_pool &&rhs) noexcept
216+
: cursor_(std::exchange(rhs.cursor_, nullptr)) {}
217+
block_pool &operator=(block_pool &&) noexcept = delete;
218+
219+
/// \brief Used to take all memory blocks from within a general-purpose block pool.
220+
/// \note Of course, the actual memory blocks they manage must be the same size.
221+
block_pool(block_pool<0, 0> &&rhs) noexcept
222+
: cursor_(reinterpret_cast<block_t *>(std::exchange(rhs.cursor_, nullptr))) {}
223+
123224
void *allocate() noexcept {
124225
if (cursor_ == nullptr) {
125226
cursor_ = expand();

include/libpmr/def.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
*/
77
#pragma once
88

9+
#include <cstddef>
10+
11+
#include "libimp/aligned.h"
12+
913
#define LIBPMR pmr
1014
#define LIBPMR_NAMESPACE_BEG_ namespace LIBPMR {
1115
#define LIBPMR_NAMESPACE_END_ }
@@ -15,8 +19,8 @@ LIBPMR_NAMESPACE_BEG_
1519
/// \brief Constants.
1620

1721
enum : std::size_t {
18-
block_pool_expansion = 64,
1922
central_cache_default_size = 1024 * 1024, ///< 1MB
23+
regular_head_size = ::LIBIMP::round_up(sizeof(std::size_t), alignof(std::max_align_t)),
2024
};
2125

2226
LIBPMR_NAMESPACE_END_

include/libpmr/new.h

Lines changed: 111 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
#pragma once
88

99
#include <cstddef>
10+
#include <unordered_map>
11+
#include <algorithm>
1012

1113
#include "libimp/aligned.h"
1214
#include "libimp/uninitialized.h"
15+
#include "libimp/byte.h"
16+
#include "libimp/detect_plat.h"
1317

1418
#include "libpmr/def.h"
1519
#include "libpmr/block_pool.h"
@@ -27,34 +31,126 @@ constexpr inline std::size_t regular_level(std::size_t s) noexcept {
2731

2832
constexpr inline std::size_t regular_sizeof(std::size_t s) noexcept {
2933
switch (regular_level(s)) {
30-
case 0 : return ::LIBIMP::round_up<std::size_t>(s, 8);
34+
case 0 : return std::max<std::size_t>(::LIBIMP::round_up<std::size_t>(s, 8), regular_head_size);
3135
case 1 : return ::LIBIMP::round_up<std::size_t>(s, 128);
3236
case 2 : return ::LIBIMP::round_up<std::size_t>(s, 1024);
3337
case 3 : return ::LIBIMP::round_up<std::size_t>(s, 8192);
34-
default: return 0;
38+
default: return (std::numeric_limits<std::size_t>::max)();
3539
}
3640
}
3741

3842
template <typename T>
3943
constexpr inline std::size_t regular_sizeof() noexcept {
40-
return regular_sizeof(sizeof(T));
44+
return regular_sizeof(regular_head_size + sizeof(T));
45+
}
46+
47+
class block_pool_like {
48+
public:
49+
virtual ~block_pool_like() noexcept = default;
50+
virtual void *allocate() noexcept = 0;
51+
virtual void deallocate(void *p) noexcept = 0;
52+
};
53+
54+
inline std::unordered_map<std::size_t, block_pool_like *> &get_block_pool_map() noexcept {
55+
thread_local std::unordered_map<std::size_t, block_pool_like *> instances;
56+
return instances;
4157
}
4258

4359
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
44-
class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion> {
60+
class block_pool_resource;
61+
62+
template <>
63+
class block_pool_resource<0, 0> : public block_pool<0, 0>
64+
, public block_pool_like {
65+
66+
void *allocate() noexcept override {
67+
return nullptr;
68+
}
69+
4570
public:
46-
static block_pool_resource *get() noexcept {
47-
thread_local block_pool_resource instance;
48-
return &instance;
71+
void deallocate(void *p) noexcept override {
72+
block_pool<0, 0>::deallocate(p);
73+
}
74+
};
75+
76+
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
77+
class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion>
78+
, public block_pool_like {
79+
80+
using base_t = block_pool<BlockSize, BlockPoolExpansion>;
81+
82+
void *allocate() noexcept override {
83+
return base_t::allocate();
4984
}
85+
86+
void deallocate(void *p) noexcept override {
87+
base_t::deallocate(p);
88+
}
89+
90+
public:
91+
static block_pool_resource *get() noexcept;
92+
93+
using base_t::base_t;
94+
5095
void *allocate(std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
51-
return block_pool<BlockSize, BlockPoolExpansion>::allocate();
96+
void *p = base_t::allocate();
97+
p = ::LIBIMP::construct<std::size_t>(p, BlockSize);
98+
return reinterpret_cast<::LIBIMP::byte *>(p) + regular_head_size;
5299
}
100+
53101
void deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept {
54-
block_pool<BlockSize, BlockPoolExpansion>::deallocate(p);
102+
p = reinterpret_cast<::LIBIMP::byte *>(p) - regular_head_size;
103+
auto r_size = *static_cast<std::size_t *>(p);
104+
if (r_size <= BlockSize) {
105+
base_t::deallocate(p);
106+
return;
107+
}
108+
auto &map = get_block_pool_map();
109+
auto it = map.find(r_size);
110+
if ((it == map.end()) || (it->second == nullptr)) LIBIMP_TRY {
111+
// If the corresponding memory resource cannot be found,
112+
// create a temporary general-purpose block pool to deallocate memory.
113+
it = map.emplace(r_size, new block_pool_resource<0, 0>).first;
114+
} LIBIMP_CATCH(...) {
115+
// If the memory resource cannot be created,
116+
// store the pointer directly to avoid leakage.
117+
base_t::deallocate(p);
118+
return;
119+
}
120+
it->second->deallocate(p);
55121
}
56122
};
57123

124+
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
125+
auto block_pool_resource<BlockSize, BlockPoolExpansion>::get() noexcept
126+
-> block_pool_resource<BlockSize, BlockPoolExpansion> * {
127+
auto &map = get_block_pool_map();
128+
thread_local block_pool_resource *pi = nullptr;
129+
if (pi != nullptr) {
130+
return pi;
131+
}
132+
auto it = map.find(BlockSize);
133+
if ((it != map.end()) && (it->second != nullptr)) {
134+
auto *bp = static_cast <block_pool<0, 0> *>(
135+
dynamic_cast<block_pool_resource<0, 0> *>(it->second));
136+
if (bp == nullptr) {
137+
return nullptr;
138+
}
139+
thread_local block_pool_resource instance(std::move(*bp));
140+
delete static_cast<block_pool_resource<0, 0> *>(bp);
141+
pi = &instance;
142+
} else {
143+
thread_local block_pool_resource instance;
144+
pi = &instance;
145+
}
146+
LIBIMP_TRY {
147+
map.emplace(BlockSize, pi);
148+
} LIBIMP_CATCH(...) {
149+
return nullptr;
150+
}
151+
return pi;
152+
}
153+
58154
template <std::size_t N, std::size_t L = regular_level(N)>
59155
class regular_resource : public new_delete_resource {};
60156

@@ -71,16 +167,19 @@ template <std::size_t N>
71167
class regular_resource<N, 3> : public block_pool_resource<N, 64> {};
72168

73169
template <typename T, typename... A>
74-
T *new_(A &&... args) noexcept {
170+
T *new$(A &&... args) noexcept {
75171
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
172+
if (mem_res == nullptr) return nullptr;
76173
return ::LIBIMP::construct<T>(mem_res->allocate(sizeof(T), alignof(T)), std::forward<A>(args)...);
77174
}
78175

79176
template <typename T>
80-
void delete_(T *p) noexcept {
177+
void delete$(T *p) noexcept {
81178
if (p == nullptr) return;
179+
::LIBIMP::destroy(p);
82180
auto *mem_res = regular_resource<regular_sizeof<T>()>::get();
83-
mem_res->deallocate(::LIBIMP::destroy(p), sizeof(T), alignof(T));
181+
if (mem_res == nullptr) return;
182+
mem_res->deallocate(p, sizeof(T), alignof(T));
84183
}
85184

86185
LIBPMR_NAMESPACE_END_

0 commit comments

Comments
 (0)