暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

UnsafeRingQueue.cs 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. using System;
  2. using System.Diagnostics;
  3. using Unity.Jobs;
  4. using Unity.Mathematics;
  5. namespace Unity.Collections.LowLevel.Unsafe
  6. {
  7. [BurstCompatible]
  8. internal struct RingControl
  9. {
  10. internal RingControl(int capacity)
  11. {
  12. Capacity = capacity;
  13. Current = 0;
  14. Write = 0;
  15. Read = 0;
  16. }
  17. internal void Reset()
  18. {
  19. Current = 0;
  20. Write = 0;
  21. Read = 0;
  22. }
  23. internal int Distance(int from, int to)
  24. {
  25. var diff = to - from;
  26. return diff < 0 ? Capacity - math.abs(diff) : diff;
  27. }
  28. internal int Available()
  29. {
  30. return Distance(Read, Current);
  31. }
  32. internal int Reserve(int count)
  33. {
  34. var dist = Distance(Write, Read) - 1;
  35. var maxCount = dist < 0 ? Capacity - 1 : dist;
  36. var absCount = math.abs(count);
  37. var test = absCount - maxCount;
  38. count = test < 0 ? count : maxCount;
  39. Write = (Write + count) % Capacity;
  40. return count;
  41. }
  42. internal int Commit(int count)
  43. {
  44. var maxCount = Distance(Current, Write);
  45. var absCount = math.abs(count);
  46. var test = absCount - maxCount;
  47. count = test < 0 ? count : maxCount;
  48. Current = (Current + count) % Capacity;
  49. return count;
  50. }
  51. internal int Consume(int count)
  52. {
  53. var maxCount = Distance(Read, Current);
  54. var absCount = math.abs(count);
  55. var test = absCount - maxCount;
  56. count = test < 0 ? count : maxCount;
  57. Read = (Read + count) % Capacity;
  58. return count;
  59. }
  60. internal int Length => Distance(Read, Write);
  61. internal readonly int Capacity;
  62. internal int Current;
  63. internal int Write;
  64. internal int Read;
  65. }
  66. /// <summary>
  67. /// A fixed-size circular buffer.
  68. /// </summary>
  69. /// <typeparam name="T">The type of the elements.</typeparam>
  70. [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
  71. [DebuggerTypeProxy(typeof(UnsafeRingQueueDebugView<>))]
  72. [BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
  73. public unsafe struct UnsafeRingQueue<T>
  74. : INativeDisposable
  75. where T : unmanaged
  76. {
  77. /// <summary>
  78. /// The internal buffer where the content is stored.
  79. /// </summary>
  80. /// <value>The internal buffer where the content is stored.</value>
  81. [NativeDisableUnsafePtrRestriction]
  82. public T* Ptr;
  83. /// <summary>
  84. /// The allocator used to create the internal buffer.
  85. /// </summary>
  86. /// <value>The allocator used to create the internal buffer.</value>
  87. public AllocatorManager.AllocatorHandle Allocator;
  88. internal RingControl Control;
  89. /// <summary>
  90. /// Whether the queue is empty.
  91. /// </summary>
  92. /// <value>True if the queue is empty or the queue has not been constructed.</value>
  93. public bool IsEmpty => !IsCreated || Length == 0;
  94. /// <summary>
  95. /// The number of elements currently in this queue.
  96. /// </summary>
  97. /// <value>The number of elements currently in this queue.</value>
  98. public int Length => Control.Length;
  99. /// <summary>
  100. /// The number of elements that fit in the internal buffer.
  101. /// </summary>
  102. /// <value>The number of elements that fit in the internal buffer.</value>
  103. public int Capacity => Control.Capacity;
  104. /// <summary>
  105. /// Initializes and returns an instance of UnsafeRingQueue which aliasing an existing buffer.
  106. /// </summary>
  107. /// <param name="ptr">An existing buffer to set as the internal buffer.</param>
  108. /// <param name="capacity">The capacity.</param>
  109. public UnsafeRingQueue(T* ptr, int capacity)
  110. {
  111. Ptr = ptr;
  112. Allocator = AllocatorManager.None;
  113. Control = new RingControl(capacity);
  114. }
  115. /// <summary>
  116. /// Initializes and returns an instance of UnsafeRingQueue.
  117. /// </summary>
  118. /// <param name="capacity">The capacity.</param>
  119. /// <param name="allocator">The allocator to use.</param>
  120. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  121. public UnsafeRingQueue(int capacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
  122. {
  123. capacity += 1;
  124. Allocator = allocator;
  125. Control = new RingControl(capacity);
  126. var sizeInBytes = capacity * UnsafeUtility.SizeOf<T>();
  127. Ptr = (T*)Memory.Unmanaged.Allocate(sizeInBytes, 16, allocator);
  128. if (options == NativeArrayOptions.ClearMemory)
  129. {
  130. UnsafeUtility.MemClear(Ptr, sizeInBytes);
  131. }
  132. }
  133. /// <summary>
  134. /// Whether this queue has been allocated (and not yet deallocated).
  135. /// </summary>
  136. /// <value>True if this queue has been allocated (and not yet deallocated).</value>
  137. public bool IsCreated => Ptr != null;
  138. /// <summary>
  139. /// Releases all resources (memory and safety handles).
  140. /// </summary>
  141. public void Dispose()
  142. {
  143. if (CollectionHelper.ShouldDeallocate(Allocator))
  144. {
  145. Memory.Unmanaged.Free(Ptr, Allocator);
  146. Allocator = AllocatorManager.Invalid;
  147. }
  148. Ptr = null;
  149. }
  150. /// <summary>
  151. /// Creates and schedules a job that will dispose this queue.
  152. /// </summary>
  153. /// <param name="inputDeps">The handle of a job which the new job will depend upon.</param>
  154. /// <returns>The handle of a new job that will dispose this queue. The new job depends upon inputDeps.</returns>
  155. [NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
  156. public JobHandle Dispose(JobHandle inputDeps)
  157. {
  158. if (CollectionHelper.ShouldDeallocate(Allocator))
  159. {
  160. var jobHandle = new UnsafeDisposeJob { Ptr = Ptr, Allocator = Allocator }.Schedule(inputDeps);
  161. Ptr = null;
  162. Allocator = AllocatorManager.Invalid;
  163. return jobHandle;
  164. }
  165. Ptr = null;
  166. return inputDeps;
  167. }
  168. /// <summary>
  169. /// Adds an element at the front of the queue.
  170. /// </summary>
  171. /// <remarks>Does nothing if the queue is full.</remarks>
  172. /// <param name="value">The value to be added.</param>
  173. /// <returns>True if the value was added.</returns>
  174. public bool TryEnqueue(T value)
  175. {
  176. if (1 != Control.Reserve(1))
  177. {
  178. return false;
  179. }
  180. Ptr[Control.Current] = value;
  181. Control.Commit(1);
  182. return true;
  183. }
  184. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  185. static void ThrowQueueFull()
  186. {
  187. throw new InvalidOperationException("Trying to enqueue into full queue.");
  188. }
  189. /// <summary>
  190. /// Adds an element at the front of the queue.
  191. /// </summary>
  192. /// <param name="value">The value to be added.</param>
  193. /// <exception cref="InvalidOperationException">Thrown if the queue was full.</exception>
  194. public void Enqueue(T value)
  195. {
  196. if (!TryEnqueue(value))
  197. {
  198. ThrowQueueFull();
  199. }
  200. }
  201. /// <summary>
  202. /// Removes the element from the end of the queue.
  203. /// </summary>
  204. /// <remarks>Does nothing if the queue is empty.</remarks>
  205. /// <param name="item">Outputs the element removed.</param>
  206. /// <returns>True if an element was removed.</returns>
  207. public bool TryDequeue(out T item)
  208. {
  209. item = Ptr[Control.Read];
  210. return 1 == Control.Consume(1);
  211. }
  212. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  213. static void ThrowQueueEmpty()
  214. {
  215. throw new InvalidOperationException("Trying to dequeue from an empty queue");
  216. }
  217. /// <summary>
  218. /// Removes the element from the end of the queue.
  219. /// </summary>
  220. /// <exception cref="InvalidOperationException">Thrown if the queue was empty.</exception>
  221. /// <returns>Returns the removed element.</returns>
  222. public T Dequeue()
  223. {
  224. if (!TryDequeue(out T item))
  225. {
  226. ThrowQueueEmpty();
  227. }
  228. return item;
  229. }
  230. }
  231. internal sealed class UnsafeRingQueueDebugView<T>
  232. where T : unmanaged
  233. {
  234. UnsafeRingQueue<T> Data;
  235. public UnsafeRingQueueDebugView(UnsafeRingQueue<T> data)
  236. {
  237. Data = data;
  238. }
  239. public unsafe T[] Items
  240. {
  241. get
  242. {
  243. T[] result = new T[Data.Length];
  244. var read = Data.Control.Read;
  245. var capacity = Data.Control.Capacity;
  246. for (var i = 0; i < result.Length; ++i)
  247. {
  248. result[i] = Data.Ptr[(read + i) % capacity];
  249. }
  250. return result;
  251. }
  252. }
  253. }
  254. }