Foundation
Loading...
Searching...
No Matches
AtomicPool.hpp
Go to the documentation of this file.
1#pragma once
2#include <cstddef>
3#include <cstring>
4#include <memory>
5#include <mutex>
6#include <utility>
7#include "Allocator.hpp"
8#include "Atomic.hpp"
9#include "Container.hpp"
10#include "Thread.hpp"
11namespace Foundation::Core
12{
22 template <typename T>
24 {
25 struct Node;
26 struct alignas(2 * sizeof(Node*)) PTag
27 {
28 Node* p{nullptr};
30 };
31 struct alignas(2 * sizeof(Node*)) Node
32 {
36
37 };
38 Node* mNodes{nullptr};
39 size_t mSize{0};
42
44 {
45 PTag old_head = mHead.load(std::memory_order_relaxed);
46 while (true)
47 {
48 if (!old_head.p)
49 return nullptr;
50 // ABA mitigation as seen in AtomicStack
51 PTag new_head = {old_head.p->next, old_head.tag + 1};
52 if (mHead.compare_exchange_weak(old_head, new_head, std::memory_order_acquire,
53 std::memory_order_relaxed))
54 {
55 return old_head.p;
56 }
57 }
58 }
59
61 {
62 PTag old_head = mHead.load(std::memory_order_relaxed);
63 while (true)
64 {
65 node->next = old_head.p;
67 if (mHead.compare_exchange_weak(old_head, new_head, std::memory_order_acquire,
68 std::memory_order_relaxed))
69 {
70 return;
71 }
72 }
73 }
74
75 public:
76 AtomicPool() = default;
77 AtomicPool(size_t size, Allocator* alloc) : mSize(size), mArena(alloc, size * sizeof(Node))
78 {
79 std::memset(mArena.arena.memory, 0, mArena.arena.size);
80 mNodes = static_cast<Node*>(mArena.arena.memory);
81 for (size_t i = 0; i + 1 < mSize; i++)
82 mNodes[i].next = &mNodes[i + 1];
83 mNodes[mSize - 1].next = nullptr;
84 mHead.store({&mNodes[0], 0});
85 }
92 template <typename... Args>
94 {
96 if (!node)
97 return nullptr;
98 node->used = true;
99 std::construct_at(&node->data, std::forward<Args>(args)...);
100 return &node->data;
101 }
105 void Destruct(T* ptr)
106 {
107 if (!ptr)
108 return;
109 // We know the memory layout is Node.data, so we can get the Node* from T*
110 // Hacky - though guaranteed to work by standard layout.
111 Node* node = reinterpret_cast<Node*>(reinterpret_cast<uintptr_t>(ptr) - offsetof(Node, data));
112 uintptr_t old_used = true;
113 if (node->used.compare_exchange_strong(old_used, false, std::memory_order_acquire, std::memory_order_relaxed))
114 {
115 node->data.~T();
117 }
118 }
123 size_t Index(T* ptr) const
124 {
125 Node* node = reinterpret_cast<Node*>(reinterpret_cast<uintptr_t>(ptr) - offsetof(Node, data));
126 return static_cast<size_t>(node - mNodes);
127 }
132 T* At(size_t index)
133 {
134 if (index >= mSize)
135 return nullptr;
136 return &mNodes[index].data;
137 }
141 void Collect()
142 {
143 for (size_t i = 0; i < mSize; i++)
144 if (mNodes[i].used)
145 mNodes[i].used = false, mNodes[i].data.~T();
146 }
148 };
149
158 template <typename T>
160 {
163 mutable Mutex mMutex;
164
165 public:
166 explicit DynamicPool(Allocator* alloc) : mAllocator(alloc), mObjects(alloc) {}
167
168 DynamicPool(DynamicPool const&) = delete;
172
179 template <typename... Args>
181 {
182 std::unique_lock lock(mMutex);
183 return &mObjects.emplace_back(std::forward<Args>(args)...);
184 }
185
189 void Destruct(T* ptr)
190 {
191 if (!ptr)
192 return;
193
195 {
196 std::unique_lock lock(mMutex);
197 for (auto it = mObjects.begin(); it != mObjects.end(); ++it)
198 {
199 if (&*it == ptr)
200 {
201 removed.splice(removed.end(), mObjects, it);
202 break;
203 }
204 }
205 }
206 }
207
211 void Collect()
212 {
214 {
215 std::unique_lock lock(mMutex);
216 removed.splice(removed.end(), mObjects);
217 }
218 }
219
220 [[nodiscard]] size_t Size() const
221 {
222 std::unique_lock lock(mMutex);
223 return mObjects.size();
224 }
225
227 };
228} // namespace Foundation::Core
General Purpose Allocator (GPA) interface.
Definition Allocator.hpp:24
Atomic, bounded object pool of fixed allocation sizes. Being a sibling to AtomicStack - key differenc...
Definition AtomicPool.hpp:24
Atomic< PTag > mHead
Definition AtomicPool.hpp:40
Node * mNodes
Definition AtomicPool.hpp:38
ScopedArena mArena
Definition AtomicPool.hpp:41
void Destruct(T *ptr)
Destructs the object pointed to by ptr and returns it to the pool.
Definition AtomicPool.hpp:105
~AtomicPool()
Definition AtomicPool.hpp:147
size_t mSize
Definition AtomicPool.hpp:39
T * At(size_t index)
Returns the pointer at the given index in the pool.
Definition AtomicPool.hpp:132
size_t Index(T *ptr) const
Returns the index of the given pointer in the pool.
Definition AtomicPool.hpp:123
T * Construct(Args &&... args)
Constructs an object of type T in the pool with the given arguments.
Definition AtomicPool.hpp:93
void Collect()
Destruct all allocated objects in the pool, collecting garbage.
Definition AtomicPool.hpp:141
AtomicPool(size_t size, Allocator *alloc)
Definition AtomicPool.hpp:77
void DeallocateNode(Node *node)
Definition AtomicPool.hpp:60
Node * AllocateNode()
Definition AtomicPool.hpp:43
Mutex-protected, dynamically sized object pool with stable object pointers.
Definition AtomicPool.hpp:160
DynamicPool(DynamicPool &&)=delete
DynamicPool(Allocator *alloc)
Definition AtomicPool.hpp:166
~DynamicPool()
Definition AtomicPool.hpp:226
List< T > mObjects
Definition AtomicPool.hpp:162
DynamicPool(DynamicPool const &)=delete
T * Construct(Args &&... args)
Constructs an object of type T in the pool with the given arguments.
Definition AtomicPool.hpp:180
DynamicPool & operator=(DynamicPool &&)=delete
DynamicPool & operator=(DynamicPool const &)=delete
Allocator * mAllocator
Definition AtomicPool.hpp:161
Mutex mMutex
Definition AtomicPool.hpp:163
size_t Size() const
Definition AtomicPool.hpp:220
void Destruct(T *ptr)
Destructs the object pointed to by ptr and releases its storage.
Definition AtomicPool.hpp:189
void Collect()
Destructs all allocated objects in the pool.
Definition AtomicPool.hpp:211
Lock-free atomic primitives and implementations of data structures.
Definition Allocator.hpp:5
std::mutex Mutex
Definition Thread.hpp:10
std::list< T, StlAllocator< T > > List
std::list with explicit Foundation::Core::StlAllocator constructor
Definition Container.hpp:188
std::atomic< T > Atomic
Alias of std::atomic<T>.
Definition Atomic.hpp:26
size_type size
Definition Allocator.hpp:19
pointer memory
Definition Allocator.hpp:18
Definition AtomicPool.hpp:32
Atomic< uintptr_t > used
Definition AtomicPool.hpp:34
Node * next
Definition AtomicPool.hpp:33
T data
Definition AtomicPool.hpp:35
Definition AtomicPool.hpp:27
Node * p
Definition AtomicPool.hpp:28
uintptr_t tag
Definition AtomicPool.hpp:29
RAII wrapper for an arena allocated from an Allocator.
Definition Allocator.hpp:45
Arena arena
Definition Allocator.hpp:47