AkObjectPool.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*******************************************************************************
  2. The content of this file includes portions of the AUDIOKINETIC Wwise Technology
  3. released in source code form as part of the SDK installer package.
  4. Commercial License Usage
  5. Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
  6. may use this file in accordance with the end user license agreement provided
  7. with the software or, alternatively, in accordance with the terms contained in a
  8. written agreement between you and Audiokinetic Inc.
  9. Apache License Usage
  10. Alternatively, this file may be used under the Apache License, Version 2.0 (the
  11. "Apache License"); you may not use this file except in compliance with the
  12. Apache License. You may obtain a copy of the Apache License at
  13. http://www.apache.org/licenses/LICENSE-2.0.
  14. Unless required by applicable law or agreed to in writing, software distributed
  15. under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
  16. OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
  17. the specific language governing permissions and limitations under the License.
  18. Copyright (c) 2023 Audiokinetic Inc.
  19. *******************************************************************************/
  20. #ifndef _AKPOOLALLOCATOR_H
  21. #define _AKPOOLALLOCATOR_H
  22. #include <AK/Tools/Common/AkObject.h>
  23. #include <AK/Tools/Common/AkAssert.h>
  24. #include <AK/Tools/Common/AkArray.h>
  25. /// Extra debug option when calling ObjectPool::Deallocate.
  26. /// Going through the free list can be slow when the object count is high.
  27. #define AK_OBJECT_POOL_EXTRA_SAFETY
  28. namespace UnitTest { struct ObjectPoolHelper; }
  29. namespace AK
  30. {
  31. template<AkMemID T_MEMID = AkMemID_Object>
  32. using ObjectPoolDefaultAllocator = AkArrayAllocatorNoAlign<T_MEMID>;
  33. struct ObjectPoolNoLock
  34. {
  35. inline static constexpr void Lock() {}
  36. inline static constexpr void Unlock() {}
  37. };
  38. using ObjectPoolDefaultLockType = ObjectPoolNoLock;
  39. /// An object pool of N reusable objects with one allocation.
  40. template<
  41. typename T,
  42. typename AllocatorType = ObjectPoolDefaultAllocator<>,
  43. typename LockType = ObjectPoolDefaultLockType>
  44. class ObjectPool : AllocatorType, LockType
  45. {
  46. public:
  47. using ValueType = T;
  48. using SizeType = AkUInt32;
  49. static constexpr SizeType kInvalidIndex = (SizeType)-1;
  50. union DataType
  51. {
  52. inline ValueType& Data() { return *(ValueType*)data; }
  53. inline const ValueType& Data() const { return *(const ValueType*)data; }
  54. alignas(ValueType) uint8_t data[sizeof(ValueType)];
  55. SizeType next;
  56. };
  57. using AllocatorType::AllocatorType;
  58. ObjectPool() = default;
  59. ObjectPool(const ObjectPool&) = delete;
  60. ObjectPool(ObjectPool&&) = delete;
  61. inline ~ObjectPool() { AKASSERT(!m_data); }
  62. ObjectPool& operator=(const ObjectPool&) = delete;
  63. ObjectPool& operator=(ObjectPool&&) = delete;
  64. inline AKRESULT Init(SizeType count)
  65. {
  66. AKASSERT(!m_data && "ObjectPool is already initialized, call 'Term' before calling 'Init' again");
  67. AKASSERT(count && "ObjectPool count must be greater than zero");
  68. AKASSERT(count < kInvalidIndex && "ObjectPool count is above SizeType's maximum capacity");
  69. if (m_data || !count)
  70. return AK_InvalidParameter;
  71. m_data = reinterpret_cast<DataType*>(AllocatorType::Alloc(count * sizeof(DataType)));
  72. m_capacity = 0;
  73. m_size = 0;
  74. m_freeList = 0;
  75. if (!m_data)
  76. return AK_InsufficientMemory;
  77. m_capacity = count;
  78. for (SizeType i = 0; i < m_capacity - 1; i++)
  79. m_data[i].next = i + 1;
  80. m_data[m_capacity - 1].next = kInvalidIndex;
  81. return AK_Success;
  82. }
  83. inline void Term()
  84. {
  85. // Only assert when some object are still allocated and ValueType is not trivially destructible.
  86. AKASSERT(m_size == 0 && "Can't call Term() when some objects are still allocated");
  87. if (!m_data)
  88. return;
  89. AllocatorType::Free(m_data);
  90. m_data = nullptr;
  91. m_capacity = 0;
  92. m_size = 0;
  93. m_freeList = 0;
  94. }
  95. AK_NODISCARD inline SizeType Size() const { return m_size; }
  96. AK_NODISCARD inline SizeType Capacity() const { return m_capacity; }
  97. AK_NODISCARD inline bool IsFull() const { return m_size >= m_capacity; }
  98. AK_NODISCARD inline bool IsEmpty() const { return m_size == 0; }
  99. /// Allocates a new object.
  100. /// @returns the object on success or nullptr if it fails (already full).
  101. /// @{
  102. AK_NODISCARD inline ValueType* Allocate()
  103. {
  104. if (IsFull())
  105. return nullptr;
  106. LockType::Lock();
  107. DataType& data = m_data[m_freeList];
  108. m_freeList = data.next;
  109. m_size++;
  110. LockType::Unlock();
  111. return &data.Data();
  112. }
  113. /// Initialize memory before returning.
  114. AK_NODISCARD inline ValueType* AllocateZeroFilled()
  115. {
  116. ValueType* value = Allocate();
  117. if (value)
  118. AKPLATFORM::AkMemSet((void*)value, 0, (AkUInt32)sizeof(ValueType));
  119. return value;
  120. }
  121. /// @}
  122. /// Deallocates given pointer.
  123. /// Calling Deallocate when IsEmpty() returns true is undefined behaviour.
  124. /// Calling Deallocate with a pointer that wasn't allocated by this pool is undefined behaviour.
  125. ///
  126. /// You can define AK_OBJECT_POOL_EXTRA_SAFETY for extra debugging check.
  127. inline AKRESULT Deallocate(ValueType* data)
  128. {
  129. AKASSERT(data && "Deallocating null data");
  130. DataType* tdata = reinterpret_cast<DataType*>(data);
  131. // Check if data is in memory range.
  132. AKASSERT((tdata >= m_data && tdata < m_data + m_capacity) && "Pointer address out of range");
  133. if (tdata < m_data || tdata >= m_data + m_capacity)
  134. return AK_InvalidParameter;
  135. LockType::Lock();
  136. AKASSERT(m_size && "Trying to deallocate when empty");
  137. #ifdef AK_OBJECT_POOL_EXTRA_SAFETY
  138. AKASSERT(IsAllocated((SizeType)(tdata - m_data)));
  139. #endif
  140. m_size--;
  141. tdata->next = m_freeList;
  142. m_freeList = (SizeType)(tdata - m_data);
  143. LockType::Unlock();
  144. return AK_Success;
  145. }
  146. /// Deallocates all objects.
  147. /// This function should only be called if ValueType is trivially destructible.
  148. inline void Clear()
  149. {
  150. for (SizeType i = 0; i < m_capacity - 1; i++)
  151. m_data[i].next = i + 1;
  152. m_data[m_capacity - 1].next = kInvalidIndex;
  153. m_size = 0;
  154. m_freeList = 0;
  155. }
  156. private:
  157. DataType* m_data = nullptr;
  158. SizeType m_capacity = 0;
  159. SizeType m_size = 0;
  160. SizeType m_freeList = 0;
  161. friend struct UnitTest::ObjectPoolHelper;
  162. inline bool IsAllocated(SizeType index) const
  163. {
  164. for (SizeType k = m_freeList; k != kInvalidIndex; k = m_data[k].next)
  165. if (index == k)
  166. return false;
  167. return true;
  168. }
  169. };
  170. } // namespace AK
  171. #endif