설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AutoFreeAllocator.cs 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. using System;
  2. using System.Diagnostics;
  3. using System.Threading;
  4. using AOT;
  5. using Unity.Burst;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using Unity.Mathematics;
  8. namespace Unity.Collections
  9. {
  10. unsafe internal struct ArrayOfArrays<T> : IDisposable where T : unmanaged
  11. {
  12. AllocatorManager.AllocatorHandle m_backingAllocatorHandle;
  13. int m_lengthInElements;
  14. int m_capacityInElements;
  15. int m_log2BlockSizeInElements;
  16. int m_blocks;
  17. IntPtr* m_block;
  18. int BlockSizeInElements => 1 << m_log2BlockSizeInElements;
  19. int BlockSizeInBytes => BlockSizeInElements * sizeof(T);
  20. int BlockMask => BlockSizeInElements - 1;
  21. public int Length => m_lengthInElements;
  22. public int Capacity => m_capacityInElements;
  23. public ArrayOfArrays(int capacityInElements, AllocatorManager.AllocatorHandle backingAllocatorHandle, int log2BlockSizeInElements = 12)
  24. {
  25. this = default;
  26. m_backingAllocatorHandle = backingAllocatorHandle;
  27. m_lengthInElements = 0;
  28. m_capacityInElements = capacityInElements;
  29. m_log2BlockSizeInElements = log2BlockSizeInElements;
  30. m_blocks = (capacityInElements + BlockMask) >> m_log2BlockSizeInElements;
  31. m_block = (IntPtr*)Memory.Unmanaged.Allocate(sizeof(IntPtr) * m_blocks, 16, m_backingAllocatorHandle);
  32. UnsafeUtility.MemSet(m_block, 0, sizeof(IntPtr) * m_blocks);
  33. }
  34. public void LockfreeAdd(T t)
  35. {
  36. var elementIndex = Interlocked.Increment(ref m_lengthInElements) - 1;
  37. var blockIndex = BlockIndexOfElement(elementIndex);
  38. CheckBlockIndex(blockIndex);
  39. if(m_block[blockIndex] == IntPtr.Zero)
  40. {
  41. void* pointer = Memory.Unmanaged.Allocate(BlockSizeInBytes, 16, m_backingAllocatorHandle); // $$$!
  42. var lastBlock = math.min(m_blocks, blockIndex + 4); // don't overgrow too fast, simply to avoid a $$$ free
  43. for(; blockIndex < lastBlock; ++blockIndex)
  44. if(IntPtr.Zero == Interlocked.CompareExchange(ref m_block[blockIndex], (IntPtr)pointer, IntPtr.Zero))
  45. break; // install the new block, into *any* empty slot available, to avoid wasting the time we spent on malloc
  46. if(blockIndex == lastBlock)
  47. Memory.Unmanaged.Free(pointer, m_backingAllocatorHandle); // $$$, only if absolutely necessary
  48. }
  49. this[elementIndex] = t;
  50. }
  51. public ref T this[int elementIndex]
  52. {
  53. get
  54. {
  55. CheckElementIndex(elementIndex);
  56. var blockIndex = BlockIndexOfElement(elementIndex);
  57. CheckBlockIndex(blockIndex);
  58. CheckBlockIsNotNull(blockIndex);
  59. IntPtr blockIntPtr = m_block[blockIndex];
  60. var elementIndexInBlock = elementIndex & BlockMask;
  61. T* blockPointer = (T*)blockIntPtr;
  62. return ref blockPointer[elementIndexInBlock];
  63. }
  64. }
  65. public void Rewind()
  66. {
  67. m_lengthInElements = 0;
  68. }
  69. public void Clear()
  70. {
  71. Rewind();
  72. for(var i = 0; i < m_blocks; ++i)
  73. if(m_block[i] != IntPtr.Zero)
  74. {
  75. Memory.Unmanaged.Free((void*)m_block[i], m_backingAllocatorHandle);
  76. m_block[i] = IntPtr.Zero;
  77. }
  78. }
  79. public void Dispose()
  80. {
  81. Clear();
  82. Memory.Unmanaged.Free(m_block, m_backingAllocatorHandle);
  83. }
  84. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  85. void CheckElementIndex(int elementIndex)
  86. {
  87. if (elementIndex >= m_lengthInElements)
  88. throw new ArgumentException($"Element index {elementIndex} must be less than length in elements {m_lengthInElements}.");
  89. }
  90. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  91. void CheckBlockIndex(int blockIndex)
  92. {
  93. if (blockIndex >= m_blocks)
  94. throw new ArgumentException($"Block index {blockIndex} must be less than number of blocks {m_blocks}.");
  95. }
  96. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  97. void CheckBlockIsNotNull(int blockIndex)
  98. {
  99. if(m_block[blockIndex] == IntPtr.Zero)
  100. throw new ArgumentException($"Block index {blockIndex} is a null pointer.");
  101. }
  102. public void RemoveAtSwapBack(int elementIndex)
  103. {
  104. this[elementIndex] = this[Length-1];
  105. --m_lengthInElements;
  106. }
  107. int BlockIndexOfElement(int elementIndex)
  108. {
  109. return elementIndex >> m_log2BlockSizeInElements;
  110. }
  111. public void TrimExcess()
  112. {
  113. for(var blockIndex = BlockIndexOfElement(m_lengthInElements + BlockMask); blockIndex < m_blocks; ++blockIndex)
  114. {
  115. CheckBlockIndex(blockIndex);
  116. if(m_block[blockIndex] != IntPtr.Zero)
  117. {
  118. var blockIntPtr = m_block[blockIndex];
  119. void* blockPointer = (void*)blockIntPtr;
  120. Memory.Unmanaged.Free(blockPointer, m_backingAllocatorHandle);
  121. m_block[blockIndex] = IntPtr.Zero;
  122. }
  123. }
  124. }
  125. }
  126. [BurstCompile]
  127. internal struct AutoFreeAllocator : AllocatorManager.IAllocator
  128. {
  129. ArrayOfArrays<IntPtr> m_allocated;
  130. ArrayOfArrays<IntPtr> m_tofree;
  131. AllocatorManager.AllocatorHandle m_handle;
  132. AllocatorManager.AllocatorHandle m_backingAllocatorHandle;
  133. unsafe public void Update()
  134. {
  135. for(var i = m_tofree.Length; i --> 0;)
  136. for(var j = m_allocated.Length; j --> 0;)
  137. if(m_allocated[j] == m_tofree[i])
  138. {
  139. Memory.Unmanaged.Free((void*)m_tofree[i], m_backingAllocatorHandle);
  140. m_allocated.RemoveAtSwapBack(j);
  141. break;
  142. }
  143. m_tofree.Rewind();
  144. m_allocated.TrimExcess();
  145. }
  146. unsafe public void Initialize(AllocatorManager.AllocatorHandle backingAllocatorHandle)
  147. {
  148. m_allocated = new ArrayOfArrays<IntPtr>(1024 * 1024, backingAllocatorHandle);
  149. m_tofree = new ArrayOfArrays<IntPtr>(128 * 1024, backingAllocatorHandle);
  150. m_backingAllocatorHandle = backingAllocatorHandle;
  151. }
  152. unsafe public void FreeAll()
  153. {
  154. Update();
  155. m_handle.Rewind();
  156. for(var i = 0; i < m_allocated.Length; ++i)
  157. Memory.Unmanaged.Free((void*) m_allocated[i], m_backingAllocatorHandle);
  158. m_allocated.Rewind();
  159. }
  160. /// <summary>
  161. /// Dispose the allocator. This must be called to free the memory blocks that were allocated from the system.
  162. /// </summary>
  163. public void Dispose()
  164. {
  165. FreeAll();
  166. m_tofree.Dispose();
  167. m_allocated.Dispose();
  168. }
  169. /// <summary>
  170. /// The allocator function. It can allocate, deallocate, or reallocate.
  171. /// </summary>
  172. public AllocatorManager.TryFunction Function => Try;
  173. /// <summary>
  174. /// Invoke the allocator function.
  175. /// </summary>
  176. /// <param name="block">The block to allocate, deallocate, or reallocate. See <see cref="AllocatorManager.Try"/></param>
  177. /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
  178. public int Try(ref AllocatorManager.Block block)
  179. {
  180. unsafe
  181. {
  182. if (block.Range.Pointer == IntPtr.Zero)
  183. {
  184. if (block.Bytes == 0)
  185. {
  186. return 0;
  187. }
  188. var ptr = (byte*)Memory.Unmanaged.Allocate(block.Bytes, block.Alignment, m_backingAllocatorHandle);
  189. block.Range.Pointer = (IntPtr)ptr;
  190. block.AllocatedItems = block.Range.Items;
  191. m_allocated.LockfreeAdd(block.Range.Pointer);
  192. return 0;
  193. }
  194. if (block.Range.Items == 0)
  195. {
  196. m_tofree.LockfreeAdd(block.Range.Pointer);
  197. block.Range.Pointer = IntPtr.Zero;
  198. block.AllocatedItems = 0;
  199. return 0;
  200. }
  201. return -1;
  202. }
  203. }
  204. [BurstCompile]
  205. [MonoPInvokeCallback(typeof(AllocatorManager.TryFunction))]
  206. internal static int Try(IntPtr state, ref AllocatorManager.Block block)
  207. {
  208. unsafe { return ((AutoFreeAllocator*)state)->Try(ref block); }
  209. }
  210. /// <summary>
  211. /// This allocator.
  212. /// </summary>
  213. /// <value>This allocator.</value>
  214. public AllocatorManager.AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
  215. /// <summary>
  216. /// Cast the Allocator index into Allocator
  217. /// </summary>
  218. public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
  219. /// <summary>
  220. /// Check whether an allocator is a custom allocator
  221. /// </summary>
  222. public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
  223. /// <summary>
  224. /// Check whether this allocator will automatically dispose allocations.
  225. /// </summary>
  226. /// <remarks>Allocations made by Auto free allocator are automatically disposed.</remarks>
  227. /// <value>Always true</value>
  228. public bool IsAutoDispose { get { return true; } }
  229. }
  230. }