libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
stack_pool.h
Go to the documentation of this file.
1// Copyright 2023 owent
2
3#pragma once
4
5#include <libcopp/utils/config/libcopp_build_features.h>
10
13
14// clang-format off
15#include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
16// clang-format on
17#include <assert.h>
18#include <cstring>
19#include <list>
20#include <memory>
21// clang-format off
22#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
23// clang-format on
24
25LIBCOPP_COPP_NAMESPACE_BEGIN
26template <typename TAlloc>
27class LIBCOPP_COPP_API_HEAD_ONLY stack_pool {
28 public:
29 using allocator_type = TAlloc;
30 using ptr_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<stack_pool<TAlloc> >;
31
32 // Compability with libcopp-1.x
34 using ptr_t = ptr_type;
35
42
53
54 private:
56
57 stack_pool() = delete;
58 stack_pool(const stack_pool &) = delete;
59
60 public:
61 static ptr_type create() {
62 return LIBCOPP_COPP_NAMESPACE_ID::memory::default_make_strong<stack_pool>(constructor_delegator());
63 }
64
66 memset(&limits_, 0, sizeof(limits_));
67 memset(&conf_, 0, sizeof(conf_));
68 conf_.stack_size = LIBCOPP_COPP_NAMESPACE_ID::stack_traits::default_size();
69 conf_.auto_gc = true;
70 }
71 ~stack_pool() { clear(); }
72
73 inline const limit_t &get_limit() const { return limits_; }
74
75 // configure
76 inline allocator_type &get_origin_allocator() LIBCOPP_MACRO_NOEXCEPT { return alloc_; }
77 inline const allocator_type &get_origin_allocator() const LIBCOPP_MACRO_NOEXCEPT { return alloc_; }
78
79 size_t set_stack_size(size_t sz) {
80 if (sz <= LIBCOPP_COPP_NAMESPACE_ID::stack_traits::minimum_size()) {
81 sz = LIBCOPP_COPP_NAMESPACE_ID::stack_traits::minimum_size();
82 } else {
83 sz = LIBCOPP_COPP_NAMESPACE_ID::stack_traits::round_to_page_size(sz);
84 }
85
86 if (sz != conf_.stack_size) {
87 clear();
88 }
89
90 return conf_.stack_size = sz;
91 }
92 size_t get_stack_size() const { return conf_.stack_size; }
93 size_t get_stack_size_offset() const { return conf_.stack_offset; }
94
95 inline void set_max_stack_size(size_t sz) LIBCOPP_MACRO_NOEXCEPT { conf_.max_stack_size = sz; }
96 inline size_t get_max_stack_size() const LIBCOPP_MACRO_NOEXCEPT { return conf_.max_stack_size; }
97 inline void set_max_stack_number(size_t sz) LIBCOPP_MACRO_NOEXCEPT { conf_.max_stack_number = sz; }
98 inline size_t get_max_stack_number() const LIBCOPP_MACRO_NOEXCEPT { return conf_.max_stack_number; }
99
100 inline void set_min_stack_size(size_t sz) LIBCOPP_MACRO_NOEXCEPT { conf_.min_stack_size = sz; }
101 inline size_t get_min_stack_size() const LIBCOPP_MACRO_NOEXCEPT { return conf_.min_stack_size; }
102 inline void set_min_stack_number(size_t sz) LIBCOPP_MACRO_NOEXCEPT { conf_.min_stack_number = sz; }
103 inline size_t get_min_stack_number() const LIBCOPP_MACRO_NOEXCEPT { return conf_.min_stack_number; }
104
105 inline void set_auto_gc(bool v) LIBCOPP_MACRO_NOEXCEPT { conf_.auto_gc = v; }
106 inline bool is_auto_gc() const LIBCOPP_MACRO_NOEXCEPT { return conf_.auto_gc; }
107
108 inline void set_gc_once_number(size_t v) LIBCOPP_MACRO_NOEXCEPT { conf_.gc_number = v; }
109 inline size_t get_gc_once_number() const LIBCOPP_MACRO_NOEXCEPT { return conf_.gc_number; }
110
111 // actions
112
119 void allocate(stack_context &ctx) LIBCOPP_MACRO_NOEXCEPT {
120#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
121 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
122 action_lock_);
123#endif
124 // check limit
125 if (0 != conf_.max_stack_number && limits_.used_stack_number >= conf_.max_stack_number) {
126 ctx.sp = nullptr;
127 ctx.size = 0;
128 return;
129 }
130
131 if (0 != conf_.max_stack_size && limits_.used_stack_size + conf_.stack_size > conf_.max_stack_size) {
132 ctx.sp = nullptr;
133 ctx.size = 0;
134 return;
135 }
136
137 // get from pool, in order to max reuse cache, we use FILO to allocate stack
138 if (!free_list_.empty()) {
139 typename std::list<stack_context>::reverse_iterator iter = free_list_.rbegin();
140 assert(iter != free_list_.rend());
141
142 // free limit
143 if LIBCOPP_UTIL_LIKELY_CONDITION (limits_.free_stack_number > 0) {
144 --limits_.free_stack_number;
145 } else {
146 limits_.free_stack_number = free_list_.size() - 1;
147 }
148
149 if LIBCOPP_UTIL_LIKELY_CONDITION (limits_.free_stack_size >= (*iter).size) {
150 limits_.free_stack_size -= (*iter).size;
151 } else {
152 limits_.free_stack_size = 0;
153 }
154
155 // make sure the stack must be greater or equal than configure after reset
156 if LIBCOPP_UTIL_LIKELY_CONDITION (iter->size >= conf_.stack_size) {
157 ctx = *iter;
158 free_list_.pop_back();
159
160 // used limit
161 ++limits_.used_stack_number;
162 limits_.used_stack_size += ctx.size;
163 return;
164 } else {
165 // just pop cache
166 free_list_.pop_back();
167 }
168 }
169
170 // get from origin allocator
171 alloc_.allocate(ctx, conf_.stack_size);
172 if (nullptr != ctx.sp && ctx.size > 0) {
173 // used limit
174 ++limits_.used_stack_number;
175 limits_.used_stack_size += ctx.size;
176
177 conf_.stack_offset = ctx.size - conf_.stack_size;
178 }
179 }
180
185 void deallocate(stack_context &ctx) LIBCOPP_MACRO_NOEXCEPT {
186 assert(ctx.sp && ctx.size > 0);
187 do {
188#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
189 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
190 action_lock_);
191#endif
192 // check ctx
193 if (ctx.sp == nullptr || 0 == ctx.size) {
194 break;
195 }
196
197 // limits
198 if LIBCOPP_UTIL_LIKELY_CONDITION (limits_.used_stack_size >= ctx.size) {
199 limits_.used_stack_size -= ctx.size;
200 } else {
201 limits_.used_stack_size = 0;
202 }
203
204 if LIBCOPP_UTIL_LIKELY_CONDITION (limits_.used_stack_number > 0) {
205 --limits_.used_stack_number;
206 }
207
208 // check size
209 if (ctx.size != conf_.stack_size + conf_.stack_offset) {
210 alloc_.deallocate(ctx);
211 break;
212 }
213
214 // push to free list
215 free_list_.push_back(ctx);
216
217 // limits
218 ++limits_.free_stack_number;
219 limits_.free_stack_size += ctx.size;
220 } while (false);
221
222 // check GC
223 if (conf_.auto_gc) {
224 gc();
225 }
226 }
227
228 size_t gc() {
229 size_t ret = 0;
230 // gc only if free stacks is greater than used
231 if (limits_.used_stack_size >= limits_.free_stack_size && limits_.used_stack_number >= limits_.free_stack_number) {
232 return ret;
233 }
234
235 // gc when stack is too large
236 if (0 != conf_.min_stack_size || 0 != conf_.min_stack_number) {
237 bool min_stack_size =
238 conf_.min_stack_size == 0 || limits_.used_stack_size + limits_.free_stack_size <= conf_.min_stack_size;
239 bool min_stack_number = conf_.min_stack_number == 0 ||
240 limits_.free_stack_number + limits_.used_stack_number <= conf_.min_stack_number;
241 if (min_stack_size && min_stack_number) {
242 return ret;
243 }
244 }
245
246#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
247 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
248 action_lock_);
249#endif
250
251 size_t keep_size = limits_.free_stack_size >> 1;
252 size_t keep_number = limits_.free_stack_number >> 1;
253 size_t left_gc = conf_.gc_number;
254 while (limits_.free_stack_size > keep_size || limits_.free_stack_number > keep_number) {
255 if (free_list_.empty()) {
256 limits_.free_stack_size = 0;
257 limits_.free_stack_number = 0;
258 break;
259 }
260
261 typename std::list<stack_context>::iterator iter = free_list_.begin();
262 assert(iter != free_list_.end());
263
264 if LIBCOPP_UTIL_LIKELY_CONDITION (limits_.free_stack_number > 0) {
265 --limits_.free_stack_number;
266 } else {
267 limits_.free_stack_number = free_list_.size() - 1;
268 }
269
270 if LIBCOPP_UTIL_LIKELY_CONDITION (limits_.free_stack_size >= (*iter).size) {
271 limits_.free_stack_size -= (*iter).size;
272 } else {
273 limits_.free_stack_size = 0;
274 }
275
276 alloc_.deallocate(*iter);
277 free_list_.pop_front();
278 ++ret;
279
280 // gc max stacks once
281 if (0 != left_gc) {
282 --left_gc;
283 if (0 == left_gc) {
284 break;
285 }
286 }
287 }
288
289 LIBCOPP_UTIL_LOCK_ATOMIC_THREAD_FENCE(LIBCOPP_COPP_NAMESPACE_ID::util::lock::memory_order_release);
290
291 return ret;
292 }
293
294 void clear() {
295#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
296 LIBCOPP_COPP_NAMESPACE_ID::util::lock::lock_holder<LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock> lock_guard(
297 action_lock_);
298#endif
299
300 limits_.free_stack_size = 0;
301 limits_.free_stack_number = 0;
302
303 for (typename std::list<stack_context>::iterator iter = free_list_.begin(); iter != free_list_.end(); ++iter) {
304 alloc_.deallocate(*iter);
305 }
306
307 LIBCOPP_UTIL_LOCK_ATOMIC_THREAD_FENCE(LIBCOPP_COPP_NAMESPACE_ID::util::lock::memory_order_release);
308 }
309
310 private:
314#if LIBCOPP_MACRO_ENABLE_MULTI_THREAD
315 LIBCOPP_COPP_NAMESPACE_ID::util::lock::spin_lock action_lock_;
316#endif
317 std::list<stack_context> free_list_;
318};
319LIBCOPP_COPP_NAMESPACE_END
#define LIBCOPP_UTIL_LOCK_ATOMIC_THREAD_FENCE(x)
size_t get_stack_size_offset() const
Definition stack_pool.h:93
size_t get_min_stack_number() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:103
void set_gc_once_number(size_t v) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:108
void set_min_stack_size(size_t sz) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:100
allocator_type allocator_t
Definition stack_pool.h:33
allocator_type alloc_
Definition stack_pool.h:313
LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr< stack_pool< TAlloc > > ptr_type
Definition stack_pool.h:30
const allocator_type & get_origin_allocator() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:77
size_t get_stack_size() const
Definition stack_pool.h:92
void set_min_stack_number(size_t sz) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:102
ptr_type ptr_t
Definition stack_pool.h:34
void set_auto_gc(bool v) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:105
size_t get_max_stack_size() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:96
const limit_t & get_limit() const
Definition stack_pool.h:73
size_t get_gc_once_number() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:109
size_t get_max_stack_number() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:98
size_t set_stack_size(size_t sz)
Definition stack_pool.h:79
limit_t limits_
Definition stack_pool.h:311
bool is_auto_gc() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:106
stack_pool(constructor_delegator)
Definition stack_pool.h:65
size_t gc()
Definition stack_pool.h:228
std::list< stack_context > free_list_
Definition stack_pool.h:317
configure_t conf_
Definition stack_pool.h:312
void allocate(stack_context &ctx) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:119
stack_pool()=delete
void set_max_stack_number(size_t sz) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:97
stack_pool(const stack_pool &)=delete
allocator_type & get_origin_allocator() LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:76
TAlloc allocator_type
Definition stack_pool.h:29
void clear()
Definition stack_pool.h:294
static ptr_type create()
Definition stack_pool.h:61
size_t get_min_stack_size() const LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:101
void set_max_stack_size(size_t sz) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:95
void deallocate(stack_context &ctx) LIBCOPP_MACRO_NOEXCEPT
Definition stack_pool.h:185
#define LIBCOPP_UTIL_LIKELY_CONDITION(__C)
锁管理器 Licensed under the MIT licenses.
自旋锁 Licensed under the MIT licenses.