StackAllocatorBuilder.inl.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Copyright (C) 2009-present, 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, PtrSize alignment, TChunk*& chunk, PtrSize& offset)
  36. {
  37. ANKI_ASSERT(size > 0);
  38. ANKI_ASSERT(alignment > 0);
  39. size += alignment;
  40. chunk = nullptr;
  41. offset = kMaxPtrSize;
  42. if(size > m_interface.getMaxChunkSize())
  43. {
  44. ANKI_UTIL_LOGE("Size requested is greated than the max chunk size");
  45. return Error::kOutOfMemory;
  46. }
  47. while(true)
  48. {
  49. // Try to allocate from the current chunk, if there is one
  50. TChunk* crntChunk = m_crntChunk.load();
  51. if(crntChunk)
  52. {
  53. offset = crntChunk->m_offsetInChunk.fetchAdd(size);
  54. }
  55. if(crntChunk && offset + size <= crntChunk->m_chunkSize)
  56. {
  57. // All is fine, there is enough space in the chunk
  58. chunk = crntChunk;
  59. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  60. if(allocationCount)
  61. {
  62. allocationCount->fetchAdd(1);
  63. }
  64. break;
  65. }
  66. else
  67. {
  68. // Need new chunk, create it and loop back
  69. LockGuard<TLock> lock(m_lock);
  70. // Make sure that only one thread will create a new chunk
  71. const Bool someOtherThreadCreatedAChunkWhileIWasHoldingTheLock = m_crntChunk.load() != crntChunk;
  72. if(someOtherThreadCreatedAChunkWhileIWasHoldingTheLock)
  73. {
  74. continue;
  75. }
  76. // We can create a new chunk
  77. // Compute the memory of the new chunk. Don't look at any previous chunk
  78. PtrSize nextChunkSize = m_interface.getInitialChunkSize();
  79. ANKI_ASSERT(nextChunkSize > 0);
  80. for(U32 i = 0; i < m_chunksInUse; ++i)
  81. {
  82. const F64 scale = m_interface.getNextChunkGrowScale();
  83. ANKI_ASSERT(scale >= 1.0);
  84. nextChunkSize = PtrSize(F64(nextChunkSize) * scale) + m_interface.getNextChunkGrowBias();
  85. ANKI_ASSERT(nextChunkSize > 0);
  86. }
  87. nextChunkSize = max(size, nextChunkSize); // Can't have the allocation fail
  88. nextChunkSize = min(nextChunkSize, m_interface.getMaxChunkSize());
  89. ANKI_ASSERT(size <= nextChunkSize); // Should have caught this before
  90. TChunk* nextChunk;
  91. if(crntChunk)
  92. {
  93. nextChunk = crntChunk->m_nextChunk;
  94. }
  95. else if(crntChunk == nullptr && m_chunksListHead)
  96. {
  97. // This will happen after reset
  98. nextChunk = m_chunksListHead;
  99. }
  100. else
  101. {
  102. nextChunk = nullptr;
  103. }
  104. if(nextChunk && nextChunk->m_chunkSize == nextChunkSize)
  105. {
  106. // Will recycle
  107. nextChunk->m_offsetInChunk.setNonAtomically(0);
  108. m_interface.recycleChunk(*nextChunk);
  109. ++m_chunksInUse;
  110. m_crntChunk.store(nextChunk);
  111. }
  112. else
  113. {
  114. // There is no chunk or there is but it's too small
  115. // Do that first because it might throw error
  116. TChunk* newNextChunk;
  117. ANKI_CHECK(m_interface.allocateChunk(nextChunkSize, newNextChunk));
  118. newNextChunk->m_nextChunk = nullptr;
  119. newNextChunk->m_offsetInChunk.setNonAtomically(0);
  120. newNextChunk->m_chunkSize = nextChunkSize;
  121. m_memoryCapacity += nextChunkSize;
  122. // Remove the existing next chunk if there is one
  123. TChunk* nextNextChunk = nullptr;
  124. if(nextChunk)
  125. {
  126. m_memoryCapacity -= nextChunk->m_chunkSize;
  127. nextNextChunk = nextChunk->m_nextChunk;
  128. m_interface.freeChunk(nextChunk);
  129. nextChunk = nullptr;
  130. }
  131. // Do list stuff
  132. if(crntChunk)
  133. {
  134. crntChunk->m_nextChunk = newNextChunk;
  135. ANKI_ASSERT(m_chunksListHead != nullptr);
  136. }
  137. else
  138. {
  139. ANKI_ASSERT(m_chunksListHead == nullptr);
  140. m_chunksListHead = newNextChunk;
  141. }
  142. newNextChunk->m_nextChunk = nextNextChunk;
  143. ++m_chunksInUse;
  144. m_crntChunk.store(newNextChunk);
  145. }
  146. }
  147. }
  148. alignRoundUp(alignment, offset);
  149. ANKI_ASSERT(chunk && offset != kMaxPtrSize);
  150. ANKI_ASSERT(offset + size <= chunk->m_chunkSize);
  151. return Error::kNone;
  152. }
  153. template<typename TChunk, typename TInterface, typename TLock>
  154. void StackAllocatorBuilder<TChunk, TInterface, TLock>::free()
  155. {
  156. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  157. if(allocationCount)
  158. {
  159. [[maybe_unused]] const U32 count = allocationCount->fetchSub(1);
  160. ANKI_ASSERT(count > 0);
  161. }
  162. }
  163. template<typename TChunk, typename TInterface, typename TLock>
  164. void StackAllocatorBuilder<TChunk, TInterface, TLock>::reset()
  165. {
  166. m_crntChunk.setNonAtomically(nullptr);
  167. m_chunksInUse = 0;
  168. // Reset allocation count and do some error checks
  169. Atomic<U32>* allocationCount = m_interface.getAllocationCount();
  170. if(allocationCount)
  171. {
  172. const U32 allocCount = allocationCount->exchange(0);
  173. if(!m_interface.ignoreDeallocationErrors() && allocCount != 0)
  174. {
  175. ANKI_UTIL_LOGW("Forgot to deallocate");
  176. }
  177. }
  178. }
  179. } // end namespace anki