No Description
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.

NativeQueue.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Threading;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. using Unity.Burst;
  6. using Unity.Jobs;
  7. using Unity.Jobs.LowLevel.Unsafe;
  8. using System.Diagnostics;
  9. using System.Runtime.CompilerServices;
  10. using System.Collections.Generic;
  11. using System.Collections;
  12. namespace Unity.Collections
  13. {
  14. /// <summary>
  15. /// An unmanaged queue.
  16. /// </summary>
  17. /// <typeparam name="T">The type of the elements.</typeparam>
  18. [StructLayout(LayoutKind.Sequential)]
  19. [NativeContainer]
  20. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
  21. public unsafe struct NativeQueue<T>
  22. : INativeDisposable
  23. where T : unmanaged
  24. {
  25. [NativeDisableUnsafePtrRestriction]
  26. UnsafeQueue<T>* m_Queue;
  27. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  28. AtomicSafetyHandle m_Safety;
  29. static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeQueue<T>>();
  30. #endif
  31. /// <summary>
  32. /// Initializes and returns an instance of NativeQueue.
  33. /// </summary>
  34. /// <param name="allocator">The allocator to use.</param>
  35. public NativeQueue(AllocatorManager.AllocatorHandle allocator)
  36. {
  37. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  38. m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
  39. CollectionHelper.InitNativeContainer<T>(m_Safety);
  40. CollectionHelper.SetStaticSafetyId<NativeQueue<T>>(ref m_Safety, ref s_staticSafetyId.Data);
  41. #endif
  42. m_Queue = UnsafeQueue<T>.Alloc(allocator);
  43. *m_Queue = new UnsafeQueue<T>(allocator);
  44. }
  45. /// <summary>
  46. /// Returns true if this queue is empty.
  47. /// </summary>
  48. /// <returns>True if this queue has no items or if the queue has not been constructed.</returns>
  49. public readonly bool IsEmpty()
  50. {
  51. if (IsCreated)
  52. {
  53. CheckRead();
  54. return m_Queue->IsEmpty();
  55. }
  56. return true;
  57. }
  58. /// <summary>
  59. /// Returns the current number of elements in this queue.
  60. /// </summary>
  61. /// <remarks>Note that getting the count requires traversing the queue's internal linked list of blocks.
  62. /// Where possible, cache this value instead of reading the property repeatedly.</remarks>
  63. /// <returns>The current number of elements in this queue.</returns>
  64. public readonly int Count
  65. {
  66. get
  67. {
  68. CheckRead();
  69. return m_Queue->Count;
  70. }
  71. }
  72. /// <summary>
  73. /// Returns the element at the end of this queue without removing it.
  74. /// </summary>
  75. /// <returns>The element at the end of this queue.</returns>
  76. public T Peek()
  77. {
  78. CheckRead();
  79. return m_Queue->Peek();
  80. }
  81. /// <summary>
  82. /// Adds an element at the front of this queue.
  83. /// </summary>
  84. /// <param name="value">The value to be enqueued.</param>
  85. public void Enqueue(T value)
  86. {
  87. CheckWrite();
  88. m_Queue->Enqueue(value);
  89. }
  90. /// <summary>
  91. /// Removes and returns the element at the end of this queue.
  92. /// </summary>
  93. /// <exception cref="InvalidOperationException">Thrown if this queue is empty.</exception>
  94. /// <returns>The element at the end of this queue.</returns>
  95. public T Dequeue()
  96. {
  97. CheckWrite();
  98. return m_Queue->Dequeue();
  99. }
  100. /// <summary>
  101. /// Removes and outputs the element at the end of this queue.
  102. /// </summary>
  103. /// <param name="item">Outputs the removed element.</param>
  104. /// <returns>True if this queue was not empty.</returns>
  105. public bool TryDequeue(out T item)
  106. {
  107. CheckWrite();
  108. return m_Queue->TryDequeue(out item);
  109. }
  110. /// <summary>
  111. /// Returns an array containing a copy of this queue's content.
  112. /// </summary>
  113. /// <param name="allocator">The allocator to use.</param>
  114. /// <returns>An array containing a copy of this queue's content. The elements are ordered in the same order they were
  115. /// enqueued, *e.g.* the earliest enqueued element is copied to index 0 of the array.</returns>
  116. public NativeArray<T> ToArray(AllocatorManager.AllocatorHandle allocator)
  117. {
  118. CheckRead();
  119. return m_Queue->ToArray(allocator);
  120. }
  121. /// <summary>
  122. /// Removes all elements of this queue.
  123. /// </summary>
  124. public void Clear()
  125. {
  126. CheckWrite();
  127. m_Queue->Clear();
  128. }
  129. /// <summary>
  130. /// Whether this queue has been allocated (and not yet deallocated).
  131. /// </summary>
  132. /// <value>True if this queue has been allocated (and not yet deallocated).</value>
  133. public readonly bool IsCreated
  134. {
  135. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  136. get => m_Queue != null && m_Queue->IsCreated;
  137. }
  138. /// <summary>
  139. /// Releases all resources (memory and safety handles).
  140. /// </summary>
  141. public void Dispose()
  142. {
  143. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  144. if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
  145. {
  146. AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
  147. }
  148. #endif
  149. if (!IsCreated)
  150. {
  151. return;
  152. }
  153. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  154. CollectionHelper.DisposeSafetyHandle(ref m_Safety);
  155. #endif
  156. UnsafeQueue<T>.Free(m_Queue);
  157. m_Queue = null;
  158. }
  159. /// <summary>
  160. /// Creates and schedules a job that releases all resources (memory and safety handles) of this queue.
  161. /// </summary>
  162. /// <param name="inputDeps">The dependency for the new job.</param>
  163. /// <returns>The handle of the new job. The job depends upon `inputDeps` and releases all resources (memory and safety handles) of this queue.</returns>
  164. public JobHandle Dispose(JobHandle inputDeps)
  165. {
  166. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  167. if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
  168. {
  169. AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
  170. }
  171. #endif
  172. if (!IsCreated)
  173. {
  174. return inputDeps;
  175. }
  176. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  177. var jobHandle = new NativeQueueDisposeJob { Data = new NativeQueueDispose { m_QueueData = (UnsafeQueue<int>*)m_Queue, m_Safety = m_Safety } }.Schedule(inputDeps);
  178. AtomicSafetyHandle.Release(m_Safety);
  179. #else
  180. var jobHandle = new NativeQueueDisposeJob { Data = new NativeQueueDispose { m_QueueData = (UnsafeQueue<int>*)m_Queue } }.Schedule(inputDeps);
  181. #endif
  182. m_Queue = null;
  183. return jobHandle;
  184. }
  185. /// <summary>
  186. /// An enumerator over the values of a container.
  187. /// </summary>
  188. /// <remarks>
  189. /// In an enumerator's initial state, <see cref="Current"/> is invalid.
  190. /// The first <see cref="MoveNext"/> call advances the enumerator to the first value.
  191. /// </remarks>
  192. [NativeContainer]
  193. [NativeContainerIsReadOnly]
  194. public struct Enumerator : IEnumerator<T>
  195. {
  196. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  197. internal AtomicSafetyHandle m_Safety;
  198. #endif
  199. internal UnsafeQueue<T>.Enumerator m_Enumerator;
  200. /// <summary>
  201. /// Does nothing.
  202. /// </summary>
  203. public void Dispose() { }
  204. /// <summary>
  205. /// Advances the enumerator to the next value.
  206. /// </summary>
  207. /// <returns>True if `Current` is valid to read after the call.</returns>
  208. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  209. public bool MoveNext()
  210. {
  211. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  212. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  213. #endif
  214. return m_Enumerator.MoveNext();
  215. }
  216. /// <summary>
  217. /// Resets the enumerator to its initial state.
  218. /// </summary>
  219. public void Reset()
  220. {
  221. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  222. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  223. #endif
  224. m_Enumerator.Reset();
  225. }
  226. /// <summary>
  227. /// The current value.
  228. /// </summary>
  229. /// <value>The current value.</value>
  230. public T Current
  231. {
  232. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  233. get => m_Enumerator.Current;
  234. }
  235. object IEnumerator.Current => Current;
  236. }
  237. /// <summary>
  238. /// Returns a readonly version of this NativeQueue instance.
  239. /// </summary>
  240. /// <remarks>ReadOnly containers point to the same underlying data as the NativeQueue it is made from.</remarks>
  241. /// <returns>ReadOnly instance for this.</returns>
  242. public ReadOnly AsReadOnly()
  243. {
  244. return new ReadOnly(ref this);
  245. }
  246. /// <summary>
  247. /// A read-only alias for the value of a NativeQueue. Does not have its own allocated storage.
  248. /// </summary>
  249. [NativeContainer]
  250. [NativeContainerIsReadOnly]
  251. public struct ReadOnly
  252. : IEnumerable<T>
  253. {
  254. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  255. AtomicSafetyHandle m_Safety;
  256. internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ReadOnly>();
  257. #endif
  258. UnsafeQueue<T>.ReadOnly m_ReadOnly;
  259. internal ReadOnly(ref NativeQueue<T> data)
  260. {
  261. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  262. m_Safety = data.m_Safety;
  263. CollectionHelper.SetStaticSafetyId<ReadOnly>(ref m_Safety, ref s_staticSafetyId.Data);
  264. #endif
  265. m_ReadOnly = new UnsafeQueue<T>.ReadOnly(ref *data.m_Queue);
  266. }
  267. /// <summary>
  268. /// Whether this container been allocated (and not yet deallocated).
  269. /// </summary>
  270. /// <value>True if this container has been allocated (and not yet deallocated).</value>
  271. public readonly bool IsCreated
  272. {
  273. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  274. get => m_ReadOnly.IsCreated;
  275. }
  276. /// <summary>
  277. /// Returns true if this queue is empty.
  278. /// </summary>
  279. /// <remarks>Note that getting the count requires traversing the queue's internal linked list of blocks.
  280. /// Where possible, cache this value instead of reading the property repeatedly.</remarks>
  281. /// <returns>True if this queue has no items or if the queue has not been constructed.</returns>
  282. public readonly bool IsEmpty()
  283. {
  284. CheckRead();
  285. return m_ReadOnly.IsEmpty();
  286. }
  287. /// <summary>
  288. /// Returns the current number of elements in this queue.
  289. /// </summary>
  290. /// <remarks>Note that getting the count requires traversing the queue's internal linked list of blocks.
  291. /// Where possible, cache this value instead of reading the property repeatedly.</remarks>
  292. /// <returns>The current number of elements in this queue.</returns>
  293. public readonly int Count
  294. {
  295. get
  296. {
  297. CheckRead();
  298. return m_ReadOnly.Count;
  299. }
  300. }
  301. /// <summary>
  302. /// The element at an index.
  303. /// </summary>
  304. /// <param name="index">An index.</param>
  305. /// <value>The element at the index.</value>
  306. /// <exception cref="IndexOutOfRangeException">Thrown if the index is out of bounds.</exception>
  307. public readonly T this[int index]
  308. {
  309. get
  310. {
  311. CheckRead();
  312. return m_ReadOnly[index];
  313. }
  314. }
  315. /// <summary>
  316. /// Returns an enumerator over the items of this container.
  317. /// </summary>
  318. /// <returns>An enumerator over the items of this container.</returns>
  319. public readonly Enumerator GetEnumerator()
  320. {
  321. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  322. var ash = m_Safety;
  323. AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(ash);
  324. AtomicSafetyHandle.UseSecondaryVersion(ref ash);
  325. #endif
  326. return new Enumerator
  327. {
  328. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  329. m_Safety = ash,
  330. #endif
  331. m_Enumerator = m_ReadOnly.GetEnumerator(),
  332. };
  333. }
  334. /// <summary>
  335. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  336. /// </summary>
  337. /// <returns>Throws NotImplementedException.</returns>
  338. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  339. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  340. {
  341. throw new NotImplementedException();
  342. }
  343. /// <summary>
  344. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  345. /// </summary>
  346. /// <returns>Throws NotImplementedException.</returns>
  347. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  348. IEnumerator IEnumerable.GetEnumerator()
  349. {
  350. throw new NotImplementedException();
  351. }
  352. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  353. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  354. readonly void CheckRead()
  355. {
  356. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  357. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  358. #endif
  359. }
  360. }
  361. /// <summary>
  362. /// Returns a parallel writer for this queue.
  363. /// </summary>
  364. /// <returns>A parallel writer for this queue.</returns>
  365. public ParallelWriter AsParallelWriter()
  366. {
  367. ParallelWriter writer;
  368. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  369. writer.m_Safety = m_Safety;
  370. CollectionHelper.SetStaticSafetyId<ParallelWriter>(ref writer.m_Safety, ref ParallelWriter.s_staticSafetyId.Data);
  371. #endif
  372. writer.unsafeWriter = m_Queue->AsParallelWriter();
  373. return writer;
  374. }
  375. /// <summary>
  376. /// A parallel writer for a NativeQueue.
  377. /// </summary>
  378. /// <remarks>
  379. /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a NativeQueue.
  380. /// </remarks>
  381. [NativeContainer]
  382. [NativeContainerIsAtomicWriteOnly]
  383. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
  384. public unsafe struct ParallelWriter
  385. {
  386. internal UnsafeQueue<T>.ParallelWriter unsafeWriter;
  387. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  388. internal AtomicSafetyHandle m_Safety;
  389. internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ParallelWriter>();
  390. #endif
  391. /// <summary>
  392. /// Adds an element at the front of the queue.
  393. /// </summary>
  394. /// <param name="value">The value to be enqueued.</param>
  395. public void Enqueue(T value)
  396. {
  397. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  398. AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
  399. #endif
  400. unsafeWriter.Enqueue(value);
  401. }
  402. /// <summary>
  403. /// Adds an element at the front of the queue.
  404. /// </summary>
  405. /// <param name="value">The value to be enqueued.</param>
  406. /// <param name="threadIndexOverride">The thread index which must be set by a field from a job struct with the <see cref="NativeSetThreadIndexAttribute"/> attribute.</param>
  407. internal void Enqueue(T value, int threadIndexOverride)
  408. {
  409. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  410. AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
  411. #endif
  412. unsafeWriter.Enqueue(value, threadIndexOverride);
  413. }
  414. }
  415. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  416. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  417. readonly void CheckRead()
  418. {
  419. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  420. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  421. #endif
  422. }
  423. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  424. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  425. void CheckWrite()
  426. {
  427. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  428. AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
  429. #endif
  430. }
  431. }
  432. [NativeContainer]
  433. [GenerateTestsForBurstCompatibility]
  434. internal unsafe struct NativeQueueDispose
  435. {
  436. [NativeDisableUnsafePtrRestriction]
  437. public UnsafeQueue<int>* m_QueueData;
  438. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  439. internal AtomicSafetyHandle m_Safety;
  440. #endif
  441. public void Dispose()
  442. {
  443. UnsafeQueue<int>.Free(m_QueueData);
  444. }
  445. }
  446. [BurstCompile]
  447. struct NativeQueueDisposeJob : IJob
  448. {
  449. public NativeQueueDispose Data;
  450. public void Execute()
  451. {
  452. Data.Dispose();
  453. }
  454. }
  455. }