Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
object_pool.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
13#pragma once
14
16#include <memory>
17#include <vector>
18#include <mutex>
19#include <atomic>
20#include <queue>
21
22namespace kcenon::logger::memory {
23
28template<typename T>
30public:
34 struct config {
35 size_t initial_size{100};
36 size_t max_size{10000};
37 bool allow_growth{true};
38
39 config() = default;
40 };
41
46 explicit object_pool(const config& cfg = config{})
47 : config_(cfg), pool_size_(0) {
49 }
50
54 ~object_pool() = default;
55
60 std::unique_ptr<T> acquire() {
61 std::lock_guard<std::mutex> lock(mutex_);
62
63 if (!available_objects_.empty()) {
64 auto obj = std::move(available_objects_.front());
66 return obj;
67 }
68
69 // If pool is empty and growth is allowed, create new object
71 pool_size_.fetch_add(1);
72 return std::make_unique<T>();
73 }
74
75 // Create temporary object if pool limits reached
76 return std::make_unique<T>();
77 }
78
83 void release(std::unique_ptr<T> obj) {
84 if (!obj) {
85 return;
86 }
87
88 std::lock_guard<std::mutex> lock(mutex_);
89
90 // Only return to pool if not exceeding max size
91 if (available_objects_.size() < config_.max_size) {
92 available_objects_.push(std::move(obj));
93 }
94 // If exceeding max size, object will be destroyed automatically
95 }
96
100 struct statistics {
104 };
105
111 std::lock_guard<std::mutex> lock(mutex_);
112 statistics stats;
113 stats.total_size = pool_size_.load();
114 stats.available_count = available_objects_.size();
115 stats.in_use_count = pool_size_.load() - available_objects_.size();
116 return stats;
117 }
118
122 void clear() {
123 std::lock_guard<std::mutex> lock(mutex_);
124 while (!available_objects_.empty()) {
125 available_objects_.pop();
126 }
127 pool_size_.store(0);
128 }
129
130private:
135 std::lock_guard<std::mutex> lock(mutex_);
136
137 for (size_t i = 0; i < config_.initial_size; ++i) {
138 available_objects_.push(std::make_unique<T>());
139 }
140
142 }
143
145 mutable std::mutex mutex_;
146 std::queue<std::unique_ptr<T>> available_objects_;
147 std::atomic<size_t> pool_size_;
148};
149
168template<typename T>
170public:
174 struct config {
175 size_t global_max{10000};
176 size_t local_cache_size{16};
177 size_t initial_size{100};
178 bool allow_growth{true};
179
180 config() = default;
181 };
182
187 explicit thread_local_object_pool(const config& cfg = config{})
188 : config_(cfg)
189 , global_size_(0) {
191 }
192
197
205 std::unique_ptr<T> acquire() {
206 // Step 1: Check thread-local cache first (no lock!)
207 auto& local_cache = get_local_cache();
208
209 if (!local_cache.empty()) {
210 local_cache_hits_.fetch_add(1, std::memory_order_relaxed);
211 auto obj = std::move(local_cache.back());
212 local_cache.pop_back();
213 return obj;
214 }
215
216 // Step 2: Fetch batch from global pool (lock once)
217 {
218 std::lock_guard<std::mutex> lock(global_mutex_);
219
220 // Batch size: half of local cache size
221 size_t batch_size = std::min(
223 global_pool_.size()
224 );
225
226 for (size_t i = 0; i < batch_size; ++i) {
227 if (!global_pool_.empty()) {
228 local_cache.push_back(std::move(global_pool_.back()));
229 global_pool_.pop_back();
230 }
231 }
232 }
233
234 // Step 3: Try local cache again after batch fetch
235 if (!local_cache.empty()) {
236 global_pool_hits_.fetch_add(1, std::memory_order_relaxed);
237 auto obj = std::move(local_cache.back());
238 local_cache.pop_back();
239 return obj;
240 }
241
242 // Step 4: Create new object if pool is empty
243 if (config_.allow_growth && global_size_.load(std::memory_order_relaxed) < config_.global_max) {
244 global_size_.fetch_add(1, std::memory_order_relaxed);
245 new_allocations_.fetch_add(1, std::memory_order_relaxed);
246 return std::make_unique<T>();
247 }
248
249 // Fallback: create temporary object even if limit reached
250 // (same behavior as original object_pool)
251 return std::make_unique<T>();
252 }
253
261 void release(std::unique_ptr<T> obj) {
262 if (!obj) {
263 return;
264 }
265
266 // Step 1: Add to thread-local cache (no lock!)
267 auto& local_cache = get_local_cache();
268
269 if (local_cache.size() < config_.local_cache_size) {
270 local_cache.push_back(std::move(obj));
271 return;
272 }
273
274 // Step 2: Transfer batch to global pool when local cache is full
275 {
276 std::lock_guard<std::mutex> lock(global_mutex_);
277
278 // Move half of local cache to global
279 size_t transfer_count = config_.local_cache_size / 2;
280
281 for (size_t i = 0; i < transfer_count && !local_cache.empty(); ++i) {
282 if (global_pool_.size() < config_.global_max) {
283 global_pool_.push_back(std::move(local_cache.back()));
284 local_cache.pop_back();
285 }
286 }
287
288 // Add current object to global pool
289 if (global_pool_.size() < config_.global_max) {
290 global_pool_.push_back(std::move(obj));
291 }
292 // If exceeding max, object will be destroyed automatically
293 }
294 }
295
306
312 std::lock_guard<std::mutex> lock(global_mutex_);
313 statistics stats;
314 stats.global_size = global_size_.load(std::memory_order_relaxed);
315 stats.global_pool_size = global_pool_.size();
316 stats.local_cache_hits = local_cache_hits_.load(std::memory_order_relaxed);
317 stats.global_pool_hits = global_pool_hits_.load(std::memory_order_relaxed);
318 stats.new_allocations = new_allocations_.load(std::memory_order_relaxed);
319 return stats;
320 }
321
326 void clear() {
327 std::lock_guard<std::mutex> lock(global_mutex_);
328 global_pool_.clear();
329 global_size_.store(0, std::memory_order_relaxed);
330 local_cache_hits_.store(0, std::memory_order_relaxed);
331 global_pool_hits_.store(0, std::memory_order_relaxed);
332 new_allocations_.store(0, std::memory_order_relaxed);
333 }
334
335private:
340 static std::vector<std::unique_ptr<T>>& get_local_cache() {
341 thread_local std::vector<std::unique_ptr<T>> cache;
342 return cache;
343 }
344
349 std::lock_guard<std::mutex> lock(global_mutex_);
350
351 for (size_t i = 0; i < config_.initial_size; ++i) {
352 global_pool_.push_back(std::make_unique<T>());
353 }
354
355 global_size_.store(config_.initial_size, std::memory_order_relaxed);
356 }
357
358 // Configuration
360
361 // Global pool
362 std::vector<std::unique_ptr<T>> global_pool_;
363 mutable std::mutex global_mutex_;
364
365 // Statistics (atomic for lock-free reads)
366 std::atomic<size_t> global_size_{0};
367 std::atomic<size_t> local_cache_hits_{0};
368 std::atomic<size_t> global_pool_hits_{0};
369 std::atomic<size_t> new_allocations_{0};
370};
371
372} // namespace kcenon::logger::memory
Thread-safe object pool for high-performance memory management.
Definition object_pool.h:29
std::mutex mutex_
Thread safety mutex.
config config_
Pool configuration.
statistics get_statistics() const
Get pool statistics.
object_pool(const config &cfg=config{})
Construct object pool with configuration.
Definition object_pool.h:46
void release(std::unique_ptr< T > obj)
Return an object to the pool.
Definition object_pool.h:83
void initialize_pool()
Initialize pool with initial objects.
std::atomic< size_t > pool_size_
Current pool size.
~object_pool()=default
Destructor.
void clear()
Clear all objects from pool.
std::unique_ptr< T > acquire()
Get an object from the pool.
Definition object_pool.h:60
std::queue< std::unique_ptr< T > > available_objects_
Available objects.
Thread-local cached object pool for improved multi-threaded performance.
static std::vector< std::unique_ptr< T > > & get_local_cache()
Get thread-local cache.
std::unique_ptr< T > acquire()
Get an object from the pool.
thread_local_object_pool(const config &cfg=config{})
Construct thread-local object pool with configuration.
statistics get_statistics() const
Get pool statistics.
void release(std::unique_ptr< T > obj)
Return an object to the pool.
void clear()
Clear all objects from pool.
std::vector< std::unique_ptr< T > > global_pool_
void initialize_pool()
Initialize pool with initial objects.
Error codes specific to the logger system.
Configuration for object pool.
Definition object_pool.h:34
bool allow_growth
Allow pool to grow beyond initial size.
Definition object_pool.h:37
Configuration for thread-local object pool.
bool allow_growth
Allow pool to grow beyond initial size.
Statistics for monitoring pool performance.
size_t global_pool_size
Objects currently in global pool.
size_t local_cache_hits
Acquire from thread-local cache (fast path)
size_t global_pool_hits
Acquire from global pool (batch fetch)