설명 없음
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.

NativeRingQueue.cs 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.CompilerServices;
  4. using System.Runtime.InteropServices;
  5. using Unity.Burst;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using Unity.Jobs;
  8. namespace Unity.Collections
  9. {
  10. /// <summary>
  11. /// A fixed-size circular buffer. For single-threaded uses only.
  12. /// </summary>
  13. /// <remarks>
  14. /// This container can't be used in parallel jobs, just on single-thread (for example: main thread, or single IJob).
  15. /// </remarks>
  16. /// <typeparam name="T">The type of the elements.</typeparam>
  17. [StructLayout(LayoutKind.Sequential)]
  18. [NativeContainer]
  19. [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
  20. [DebuggerTypeProxy(typeof(NativeRingQueueDebugView<>))]
  21. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  22. public unsafe struct NativeRingQueue<T>
  23. : INativeDisposable
  24. where T : unmanaged
  25. {
  26. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  27. internal AtomicSafetyHandle m_Safety;
  28. static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeRingQueue<T>>();
  29. #endif
  30. [NativeDisableUnsafePtrRestriction]
  31. internal UnsafeRingQueue<T>* m_RingQueue;
  32. /// <summary>
  33. /// Whether this queue has been allocated (and not yet deallocated).
  34. /// </summary>
  35. /// <value>True if this queue has been allocated (and not yet deallocated).</value>
  36. public readonly bool IsCreated
  37. {
  38. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  39. get => m_RingQueue != null && m_RingQueue->IsCreated;
  40. }
  41. /// <summary>
  42. /// Whether the queue is empty.
  43. /// </summary>
  44. /// <value>True if the queue is empty or the queue has not been constructed.</value>
  45. public readonly bool IsEmpty
  46. {
  47. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  48. get => m_RingQueue == null || m_RingQueue->Length == 0;
  49. }
  50. /// <summary>
  51. /// The number of elements currently in this queue.
  52. /// </summary>
  53. /// <value>The number of elements currently in this queue.</value>
  54. public readonly int Length
  55. {
  56. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  57. get
  58. {
  59. CheckRead();
  60. return CollectionHelper.AssumePositive(m_RingQueue->Length);
  61. }
  62. }
  63. /// <summary>
  64. /// The number of elements that fit in the internal buffer.
  65. /// </summary>
  66. /// <value>The number of elements that fit in the internal buffer.</value>
  67. public readonly int Capacity
  68. {
  69. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  70. get
  71. {
  72. CheckRead();
  73. return CollectionHelper.AssumePositive(m_RingQueue->Capacity);
  74. }
  75. }
  76. /// <summary>
  77. /// Initializes and returns an instance of NativeRingQueue.
  78. /// </summary>
  79. /// <param name="capacity">The capacity.</param>
  80. /// <param name="allocator">The allocator to use.</param>
  81. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  82. public NativeRingQueue(int capacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
  83. {
  84. CollectionHelper.CheckAllocator(allocator);
  85. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  86. m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
  87. CollectionHelper.SetStaticSafetyId<NativeRingQueue<T>>(ref m_Safety, ref s_staticSafetyId.Data);
  88. #endif
  89. m_RingQueue = UnsafeRingQueue<T>.Alloc(allocator);
  90. *m_RingQueue = new UnsafeRingQueue<T>(capacity, allocator, options);
  91. }
  92. /// <summary>
  93. /// Releases all resources (memory and safety handles).
  94. /// </summary>
  95. public void Dispose()
  96. {
  97. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  98. if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
  99. {
  100. AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
  101. }
  102. #endif
  103. if (!IsCreated)
  104. {
  105. return;
  106. }
  107. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  108. CollectionHelper.DisposeSafetyHandle(ref m_Safety);
  109. #endif
  110. UnsafeRingQueue<T>.Free(m_RingQueue);
  111. m_RingQueue = null;
  112. }
  113. /// <summary>
  114. /// Creates and schedules a job that will dispose this queue.
  115. /// </summary>
  116. /// <param name="inputDeps">The handle of a job which the new job will depend upon.</param>
  117. /// <returns>The handle of a new job that will dispose this queue. The new job depends upon inputDeps.</returns>
  118. public JobHandle Dispose(JobHandle inputDeps)
  119. {
  120. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  121. if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
  122. {
  123. AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
  124. }
  125. #endif
  126. if (!IsCreated)
  127. {
  128. return inputDeps;
  129. }
  130. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  131. var jobHandle = new NativeRingQueueDisposeJob { Data = new NativeRingQueueDispose { m_QueueData = (UnsafeRingQueue<int>*)m_RingQueue, m_Safety = m_Safety } }.Schedule(inputDeps);
  132. AtomicSafetyHandle.Release(m_Safety);
  133. #else
  134. var jobHandle = new NativeRingQueueDisposeJob { Data = new NativeRingQueueDispose { m_QueueData = (UnsafeRingQueue<int>*)m_RingQueue } }.Schedule(inputDeps);
  135. #endif
  136. m_RingQueue = null;
  137. return jobHandle;
  138. }
  139. /// <summary>
  140. /// Adds an element at the front of the queue.
  141. /// </summary>
  142. /// <remarks>Does nothing if the queue is full.</remarks>
  143. /// <param name="value">The value to be added.</param>
  144. /// <returns>True if the value was added.</returns>
  145. public bool TryEnqueue(T value)
  146. {
  147. CheckWrite();
  148. return m_RingQueue->TryEnqueue(value);
  149. }
  150. /// <summary>
  151. /// Adds an element at the front of the queue.
  152. /// </summary>
  153. /// <param name="value">The value to be added.</param>
  154. /// <exception cref="InvalidOperationException">Thrown if the queue was full.</exception>
  155. public void Enqueue(T value)
  156. {
  157. CheckWrite();
  158. m_RingQueue->Enqueue(value);
  159. }
  160. /// <summary>
  161. /// Removes the element from the end of the queue.
  162. /// </summary>
  163. /// <remarks>Does nothing if the queue is empty.</remarks>
  164. /// <param name="item">Outputs the element removed.</param>
  165. /// <returns>True if an element was removed.</returns>
  166. public bool TryDequeue(out T item)
  167. {
  168. CheckRead();
  169. return m_RingQueue->TryDequeue(out item);
  170. }
  171. /// <summary>
  172. /// Removes the element from the end of the queue.
  173. /// </summary>
  174. /// <exception cref="InvalidOperationException">Thrown if the queue was empty.</exception>
  175. /// <returns>Returns the removed element.</returns>
  176. public T Dequeue()
  177. {
  178. CheckRead();
  179. return m_RingQueue->Dequeue();
  180. }
  181. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  182. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  183. readonly void CheckRead()
  184. {
  185. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  186. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  187. #endif
  188. }
  189. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  190. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  191. readonly void CheckWrite()
  192. {
  193. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  194. AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
  195. #endif
  196. }
  197. }
  198. internal unsafe sealed class NativeRingQueueDebugView<T>
  199. where T : unmanaged
  200. {
  201. UnsafeRingQueue<T>* Data;
  202. public NativeRingQueueDebugView(NativeRingQueue<T> data)
  203. {
  204. Data = data.m_RingQueue;
  205. }
  206. public T[] Items
  207. {
  208. get
  209. {
  210. T[] result = new T[Data->Length];
  211. var read = Data->m_Read;
  212. var capacity = Data->m_Capacity;
  213. for (var i = 0; i < result.Length; ++i)
  214. {
  215. result[i] = Data->Ptr[(read + i) % capacity];
  216. }
  217. return result;
  218. }
  219. }
  220. }
  221. [NativeContainer]
  222. [GenerateTestsForBurstCompatibility]
  223. internal unsafe struct NativeRingQueueDispose
  224. {
  225. [NativeDisableUnsafePtrRestriction]
  226. public UnsafeRingQueue<int>* m_QueueData;
  227. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  228. public AtomicSafetyHandle m_Safety;
  229. #endif
  230. public void Dispose()
  231. {
  232. UnsafeRingQueue<int>.Free(m_QueueData);
  233. }
  234. }
  235. [BurstCompile]
  236. internal unsafe struct NativeRingQueueDisposeJob : IJob
  237. {
  238. public NativeRingQueueDispose Data;
  239. public void Execute()
  240. {
  241. Data.Dispose();
  242. }
  243. }
  244. }