The Sparta Modeling Framework
Loading...
Searching...
No Matches
SpartaSharedPointerAllocator.hpp
1#pragma once
2
4
5namespace sparta
6{
7
153 template<class PointerT>
154 class SpartaSharedPointerAllocator : public BaseAllocator
155 {
156 public:
157
159 using element_type = PointerT;
160
163 using WaterMarkWarningCallback = std::function<void (const SpartaSharedPointerAllocator &)>;
164
167 using OverAllocationCallback = std::function<void (const SpartaSharedPointerAllocator &)>;
168
180 SpartaSharedPointerAllocator(const size_t max_num_blocks,
181 const size_t water_mark) :
182 memory_blocks_(max_num_blocks),
183 water_mark_(water_mark),
184 watermark_warning_callback_(waterMarkWarningCallback_),
185 over_allocation_callback_(overAllocationCallback_)
186 {
187 sparta_assert(water_mark <= max_num_blocks,
188 "The water_mark on SpartaSharedPointerAllocator should be less than or " <<
189 "equal to the maximum number of blocks. water_mark=" << water_mark <<
190 " max_num_blocks=" << max_num_blocks);
191 free_blocks_.resize(max_num_blocks, nullptr);
192 }
193
200
204 {
205 std::cerr << "WARNING: Seems that not all of the blocks made it back. \n'" <<
206 __PRETTY_FUNCTION__ << "'\nAllocated: " << allocated_ <<
207 "\nReturned: " << free_idx_ << std::endl;
208 }
209 }
210
215 size_t getNumFree() const {
216 return free_idx_;
217 }
218
225 size_t getNumAllocated() const {
226 return memory_blocks_.size();
227 }
228
234 return (allocated_ != free_idx_);
235 }
236
242 std::vector<const PointerT*> getOutstandingAllocatedObjects() const
243 {
244 std::vector<const PointerT*> allocated_objs;
245
246 const size_t size = memory_blocks_.size();
247 for(uint32_t i = 0; i < size; ++i) {
248 if(memory_blocks_[i]->ref_count->count > 0) {
249 allocated_objs.emplace_back(memory_blocks_[i]->object);
250 }
251 }
252
253 return allocated_objs;
254 }
255
264 watermark_warning_callback_ = callback;
265 }
266
275 over_allocation_callback_ = callback;
276 }
277
278 private:
279
280 // Let's make friends
281 friend class SpartaSharedPointer<PointerT>;
282
283 // Make the allocate function a buddy
284 template<typename PtrT, typename... Args>
286 allocate_sparta_shared_pointer(SpartaSharedPointerAllocator<PtrT> &, Args&&...args);
287
288 template<typename T>
289 struct AlignedStorage
290 {
291 alignas(T) std::byte buf[sizeof(T)];
292 };
293
294 // Internal MemoryBlock
295 struct MemBlock : public BaseAllocator::MemBlockBase
296 {
297 using RefCountType = typename SpartaSharedPointer<PointerT>::RefCount;
298
299 using RefCountAlignedStorage = AlignedStorage<RefCountType>;
300
301 using PointerTAlignedStorage = AlignedStorage<PointerT>;
302
303 template<typename ...ObjArgs>
304 MemBlock(SpartaSharedPointerAllocator * alloc_in, ObjArgs&&...obj_args) :
305 MemBlockBase(alloc_in)
306 {
307 // Build the new object using the inplacement new.
308 object = new (&object_storage) PointerT(std::forward<ObjArgs>(obj_args)...);
309
310 // Build the reference count using that object
311 ref_count = new (&ref_count_storage) RefCountType(object, (void*)this);
312 }
313
314 PointerT * object = nullptr;
315 RefCountType * ref_count = nullptr;
316
317 RefCountAlignedStorage ref_count_storage;
318 PointerTAlignedStorage object_storage;
319
320 MemBlock(const MemBlock &) = delete;
321 MemBlock(MemBlock &&) = delete;
322 MemBlock & operator=(const MemBlock&) = delete;
323 ~MemBlock() {} // Does nothing; implementation for cppcheck
324 };
325
326 // Class to manager the large block of memory allocated
327 class MemBlockVector
328 {
329 // Align the storage for the MemBlock
330 using MemBlockAlignedStorage = AlignedStorage<MemBlock>;
331
332 std::vector<MemBlockAlignedStorage> data_;
333 std::size_t size_ = 0;
334
335 public:
336
337 // Create num_blocks amount of MemBlocks to use
338 explicit MemBlockVector(const size_t num_blocks) :
339 data_(num_blocks)
340 {}
341
342 // Allocate an object in aligned storage
343 template<typename ...Args>
344 MemBlock * allocate(SpartaSharedPointerAllocator * alloc, Args&&... args)
345 {
346 sparta_assert(size_ < data_.size(), "Out of memory");
347
348 // construct value in memory of aligned storage
349 // using inplace operator new
350 auto block = new(&data_[size_]) MemBlock(alloc, std::forward<Args>(args)...);
351 ++size_;
352
353 return block;
354 }
355
356 // Access an object in aligned storage
357 const MemBlock* operator[](std::size_t idx) const
358 {
359 sparta_assert(idx < size_);
360 return reinterpret_cast<const MemBlock*>(&data_[idx]);
361 }
362
363 // The capacity or num_blocks
364 size_t capacity() const {
365 return data_.size();
366 }
367
368 // The number of blocks requested so far
369 size_t size() const {
370 return size_;
371 }
372
373 // Start over.
374 void clear() {
375 size_ = 0;
376 }
377 };
378
388 template<typename ...PointerTArgs>
389 typename SpartaSharedPointer<PointerT>::RefCount * allocate_(PointerTArgs&&... args)
390 {
391 // Return memory allocated here.
392 MemBlock * block = nullptr;
393
394 // Check for previously freed blocks and reuse them.
395 if(free_idx_ > 0) {
396 --free_idx_;
397 block = free_blocks_[free_idx_];
398 sparta_assert(block->ref_count->p != nullptr);
399 block->ref_count->mem_block = (void*)block;
400 block->ref_count->count = 1;
401 // perform an in place new on the reused pointer
402 new (block->ref_count->p) PointerT(std::forward<PointerTArgs>(args)...);
403 }
404 else {
405 if(SPARTA_EXPECT_FALSE(allocated_ > water_mark_)) {
406 if(SPARTA_EXPECT_FALSE(!water_mark_warning_)) {
407 watermark_warning_callback_(*this);
408 water_mark_warning_ = true;
409 }
410
411 // Only need to check for overallocation if we've passed the watermark
412 if(SPARTA_EXPECT_FALSE(allocated_ >= memory_blocks_.capacity())) {
413 over_allocation_callback_(*this);
414 SpartaException ex;
415 ex << "This allocator has run out of memory: \n\n\t"
416 << __PRETTY_FUNCTION__
417 << "\n\n"
418 << "\t\tNumber blocks preallocated: " << memory_blocks_.capacity()
419 << "\n\t\tWatermark : " << water_mark_;
420 throw ex;
421 }
422 }
423 block = memory_blocks_.allocate(this, std::forward<PointerTArgs>(args)...);
424 ++allocated_;
425 }
426 return block->ref_count;
427 }
428
429 // Default watermark warning callback
430 static void waterMarkWarningCallback_(const SpartaSharedPointerAllocator & allocator) {
431 std::cerr << "WARNING: The watermark for this allocator has been surpassed: \n\n\t"
432 << __PRETTY_FUNCTION__
433 << "\n\n"
434 << "\t\tNumber blocks preallocated: " << allocator.memory_blocks_.capacity()
435 << "\n\t\tWatermark : " << allocator.water_mark_ << std::endl;
436 }
437
438 // Default over allocation callback
439 static void overAllocationCallback_(const SpartaSharedPointerAllocator & allocator) {
440 }
441
446 void releaseObject_(void * block) const noexcept override {
447 static_cast<MemBlock *>(block)->ref_count->p->~PointerT();
448 }
449
459 void releaseBlock_(void * block) override {
460 sparta_assert(free_idx_ < free_blocks_.capacity());
461 free_blocks_[free_idx_] = static_cast<MemBlock *>(block);
462 ++free_idx_;
463 }
464
465 MemBlockVector memory_blocks_;
466 std::vector<MemBlock*> free_blocks_;
467 size_t free_idx_ = 0;
468 size_t allocated_ = 0;
469 const size_t water_mark_;
470 bool water_mark_warning_ = false;
471 WaterMarkWarningCallback watermark_warning_callback_;
472 OverAllocationCallback over_allocation_callback_;
473 };
474
486 template<typename PointerT, typename... Args>
487 SpartaSharedPointer<PointerT>
489 Args&&...args)
490 {
491 static_assert(std::is_constructible<PointerT, Args...>::value,
492 "Can't construct object in allocate_sparta_shared_pointer with the arguments given");
493
494 SpartaSharedPointer<PointerT> ptr(alloc.allocate_(std::forward<Args>(args)...));
495 return ptr;
496 }
497
498}
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
Defines the SpartaSharedPointer class used for garbage collection.
A memory allocator complementing SpartaSharedPointer that reuses old memory.
std::function< void(const SpartaSharedPointerAllocator &)> OverAllocationCallback
bool hasOutstandingObjects() const
Query to see if the allocator has any outstanding memory not yet returned.
SpartaSharedPointerAllocator(const SpartaSharedPointerAllocator &)=delete
Disallow copies/assignments/moves.
void registerCustomOverAllocationCallback(const OverAllocationCallback &callback)
Set a custom over allocation callback.
SpartaSharedPointerAllocator(SpartaSharedPointerAllocator &&)=delete
Disallow copies/assignments/moves.
std::vector< const PointerT * > getOutstandingAllocatedObjects() const
Return a vector of objects that have not been deleted/not yet returned to the allocator.
std::function< void(const SpartaSharedPointerAllocator &)> WaterMarkWarningCallback
void registerCustomWaterMarkCallback(const WaterMarkWarningCallback &callback)
Set a custom watermark warning callback.
size_t getNumAllocated() const
Return the number of allocated objects this allocator allocated over time.
size_t getNumFree() const
Return the number of freed objects this allocator holds.
~SpartaSharedPointerAllocator()
Clean up the allocator – will warn if there are objects not returned to the allocator.
SpartaSharedPointerAllocator operator=(const SpartaSharedPointerAllocator &)=delete
Disallow copies/assignments/moves.
SpartaSharedPointerAllocator(const size_t max_num_blocks, const size_t water_mark)
Construct this allocator with num_blocks of initial memory.
Used for garbage collection, will delete the object it points to when all objects are finished using ...
Macros for handling exponential backoff.
SpartaSharedPointer< PointerT > allocate_sparta_shared_pointer(SpartaSharedPointerAllocator< PointerT > &alloc, Args &&...args)
Allocate a SpartaSharedPointer.