Sparkle 0.0.1
Loading...
Searching...
No Matches
spk_pool.hpp
1#pragma once
2
3#ifndef NOMINMAX
4# define NOMINMAX
5#endif
6
7#include <functional>
8#include <limits>
9#include <memory>
10#include <mutex>
11#include <utility>
12#include <vector>
13
14namespace spk
15{
27 template <typename TType>
28 class Pool
29 {
30 public:
32 using Deleter = std::function<void(TType *)>;
34 using Object = std::unique_ptr<TType, Deleter>;
35
36 private:
37 struct Data
38 {
39 std::mutex mutex;
40 std::vector<std::unique_ptr<TType>> availableElements;
41 size_t maximumSize = std::numeric_limits<size_t>::max();
42
43 bool closed = false;
44 };
45
46 std::shared_ptr<Data> _data;
47
48 public:
52 Pool() :
53 _data(std::make_shared<Data>())
54 {
55 }
56
61 {
62 std::scoped_lock<std::mutex> lock(_data->mutex);
63 _data->closed = true;
64 }
65
66 Pool(const Pool &) = delete;
67 Pool &operator=(const Pool &) = delete;
68
73 void setMaximumAllocationSize(size_t p_size)
74 {
75 std::scoped_lock<std::mutex> lock(_data->mutex);
76
77 if (_data->closed == true)
78 {
79 throw std::runtime_error("Can't edit the maximulm allocated size of a closed pool");
80 }
81
82 _data->maximumSize = p_size;
83
84 while (_data->availableElements.size() > _data->maximumSize)
85 {
86 _data->availableElements.pop_back();
87 }
88 }
89
94 size_t size() const
95 {
96 std::scoped_lock<std::mutex> lock(_data->mutex);
97 return _data->availableElements.size();
98 }
99
100 template <typename... TArgs>
105 void allocate(TArgs &&...p_args)
106 {
107 std::scoped_lock<std::mutex> lock(_data->mutex);
108
109 if (_data->closed == true)
110 {
111 throw std::runtime_error("Can't allocate a new object in closed pool");
112 }
113
114 _data->availableElements.emplace_back(std::make_unique<TType>(std::forward<TArgs>(p_args)...));
115 }
116
117 template <typename... TArgs>
123 void resize(size_t p_newSize, TArgs &&...p_args)
124 {
125 std::scoped_lock<std::mutex> lock(_data->mutex);
126
127 if (_data->closed == true)
128 {
129 throw std::runtime_error("Can't resize a closed pool");
130 }
131
132 while (_data->availableElements.size() > p_newSize)
133 {
134 _data->availableElements.pop_back();
135 }
136
137 while (_data->availableElements.size() < p_newSize)
138 {
139 _data->availableElements.emplace_back(std::make_unique<TType>(std::forward<TArgs>(p_args)...));
140 }
141 }
142
146 void release()
147 {
148 std::scoped_lock<std::mutex> lock(_data->mutex);
149
150 if (_data->closed == true)
151 {
152 throw std::runtime_error("Can't release a closed pool");
153 }
154
155 _data->availableElements.clear();
156 }
157
158 template <typename... TArgs>
164 Object obtain(TArgs &&...p_args)
165 {
166 {
167 std::scoped_lock<std::mutex> lock(_data->mutex);
168
169 if (_data->closed == true)
170 {
171 throw std::runtime_error("Can't obtain object from a closed pool");
172 }
173 }
174
175 TType *rawPtr = nullptr;
176
177 {
178 std::scoped_lock<std::mutex> lock(_data->mutex);
179
180 if (_data->availableElements.empty())
181 {
182 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) << Will be passed to a unique_ptr with custom destructor later on
183 rawPtr = new TType(std::forward<TArgs>(p_args)...);
184 }
185 else
186 {
187 rawPtr = _data->availableElements.back().release();
188 _data->availableElements.pop_back();
189
190 rawPtr->~TType();
191 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) << Will be passed to a unique_ptr with custom destructor later on
192 new (rawPtr) TType(std::forward<TArgs>(p_args)...);
193 }
194 }
195
196 std::weak_ptr<Data> weakData = _data;
197
198 auto deleter = [weakData](TType *p_ptr) {
199 if (auto weakDataContent = weakData.lock())
200 {
201 std::scoped_lock<std::mutex> lock(weakDataContent->mutex);
202
203 if (weakDataContent->closed == false && weakDataContent->availableElements.size() < weakDataContent->maximumSize)
204 {
205 weakDataContent->availableElements.emplace_back(p_ptr);
206 return;
207 }
208 }
209
210 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) << Release of the pointer data uppon "real" destruction
211 delete p_ptr;
212 };
213
214 return Object(rawPtr, deleter);
215 }
216 };
217}
Thread-safe object pool that reuses allocations with custom destruction.
Definition spk_pool.hpp:29
void resize(size_t p_newSize, TArgs &&...p_args)
Adjusts the pool size by constructing or discarding objects.
Definition spk_pool.hpp:123
Pool()
Constructs an empty pool with unlimited capacity.
Definition spk_pool.hpp:52
std::unique_ptr< TType, Deleter > Object
Unique pointer type that recycles its payload back into the pool.
Definition spk_pool.hpp:34
void setMaximumAllocationSize(size_t p_size)
Limits the number of recyclable objects stored by the pool.
Definition spk_pool.hpp:73
~Pool()
Marks the pool as closed and prevents further allocations.
Definition spk_pool.hpp:60
void allocate(TArgs &&...p_args)
Pre-allocates a new instance and pushes it into the available set.
Definition spk_pool.hpp:105
std::function< void(TType *)> Deleter
Custom deleter used to return instances to the pool.
Definition spk_pool.hpp:32
void release()
Clears all cached objects without closing the pool.
Definition spk_pool.hpp:146
size_t size() const
Retrieves the number of available objects stored in the pool.
Definition spk_pool.hpp:94
Object obtain(TArgs &&...p_args)
Provides an object from the pool, constructing one if necessary.
Definition spk_pool.hpp:164
STL namespace.