FixedSizeFreeList.h 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Core/NonCopyable.h>
  5. #include <Jolt/Core/Mutex.h>
  6. #include <Jolt/Core/Memory.h>
  7. #include <atomic>
  8. namespace JPH {
  9. /// Class that allows lock free creation / destruction of objects (unless a new page of objects needs to be allocated)
  10. /// It contains a fixed pool of objects and also allows batching up a lot of objects to be destroyed
  11. /// and doing the actual free in a single atomic operation
  12. template <typename Object>
  13. class FixedSizeFreeList : public NonCopyable
  14. {
  15. private:
  16. /// Storage type for an Object
  17. struct alignas(Object) ObjectStorage
  18. {
  19. /// Constructor to satisfy the vector class
  20. ObjectStorage() = default;
  21. ObjectStorage(const ObjectStorage &inRHS) : mNextFreeObject(inRHS.mNextFreeObject.load()) { memcpy(mData, inRHS.mData, sizeof(Object)); }
  22. /// Storage space for the Object, stored as uninitialized data
  23. uint8 mData[sizeof(Object)];
  24. /// When the object is freed (or in the process of being freed as a batch) this will contain the next free object
  25. /// When an object is in use it will contain the object's index in the free list
  26. atomic<uint32> mNextFreeObject;
  27. };
  28. static_assert(alignof(ObjectStorage) == alignof(Object), "Object not properly aligned");
  29. /// Access the object storage given the object index
  30. const ObjectStorage & GetStorage(uint32 inObjectIndex) const { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; }
  31. ObjectStorage & GetStorage(uint32 inObjectIndex) { return mPages[inObjectIndex >> mPageShift][inObjectIndex & mObjectMask]; }
  32. /// Number of objects that we currently have in the free list / new pages
  33. #ifdef JPH_ENABLE_ASSERTS
  34. atomic<uint32> mNumFreeObjects;
  35. #endif // JPH_ENABLE_ASSERTS
  36. /// Simple counter that makes the first free object pointer update with every CAS so that we don't suffer from the ABA problem
  37. atomic<uint32> mAllocationTag;
  38. /// Index of first free object, the first 32 bits of an object are used to point to the next free object
  39. atomic<uint64> mFirstFreeObjectAndTag;
  40. /// Size (in objects) of a single page
  41. uint32 mPageSize;
  42. /// Number of bits to shift an object index to the right to get the page number
  43. uint32 mPageShift;
  44. /// Mask to and an object index with to get the page number
  45. uint32 mObjectMask;
  46. /// Total number of pages that are usable
  47. uint32 mNumPages;
  48. /// Total number of objects that have been allocated
  49. uint32 mNumObjectsAllocated;
  50. /// The first free object to use when the free list is empty (may need to allocate a new page)
  51. atomic<uint32> mFirstFreeObjectInNewPage;
  52. /// Array of pages of objects
  53. ObjectStorage ** mPages = nullptr;
  54. /// Mutex that is used to allocate a new page if the storage runs out
  55. Mutex mPageMutex;
  56. public:
  57. /// Invalid index
  58. static const uint32 cInvalidObjectIndex = 0xffffffff;
  59. /// Size of an object + bookkeeping for the freelist
  60. static const int ObjectStorageSize = sizeof(ObjectStorage);
  61. /// Destructor
  62. inline ~FixedSizeFreeList();
  63. /// Initialize the free list, up to inMaxObjects can be allocated
  64. inline void Init(uint inMaxObjects, uint inPageSize);
  65. /// Lockless construct a new object, inParameters are passed on to the constructor
  66. template <typename... Parameters>
  67. inline uint32 ConstructObject(Parameters &&... inParameters);
  68. /// Lockless destruct an object and return it to the free pool
  69. inline void DestructObject(uint32 inObjectIndex);
  70. /// Lockless destruct an object and return it to the free pool
  71. inline void DestructObject(Object *inObject);
  72. /// A batch of objects that can be destructed
  73. struct Batch
  74. {
  75. uint32 mFirstObjectIndex = cInvalidObjectIndex;
  76. uint32 mLastObjectIndex = cInvalidObjectIndex;
  77. uint32 mNumObjects = 0;
  78. };
  79. /// Add a object to an existing batch to be destructed.
  80. /// Adding objects to a batch does not destroy or modify the objects, this will merely link them
  81. /// so that the entire batch can be returned to the free list in a single atomic operation
  82. inline void AddObjectToBatch(Batch &ioBatch, uint32 inObjectIndex);
  83. /// Lockless destruct batch of objects
  84. inline void DestructObjectBatch(Batch &ioBatch);
  85. /// Access an object by index.
  86. inline Object & Get(uint32 inObjectIndex) { return reinterpret_cast<Object &>(GetStorage(inObjectIndex).mData); }
  87. /// Access an object by index.
  88. inline const Object & Get(uint32 inObjectIndex) const { return reinterpret_cast<const Object &>(GetStorage(inObjectIndex).mData); }
  89. };
  90. } // JPH
  91. #include "FixedSizeFreeList.inl"