StackAllocatorBuilder.inl.h 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Util/StackAllocatorBuilder.h>
  6. #include <AnKi/Util/Functions.h>
  7. #include <AnKi/Util/Logger.h>
  8. namespace anki {
  9. template<typename TChunk, typename TInterface, typename TLock>
  10. void StackAllocatorBuilder<TChunk, TInterface, TLock>::destroy()
  11. {
  12. // Free chunks
  13. TChunk* chunk = m_chunksListHead;
  14. while(chunk)
  15. {
  16. TChunk* next = chunk->m_nextChunk;
  17. m_interface.freeChunk(chunk);
  18. chunk = next;
  19. }
  20. // Do some error checks
  21. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  22. if(allocationCount)
  23. {
  24. if(!m_interface.ignoreDeallocationErrors() && allocationCount->load() != 0)
  25. {
  26. ANKI_UTIL_LOGW("Forgot to deallocate");
  27. }
  28. }
  29. m_crntChunk.setNonAtomically(nullptr);
  30. m_chunksListHead = nullptr;
  31. m_memoryCapacity = 0;
  32. m_chunksInUse = 0;
  33. }
  34. template<typename TChunk, typename TInterface, typename TLock>
  35. Error StackAllocatorBuilder<TChunk, TInterface, TLock>::allocate(PtrSize size, [[maybe_unused]] PtrSize alignment,
  36. TChunk*& chunk, PtrSize& offset)
  37. {
  38. ANKI_ASSERT(alignment <= m_interface.getMaxAlignment());
  39. size = getAlignedRoundUp(m_interface.getMaxAlignment(), size);
  40. ANKI_ASSERT(size > 0);
  41. chunk = nullptr;
  42. offset = kMaxPtrSize;
  43. while(true)
  44. {
  45. // Try to allocate from the current chunk, if there is one
  46. TChunk* crntChunk = m_crntChunk.load();
  47. if(crntChunk)
  48. {
  49. offset = crntChunk->m_offsetInChunk.fetchAdd(size);
  50. }
  51. if(crntChunk && offset + size <= crntChunk->m_chunkSize)
  52. {
  53. // All is fine, there is enough space in the chunk
  54. chunk = crntChunk;
  55. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  56. if(allocationCount)
  57. {
  58. allocationCount->fetchAdd(1);
  59. }
  60. break;
  61. }
  62. else
  63. {
  64. // Need new chunk
  65. LockGuard<TLock> lock(m_lock);
  66. // Make sure that only one thread will create a new chunk
  67. const Bool someOtherThreadCreatedAChunkWhileIWasHoldingTheLock = m_crntChunk.load() != crntChunk;
  68. if(someOtherThreadCreatedAChunkWhileIWasHoldingTheLock)
  69. {
  70. continue;
  71. }
  72. // We can create a new chunk
  73. // Compute the memory of the new chunk. Don't look at any previous chunk
  74. PtrSize nextChunkSize = m_interface.getInitialChunkSize();
  75. ANKI_ASSERT(nextChunkSize > 0);
  76. for(U32 i = 0; i < m_chunksInUse; ++i)
  77. {
  78. const F64 scale = m_interface.getNextChunkGrowScale();
  79. ANKI_ASSERT(scale >= 1.0);
  80. nextChunkSize = PtrSize(F64(nextChunkSize) * scale) + m_interface.getNextChunkGrowBias();
  81. ANKI_ASSERT(nextChunkSize > 0);
  82. }
  83. nextChunkSize = max(size, nextChunkSize); // Can't have the allocation fail
  84. alignRoundUp(m_interface.getMaxAlignment(), nextChunkSize); // Align again
  85. TChunk* nextChunk;
  86. if(crntChunk)
  87. {
  88. nextChunk = crntChunk->m_nextChunk;
  89. }
  90. else if(crntChunk == nullptr && m_chunksListHead)
  91. {
  92. // This will happen after reset
  93. nextChunk = m_chunksListHead;
  94. }
  95. else
  96. {
  97. nextChunk = nullptr;
  98. }
  99. if(nextChunk && nextChunk->m_chunkSize == nextChunkSize)
  100. {
  101. // Will recycle
  102. nextChunk->m_offsetInChunk.setNonAtomically(0);
  103. m_interface.recycleChunk(*nextChunk);
  104. ++m_chunksInUse;
  105. m_crntChunk.store(nextChunk);
  106. }
  107. else
  108. {
  109. // There is no chunk or there is but it's too small
  110. // Do that first because it might throw error
  111. TChunk* newNextChunk;
  112. ANKI_CHECK(m_interface.allocateChunk(nextChunkSize, newNextChunk));
  113. newNextChunk->m_nextChunk = nullptr;
  114. newNextChunk->m_offsetInChunk.setNonAtomically(0);
  115. newNextChunk->m_chunkSize = nextChunkSize;
  116. m_memoryCapacity += nextChunkSize;
  117. // Remove the existing next chunk if there is one
  118. TChunk* nextNextChunk = nullptr;
  119. if(nextChunk)
  120. {
  121. m_memoryCapacity -= nextChunk->m_chunkSize;
  122. nextNextChunk = nextChunk->m_nextChunk;
  123. m_interface.freeChunk(nextChunk);
  124. nextChunk = nullptr;
  125. }
  126. // Do list stuff
  127. if(crntChunk)
  128. {
  129. crntChunk->m_nextChunk = newNextChunk;
  130. ANKI_ASSERT(m_chunksListHead != nullptr);
  131. }
  132. else
  133. {
  134. ANKI_ASSERT(m_chunksListHead == nullptr);
  135. m_chunksListHead = newNextChunk;
  136. }
  137. newNextChunk->m_nextChunk = nextNextChunk;
  138. ++m_chunksInUse;
  139. m_crntChunk.store(newNextChunk);
  140. }
  141. }
  142. }
  143. ANKI_ASSERT(chunk && offset != kMaxPtrSize);
  144. return Error::kNone;
  145. }
  146. template<typename TChunk, typename TInterface, typename TLock>
  147. void StackAllocatorBuilder<TChunk, TInterface, TLock>::free()
  148. {
  149. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  150. if(allocationCount)
  151. {
  152. [[maybe_unused]] const U32 count = allocationCount->fetchSub(1);
  153. ANKI_ASSERT(count > 0);
  154. }
  155. }
  156. template<typename TChunk, typename TInterface, typename TLock>
  157. void StackAllocatorBuilder<TChunk, TInterface, TLock>::reset()
  158. {
  159. m_crntChunk.setNonAtomically(nullptr);
  160. m_chunksInUse = 0;
  161. // Reset allocation count and do some error checks
  162. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  163. if(allocationCount)
  164. {
  165. const U32 allocCount = allocationCount->exchange(0);
  166. if(!m_interface.ignoreDeallocationErrors() && allocCount != 0)
  167. {
  168. ANKI_UTIL_LOGW("Forgot to deallocate");
  169. }
  170. }
  171. }
  172. } // end namespace anki