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