Leviathan  0.8.0.0
Leviathan game engine
ObjectPoolThreadSafe.h
Go to the documentation of this file.
1 // Leviathan Game Engine
2 // Copyright (c) 2012-2018 Henri Hyyryläinen
3 #pragma once
4 // ------------------------------------ //
5 #include "ThreadSafe.h"
6 
7 #include "Exceptions.h"
8 
9 #include <functional>
10 #include <tuple>
11 #include <type_traits>
12 #include <unordered_map>
13 #include <vector>
14 
15 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
16 //#include "boost/pool/object_pool.hpp"
17 #include "boost/pool/pool.hpp"
18 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
19 
20 
21 namespace Leviathan {
22 
24 template<class ElementType, typename KeyType, bool AutoCleanupObjects = true>
26 public:
28 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
29  :
30  Elements(sizeof(ElementType), 100, 200)
31 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
32  {}
33 
35  {
36  if(!AutoCleanupObjects)
37  return;
38 
39  Clear();
40  }
41 
44  template<typename... Args>
45  ElementType* ConstructNew(Lock& guard, KeyType forentity, Args&&... args)
46  {
47  if(Find(guard, forentity))
48  throw Exception("Entity with ID already has object in pool of this type");
49 
50 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
51  // Get memory to hold the object //
52  void* memoryForObject = Elements.malloc();
53 
54  if(!memoryForObject)
55  throw Exception("Out of memory for element");
56 
57  // Construct the object //
58  ElementType* created;
59  try {
60  created = new(memoryForObject) ElementType(std::forward<Args>(args)...);
61  } catch(...) {
62 
63  Elements.free(memoryForObject);
64  throw;
65  }
66 #else
67  // Construct the object //
68  ElementType* created = new ElementType(std::forward<Args>(args)...);
69 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
70 
71  // Add to index for finding later //
72  Index.insert(std::make_pair(forentity, created));
73 
74  Added.push_back(std::make_tuple(created, forentity));
75 
76  return created;
77  }
78 
79  template<typename... Args>
80  inline ElementType* ConstructNew(KeyType forentity, Args&&... args)
81  {
82  GUARD_LOCK();
83  return ConstructNew(guard, forentity, args...);
84  }
85 
87  bool HasElementsInRemoved() const
88  {
89  GUARD_LOCK();
90  return !Removed.empty();
91  }
92 
94  bool HasElementsInAdded() const
95  {
96  GUARD_LOCK();
97  return !Added.empty();
98  }
99 
101  bool HasElementsInQueued() const
102  {
103  GUARD_LOCK();
104  return !Queued.empty();
105  }
106 
109  template<typename... Args>
110  void ReleaseQueued(Args&&... args)
111  {
112  GUARD_LOCK();
113 
114  for(auto iter = Queued.begin(); iter != Queued.end(); ++iter) {
115 
116  auto object = std::get<0>(*iter);
117  const auto id = std::get<1>(*iter);
118 
119  object->Release(std::forward<Args>(args)...);
120 
121 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
122  object->~ElementType();
123  Elements.free(object);
124 #else
125  delete object;
126 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
127 
128  Removed.push_back(std::make_tuple(object, id));
129  RemoveFromIndex(guard, id);
130  }
131 
132  Queued.clear();
133  }
134 
136  template<typename... Args>
137  void Release(KeyType entity, Args&&... args)
138  {
139  GUARD_LOCK();
140 
141  auto* object = Find(guard, entity);
142 
143  if(!object)
144  throw NotFound("entity not in pool");
145 
146  object->Release(std::forward<Args>(args)...);
147 
148 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
149  object->~ElementType();
150  Elements.free(object);
151 #else
152  delete object;
153 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
154 
155  RemoveFromIndex(guard, entity);
156  RemoveFromAdded(guard, entity);
157  }
158 
161  void ClearQueued()
162  {
163  GUARD_LOCK();
164 
165  for(auto iter = Queued.begin(); iter != Queued.end(); ++iter) {
166 
167  auto object = std::get<0>(*iter);
168  const auto id = std::get<1>(*iter);
169 
170 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
171  object->~ElementType();
172  Elements.free(object);
173 #else
174  delete object;
175 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
176 
177  Removed.push_back(std::make_tuple(object, id));
178  RemoveFromIndex(guard, id);
179  }
180 
181  Queued.clear();
182  }
183 
187  const auto& GetRemoved(Lock& locked) const
188  {
189  return Removed;
190  }
191 
192 
193 
197  auto& GetAdded(Lock& locked)
198  {
199  return Added;
200  }
201 
203  inline void ClearAdded()
204  {
205  GUARD_LOCK();
206  ClearAdded(guard);
207  }
208 
209  void ClearAdded(Lock& guard)
210  {
211  Added.clear();
212  }
213 
216  {
217  GUARD_LOCK();
218  Removed.clear();
219  }
220 
224  template<typename Any>
226  const std::vector<std::tuple<Any, KeyType>>& values, bool addtoremoved = false)
227  {
228  GUARD_LOCK();
229 
230  for(auto iter = values.begin(); iter != values.end(); ++iter) {
231 
232  auto todelete = Index.find(std::get<1>(*iter));
233 
234  if(todelete == Index.end())
235  continue;
236 
237  if(addtoremoved) {
238 
239  Removed.push_back(std::make_tuple(todelete->second, todelete->first));
240  }
241 
242 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
243  todelete->second->~ElementType();
244  Elements.free(todelete->second);
245 #else
246  delete todelete->second;
247 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
248 
249  RemoveFromAdded(guard, todelete->first);
250 
251  Index.erase(todelete);
252  }
253  }
254 
260  void RemoveFromAdded(Lock& guard, KeyType id)
261  {
262  for(auto iter = Added.begin(); iter != Added.end(); ++iter) {
263 
264  if(std::get<1>(*iter) == id) {
265 
266  Added.erase(iter);
267  return;
268  }
269  }
270  }
271 
273  ElementType* Find(Lock& guard, KeyType id) const
274  {
275  auto iter = Index.find(id);
276 
277  if(iter == Index.end())
278  return nullptr;
279 
280  return iter->second;
281  }
282 
283  inline ElementType* Find(KeyType id) const
284  {
285  GUARD_LOCK();
286  return Find(guard, id);
287  }
288 
290  void Destroy(Lock& guard, KeyType id, bool addtoremoved = true)
291  {
292  auto object = Find(guard, id);
293 
294  if(!object)
295  throw InvalidArgument("ID is not in index");
296 
297 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
298  object->~ElementType();
299  Elements.free(object);
300 #else
301  delete object;
302 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
303 
304  if(addtoremoved)
305  Removed.push_back(std::make_tuple(object, id));
306 
307  RemoveFromIndex(guard, id);
308  RemoveFromAdded(guard, id);
309  }
310 
311  inline void Destroy(KeyType id, bool addtoremoved = true)
312  {
313  GUARD_LOCK();
314  Destroy(guard, id, addtoremoved);
315  }
316 
320  void QueueDestroy(Lock& guard, KeyType id)
321  {
322  auto end = Index.end();
323  for(auto iter = Index.begin(); iter != end; ++iter) {
324 
325  if(iter->first == id) {
326 
327  Queued.push_back(std::make_tuple(iter->second, id));
328 
329  RemoveFromAdded(guard, id);
330 
331  return;
332  }
333  }
334 
335  throw InvalidArgument("ID is not in index");
336  }
337 
338  inline void QueueDestroy(KeyType id)
339  {
340  GUARD_LOCK();
341  QueueDestroy(guard, id);
342  }
343 
351  void Call(std::function<bool(ElementType&, KeyType, Lock&)> function)
352  {
353  GUARD_LOCK();
354 
355  for(auto iter = Index.begin(); iter != Index.end();) {
356 
357  if(function(*iter->second, iter->first, guard)) {
358 
359 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
360  iter->second->~ElementType();
361  Elements.free(iter->second);
362 #else
363  delete iter->second;
364 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
365 
366  iter = Index.erase(iter);
367 
368  } else {
369 
370  ++iter;
371  }
372  }
373  }
374 
377  void Clear()
378  {
379  GUARD_LOCK();
380 
381  for(auto iter = Index.begin(); iter != Index.end(); ++iter) {
382 
383 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
384  iter->second->~ElementType();
385  Elements.free(iter->second);
386 #else
387  delete iter->second;
388 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
389  }
390 
391  Index.clear();
392  Removed.clear();
393  Queued.clear();
394  Added.clear();
395  }
396 
400  inline std::unordered_map<KeyType, ElementType*>& GetIndex()
401  {
402  return Index;
403  }
404 
405 protected:
408  bool RemoveFromIndex(Lock& guard, KeyType id)
409  {
410  auto end = Index.end();
411  for(auto iter = Index.begin(); iter != end; ++iter) {
412 
413  if(iter->first == id) {
414 
415  Index.erase(iter);
416  return true;
417  }
418  }
419 
420  return false;
421  }
422 
423  inline bool RemoveFromIndex(KeyType id)
424  {
425  GUARD_LOCK();
426  return RemoveFromIndex(id);
427  }
428 
429 protected:
431  std::unordered_map<KeyType, ElementType*> Index;
432 
436  std::vector<std::tuple<ElementType*, KeyType>> Removed;
437 
441  std::vector<std::tuple<ElementType*, KeyType>> Queued;
442 
444  std::vector<std::tuple<ElementType*, KeyType>> Added;
445 
446 #ifdef LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
447  boost::pool<> Elements;
449 #endif // LEVIATHAN_USE_ACTUAL_OBJECT_POOLS
450 };
451 
452 } // namespace Leviathan
void RemoveFromAdded(Lock &guard, KeyType id)
Removes a specific id from the added list.
auto & GetAdded(Lock &locked)
Returns a reference to the vector of added elements.
Allows the inherited object to be locked.
Definition: ThreadSafe.h:137
ElementType * ConstructNew(Lock &guard, KeyType forentity, Args &&... args)
Constructs a new component of the held type for entity.
bool RemoveFromIndex(Lock &guard, KeyType id)
Removes an component from the index but doesn't destruct it.
ElementType * Find(Lock &guard, KeyType id) const
ElementType * Find(KeyType id) const
void Call(std::function< bool(ElementType &, KeyType, Lock &)> function)
Calls an function on all the objects in the pool.
void Destroy(Lock &guard, KeyType id, bool addtoremoved=true)
Destroys a component based on id.
void RemoveBasedOnKeyTupleList(const std::vector< std::tuple< Any, KeyType >> &values, bool addtoremoved=false)
Destroys without releasing elements based on ids in vector.
Thread safe version of ObjectPool.
boost::pool Elements
Pool for objects.
Base class for all exceptions thrown by Leviathan.
Definition: Exceptions.h:10
const auto & GetRemoved(Lock &locked) const
Returns a reference to the vector of removed elements.
ElementType * ConstructNew(KeyType forentity, Args &&... args)
bool HasElementsInQueued() const
Returns true if there are objects in Queued.
std::vector< std::tuple< ElementType *, KeyType > > Removed
std::vector< std::tuple< ElementType *, KeyType > > Added
Used for detecting created elements.
void ClearAdded()
Clears the added list.
void Destroy(KeyType id, bool addtoremoved=true)
void Release(KeyType entity, Args &&... args)
Calls Release on an object and then removes it from the pool.
std::vector< std::tuple< ElementType *, KeyType > > Queued
std::unordered_map< KeyType, ElementType * > Index
Used for looking up element belonging to id.
bool HasElementsInRemoved() const
Returns true if there are objects in Removed.
void ReleaseQueued(Args &&... args)
Calls Release with the specified arguments on elements that are queued for destruction.
void QueueDestroy(Lock &guard, KeyType id)
Queues destruction of an element.
void Clear()
Clears the index and replaces the pool with a new one.
bool HasElementsInAdded() const
Returns true if there are objects in Added.
The access mask controls which registered functions and classes a script sees.
Definition: GameModule.h:12
std::unordered_map< KeyType, ElementType * > & GetIndex()
Returns a direct access to Index.
#define GUARD_LOCK()
Definition: ThreadSafe.h:111
void ClearQueued()
Removes elements that are queued for destruction without calling release.
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:18
void ClearRemoved()
Clears the removed list.