14
14
#include " libimp/uninitialized.h"
15
15
#include " libimp/byte.h"
16
16
#include " libimp/detect_plat.h"
17
+ #include " libimp/export.h"
17
18
18
19
#include " libpmr/def.h"
19
20
#include " libpmr/block_pool.h"
20
21
#include " libpmr/memory_resource.h"
21
22
22
23
LIBPMR_NAMESPACE_BEG_
23
24
25
+ // / \brief Select the incremental level based on the size.
24
26
constexpr inline std::size_t regular_level (std::size_t s) noexcept {
25
27
return (s <= 128 ) ? 0 :
26
28
(s <= 1024 ) ? 1 :
27
29
(s <= 8192 ) ? 2 :
28
30
(s <= 65536 ) ? 3 : 4 ;
29
31
}
30
32
33
+ // / \brief Calculates the appropriate memory block size based on the increment level and size.
31
34
constexpr inline std::size_t regular_sizeof_impl (std::size_t l, std::size_t s) noexcept {
32
35
return (l == 0 ) ? std::max<std::size_t >(::LIBIMP::round_up<std::size_t >(s, 8 ), regular_head_size) :
33
36
(l == 1 ) ? ::LIBIMP::round_up<std::size_t >(s, 128 ) :
34
37
(l == 2 ) ? ::LIBIMP::round_up<std::size_t >(s, 1024 ) :
35
38
(l == 3 ) ? ::LIBIMP::round_up<std::size_t >(s, 8192 ) : (std::numeric_limits<std::size_t >::max)();
36
39
}
37
40
41
+ // / \brief Calculates the appropriate memory block size based on the size.
38
42
constexpr inline std::size_t regular_sizeof (std::size_t s) noexcept {
39
43
return regular_sizeof_impl (regular_level (s), s);
40
44
}
41
45
46
+ // / \brief Calculates the appropriate memory block size based on the specific type.
42
47
template <typename T>
43
48
constexpr inline std::size_t regular_sizeof () noexcept {
44
49
return regular_sizeof (regular_head_size + sizeof (T));
45
50
}
46
51
47
- class block_pool_like {
52
+ // / \brief Defines the memory block collector interface.
53
+ class LIBIMP_EXPORT block_collector {
48
54
public:
49
- virtual ~block_pool_like () noexcept = default ;
50
- virtual void *allocate () noexcept = 0;
55
+ virtual ~block_collector () noexcept = default ;
51
56
virtual void deallocate (void *p) noexcept = 0;
52
57
};
53
58
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;
57
- }
59
+ // / \brief Gets all block pools of the thread cache.
60
+ LIBIMP_EXPORT auto get_thread_block_pool_map () noexcept
61
+ -> std::unordered_map<std::size_t, block_collector *> &;
58
62
63
+ // / \brief Defines block pool memory resource based on block pool.
59
64
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
60
65
class block_pool_resource ;
61
66
67
+ // / \brief Memory block collector of unknown size.
68
+ // / \note This memory resource is only used to temporarily collect memory blocks
69
+ // / that cannot find a suitable block pool memory resource.
62
70
template <>
63
71
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
-
72
+ , public block_collector {
70
73
public:
71
74
void deallocate (void *p) noexcept override {
72
75
block_pool<0 , 0 >::deallocate (p);
73
76
}
74
77
};
75
78
79
+ // / \brief A block pool memory resource for a block of memory of a specific size.
76
80
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
77
81
class block_pool_resource : public block_pool <BlockSize, BlockPoolExpansion>
78
- , public block_pool_like {
82
+ , public block_collector {
79
83
80
84
using base_t = block_pool<BlockSize, BlockPoolExpansion>;
81
85
82
- void *allocate () noexcept override {
83
- return base_t::allocate ();
84
- }
85
-
86
86
void deallocate (void *p) noexcept override {
87
87
base_t::deallocate (p);
88
88
}
@@ -105,7 +105,9 @@ class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion>
105
105
base_t::deallocate (p);
106
106
return ;
107
107
}
108
- auto &map = get_block_pool_map ();
108
+ // When the actual size exceeds the current memory block size,
109
+ // try to find a suitable pool among all memory block pools for this thread.
110
+ auto &map = get_thread_block_pool_map ();
109
111
auto it = map.find (r_size);
110
112
if ((it == map.end ()) || (it->second == nullptr )) {
111
113
block_pool_resource<0 , 0 > *bp = nullptr ;
@@ -128,62 +130,70 @@ class block_pool_resource : public block_pool<BlockSize, BlockPoolExpansion>
128
130
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
129
131
auto block_pool_resource<BlockSize, BlockPoolExpansion>::get() noexcept
130
132
-> block_pool_resource<BlockSize, BlockPoolExpansion> * {
131
- auto &map = get_block_pool_map ();
132
133
thread_local block_pool_resource *pi = nullptr ;
133
134
if (pi != nullptr ) {
134
135
return pi;
135
136
}
137
+ // Create a new block pool resource for this thread.
138
+ auto &map = get_thread_block_pool_map ();
136
139
auto it = map.find (BlockSize);
137
140
if ((it != map.end ()) && (it->second != nullptr )) {
141
+ // If there are existing block pool resources in the thread cache,
142
+ // a new block pool resource is constructed based on it and the cache is updated.
138
143
auto *bp = static_cast <block_pool<0 , 0 > *>(
139
144
dynamic_cast <block_pool_resource<0 , 0 > *>(it->second ));
140
145
if (bp == nullptr ) {
141
146
return nullptr ;
142
147
}
143
148
thread_local block_pool_resource instance (std::move (*bp));
144
149
delete static_cast <block_pool_resource<0 , 0 > *>(bp);
145
- pi = &instance;
150
+ it->second = pi = &instance;
151
+ return pi;
146
152
} else {
153
+ // If there are no existing block pool resources in the thread cache,
154
+ // the thread local storage instance is constructed and the pointer is cached.
147
155
thread_local block_pool_resource instance;
148
- pi = &instance;
149
- }
150
- LIBIMP_TRY {
151
- map. emplace (BlockSize, pi);
152
- } LIBIMP_CATCH (...) {
153
- return nullptr ;
156
+ LIBIMP_TRY {
157
+ map. emplace (BlockSize, pi = &instance);
158
+ return pi;
159
+ } LIBIMP_CATCH (...) {
160
+ return nullptr ;
161
+ }
154
162
}
155
- return pi;
156
163
}
157
164
165
+ // / \brief Match the appropriate memory block resources
166
+ // / according to the size of the specification.
158
167
template <std::size_t N, std::size_t L = regular_level(N)>
159
168
class regular_resource : public new_delete_resource {};
160
169
161
- template <std::size_t N>
162
- class regular_resource <N, 0 > : public block_pool_resource<N, 512 > {};
163
-
164
- template <std::size_t N>
165
- class regular_resource <N, 1 > : public block_pool_resource<N, 256 > {};
166
-
167
- template <std::size_t N>
168
- class regular_resource <N, 2 > : public block_pool_resource<N, 128 > {};
169
-
170
- template <std::size_t N>
171
- class regular_resource <N, 3 > : public block_pool_resource<N, 64 > {};
170
+ // / \brief Different increment levels match different chunk sizes.
171
+ // / 512 means that 512 consecutive memory blocks are allocated at a time, and the block size is N.
172
+ template <std::size_t N> class regular_resource <N, 0 > : public block_pool_resource<N, 512 > {};
173
+ template <std::size_t N> class regular_resource <N, 1 > : public block_pool_resource<N, 256 > {};
174
+ template <std::size_t N> class regular_resource <N, 2 > : public block_pool_resource<N, 128 > {};
175
+ template <std::size_t N> class regular_resource <N, 3 > : public block_pool_resource<N, 64 > {};
172
176
177
+ // / \brief Creates an object based on the specified type and parameters with block pool resource.
178
+ // / \note This function is thread-safe.
173
179
template <typename T, typename ... A>
174
180
T *new $(A &&... args) noexcept {
175
181
auto *mem_res = regular_resource<regular_sizeof<T>()>::get ();
176
182
if (mem_res == nullptr ) return nullptr ;
177
183
return ::LIBIMP::construct<T>(mem_res->allocate (sizeof (T), alignof (T)), std::forward<A>(args)...);
178
184
}
179
185
186
+ // / \brief Destroys object previously allocated by the `new$` and releases obtained memory area.
187
+ // / \note This function is thread-safe. If the pointer type passed in is different from `new$`,
188
+ // / additional performance penalties may be incurred.
180
189
template <typename T>
181
190
void delete $(T *p) noexcept {
182
191
if (p == nullptr ) return ;
183
192
::LIBIMP::destroy (p);
184
193
auto *mem_res = regular_resource<regular_sizeof<T>()>::get ();
185
194
if (mem_res == nullptr ) return ;
186
195
#if defined(LIBIMP_CC_MSVC_2015)
196
+ // `alignof` of vs2015 requires that type must be able to be instantiated.
187
197
mem_res->deallocate (p, sizeof (T));
188
198
#else
189
199
mem_res->deallocate (p, sizeof (T), alignof (T));
0 commit comments