1 #ifndef PPX_BASE_MEMORY_POOL_H_
2 #define PPX_BASE_MEMORY_POOL_H_
3
4 #include <climits>
5 #include <cstddef>
6 #include <mutex>
7
8 namespace ppx {
9 namespace base {
10 template <typename T, size_t BlockSize = 4096, bool ZeroOnDeallocate = true>
11 class MemoryPool {
12 public:
13 /* Member types */
14 typedef T value_type;
15 typedef T* pointer;
16 typedef T& reference;
17 typedef const T* const_pointer;
18 typedef const T& const_reference;
19 typedef size_t size_type;
20 typedef ptrdiff_t difference_type;
21 typedef std::false_type propagate_on_container_copy_assignment;
22 typedef std::true_type propagate_on_container_move_assignment;
23 typedef std::true_type propagate_on_container_swap;
24
25 template <typename U> struct rebind {
26 typedef MemoryPool<U> other;
27 };
28
29 /* Member functions */
30 MemoryPool() noexcept;
31 MemoryPool(const MemoryPool& memoryPool) noexcept;
32 MemoryPool(MemoryPool&& memoryPool) noexcept;
33 template <class U> MemoryPool(const MemoryPool<U>& memoryPool) noexcept;
34
35 ~MemoryPool() noexcept;
36
37 MemoryPool& operator=(const MemoryPool& memoryPool) = delete;
38 MemoryPool& operator=(MemoryPool&& memoryPool) noexcept;
39
40 pointer address(reference x) const noexcept;
41 const_pointer address(const_reference x) const noexcept;
42
43 // Can only allocate one object at a time. n and hint are ignored
44 pointer allocate(size_type n = 1, const_pointer hint = 0);
45 void deallocate(pointer p, size_type n = 1);
46
47 size_type max_size() const noexcept;
48
49 template <class U, class... Args> void construct(U* p, Args&&... args);
50 template <class U> void destroy(U* p);
51
52 template <class... Args> pointer newElement(Args&&... args);
53 void deleteElement(pointer p);
54
55 private:
56 struct Element_ {
57 Element_* pre;
58 Element_* next;
59 };
60
61 typedef char* data_pointer;
62 typedef Element_ element_type;
63 typedef Element_* element_pointer;
64
65 element_pointer data_element_;
66 element_pointer free_element_;
67
68 std::recursive_mutex m_;
69
70 size_type padPointer(data_pointer p, size_type align) const noexcept;
71 void allocateBlock();
72
73 static_assert(BlockSize >= 2 * sizeof(element_type), "BlockSize too small.");
74 };
75
76
77 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
78 inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::size_type
79 MemoryPool<T, BlockSize, ZeroOnDeallocate>::padPointer(data_pointer p, size_type align)
80 const noexcept {
81 uintptr_t result = reinterpret_cast<uintptr_t>(p);
82 return ((align - result) % align);
83 }
84
85
86
87 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
88 MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool()
89 noexcept {
90 data_element_ = nullptr;
91 free_element_ = nullptr;
92 }
93
94 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
95 MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool(const MemoryPool& memoryPool)
96 noexcept :
97 MemoryPool() {
98 }
99
100 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
101 MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool(MemoryPool&& memoryPool)
102 noexcept {
103 std::lock_guard<std::recursive_mutex> lock(m_);
104
105 data_element_ = memoryPool.data_element_;
106 memoryPool.data_element_ = nullptr;
107 free_element_ = memoryPool.free_element_;
108 memoryPool.free_element_ = nullptr;
109 }
110
111 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
112 template<class U>
113 MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool(const MemoryPool<U>& memoryPool)
114 noexcept :
115 MemoryPool() {
116 }
117
118 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
119 MemoryPool<T, BlockSize, ZeroOnDeallocate>&
120 MemoryPool<T, BlockSize, ZeroOnDeallocate>::operator=(MemoryPool&& memoryPool)
121 noexcept {
122 std::lock_guard<std::recursive_mutex> lock(m_);
123
124 if (this != &memoryPool) {
125 std::swap(data_element_, memoryPool.data_element_);
126 std::swap(free_element_, memoryPool.free_element_);
127 }
128 return *this;
129 }
130
131 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
132 MemoryPool<T, BlockSize, ZeroOnDeallocate>::~MemoryPool()
133 noexcept {
134 std::lock_guard<std::recursive_mutex> lock(m_);
135
136 element_pointer curr = data_element_;
137 while (curr != nullptr) {
138 element_pointer prev = curr->next;
139 operator delete(reinterpret_cast<void*>(curr));
140 curr = prev;
141 }
142
143 curr = free_element_;
144 while (curr != nullptr) {
145 element_pointer prev = curr->next;
146 operator delete(reinterpret_cast<void*>(curr));
147 curr = prev;
148 }
149 }
150
151 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
152 inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::pointer
153 MemoryPool<T, BlockSize, ZeroOnDeallocate>::address(reference x)
154 const noexcept {
155 return &x;
156 }
157
158 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
159 inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::const_pointer
160 MemoryPool<T, BlockSize, ZeroOnDeallocate>::address(const_reference x)
161 const noexcept {
162 return &x;
163 }
164
165 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
166 void
167 MemoryPool<T, BlockSize, ZeroOnDeallocate>::allocateBlock() {
168 // Allocate space for the new block and store a pointer to the previous one
169 data_pointer new_block = reinterpret_cast<data_pointer> (operator new(BlockSize));
170 element_pointer new_ele_pointer = reinterpret_cast<element_pointer>(new_block);
171 new_ele_pointer->pre = nullptr;
172 new_ele_pointer->next = nullptr;
173
174 if (data_element_) {
175 data_element_->pre = new_ele_pointer;
176 }
177
178 new_ele_pointer->next = data_element_;
179 data_element_ = new_ele_pointer;
180 }
181
182 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
183 inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::pointer
184 MemoryPool<T, BlockSize, ZeroOnDeallocate>::allocate(size_type n, const_pointer hint) {
185 std::lock_guard<std::recursive_mutex> lock(m_);
186
187 if (free_element_ != nullptr) {
188 data_pointer body =
189 reinterpret_cast<data_pointer>(reinterpret_cast<data_pointer>(free_element_) + sizeof(element_type));
190
191 size_type bodyPadding = padPointer(body, alignof(element_type));
192
193 pointer result = reinterpret_cast<pointer>(reinterpret_cast<data_pointer>(body + bodyPadding));
194
195 element_pointer tmp = free_element_;
196
197 free_element_ = free_element_->next;
198
199 if (free_element_)
200 free_element_->pre = nullptr;
201
202 tmp->next = data_element_;
203 if (data_element_)
204 data_element_->pre = tmp;
205 tmp->pre = nullptr;
206 data_element_ = tmp;
207
208 return result;
209 }
210 else {
211 allocateBlock();
212
213 data_pointer body =
214 reinterpret_cast<data_pointer>(reinterpret_cast<data_pointer>(data_element_) + sizeof(element_type));
215
216 size_type bodyPadding = padPointer(body, alignof(element_type));
217
218 pointer result = reinterpret_cast<pointer>(reinterpret_cast<data_pointer>(body + bodyPadding));
219
220 return result;
221 }
222 }
223
224 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
225 inline void
226 MemoryPool<T, BlockSize, ZeroOnDeallocate>::deallocate(pointer p, size_type n) {
227 std::lock_guard<std::recursive_mutex> lock(m_);
228
229 if (p != nullptr) {
230 element_pointer ele_p =
231 reinterpret_cast<element_pointer>(reinterpret_cast<data_pointer>(p) - sizeof(element_type));
232
233 if (ZeroOnDeallocate) {
234 memset(reinterpret_cast<data_pointer>(p), 0, BlockSize - sizeof(element_type));
235 }
236
237 if (ele_p->pre) {
238 ele_p->pre->next = ele_p->next;
239 }
240
241 if (ele_p->next) {
242 ele_p->next->pre = ele_p->pre;
243 }
244
245 if (ele_p->pre == nullptr) {
246 data_element_ = ele_p->next;
247 }
248
249 ele_p->pre = nullptr;
250 if (free_element_) {
251 ele_p->next = free_element_;
252 free_element_->pre = ele_p;
253 }
254 else {
255 ele_p->next = nullptr;
256 }
257 free_element_ = ele_p;
258 }
259 }
260
261 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
262 inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::size_type
263 MemoryPool<T, BlockSize, ZeroOnDeallocate>::max_size()
264 const noexcept {
265 size_type maxBlocks = -1 / BlockSize;
266 return (BlockSize - sizeof(data_pointer)) / sizeof(element_type) * maxBlocks;
267 }
268
269 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
270 template <class U, class... Args>
271 inline void
272 MemoryPool<T, BlockSize, ZeroOnDeallocate>::construct(U* p, Args&&... args) {
273 new (p) U(std::forward<Args>(args)...);
274 }
275
276 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
277 template <class U>
278 inline void
279 MemoryPool<T, BlockSize, ZeroOnDeallocate>::destroy(U* p) {
280 p->~U();
281 }
282
283 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
284 template <class... Args>
285 inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::pointer
286 MemoryPool<T, BlockSize, ZeroOnDeallocate>::newElement(Args&&... args) {
287 std::lock_guard<std::recursive_mutex> lock(m_);
288 pointer result = allocate();
289 construct<value_type>(result, std::forward<Args>(args)...);
290 return result;
291 }
292
293 template <typename T, size_t BlockSize, bool ZeroOnDeallocate>
294 inline void
295 MemoryPool<T, BlockSize, ZeroOnDeallocate>::deleteElement(pointer p) {
296 std::lock_guard<std::recursive_mutex> lock(m_);
297 if (p != nullptr) {
298 p->~value_type();
299 deallocate(p);
300 }
301 }
302 }
303 }
304
305 #endif // PPX_BASE_MEMORY_POOL_H_
306
307
308 使用示例:
309 #include <iostream>
310 #include <thread>
311 using namespace std;
312 class Apple {
313 public:
314 Apple() {
315 id_ = 0;
316 cout << "Apple()" << endl;
317 }
318
319 Apple(int id) {
320 id_ = id;
321 cout << "Apple(" << id_ << ")" << endl;
322 }
323
324 ~Apple() {
325 cout << "~Apple()" << endl;
326 }
327
328 void SetId(int id) {
329 id_ = id;
330 }
331
332 int GetId() {
333 return id_;
334 }
335 private:
336 int id_;
337 };
338
339
340
341 void ThreadProc(ppx::base::MemoryPool<char> *mp) {
342 int i = 0;
343 while (i++ < 100000) {
344 char* p0 = (char*)mp->allocate();
345
346 char* p1 = (char*)mp->allocate();
347
348 mp->deallocate(p0);
349
350 char* p2 = (char*)mp->allocate();
351
352 mp->deallocate(p1);
353
354 mp->deallocate(p2);
355
356 }
357 }
358
359 int main()
360 {
361 ppx::base::MemoryPool<char> mp;
362 int i = 0;
363 while (i++ < 100000) {
364 char* p0 = (char*)mp.allocate();
365
366 char* p1 = (char*)mp.allocate();
367
368 mp.deallocate(p0);
369
370 char* p2 = (char*)mp.allocate();
371
372 mp.deallocate(p1);
373
374 mp.deallocate(p2);
375
376 }
377
378 std::thread th0(ThreadProc, &mp);
379 std::thread th1(ThreadProc, &mp);
380 std::thread th2(ThreadProc, &mp);
381
382 th0.join();
383 th1.join();
384 th2.join();
385
386 Apple *apple = nullptr;
387 {
388 ppx::base::MemoryPool<Apple> mp2;
389 apple = mp2.newElement(10);
390 int a = apple->GetId();
391 apple->SetId(10);
392 a = apple->GetId();
393
394 mp2.deleteElement(apple);
395 }
396
397 apple->SetId(12);
398 int b = -4 % 4;
399
400 int *a = nullptr;
401 {
402 ppx::base::MemoryPool<int, 18> mp3;
403 a = mp3.allocate();
404 *a = 100;
405 //mp3.deallocate(a);
406
407 int *b = mp3.allocate();
408 *b = 200;
409 //mp3.deallocate(b);
410
411 mp3.deallocate(a);
412 mp3.deallocate(b);
413
414 int *c = mp3.allocate();
415 *c = 300;
416 }
417
418 getchar();
419 return 0;
420 }