273 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			273 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Diagnostics; | ||
|  | using System.Runtime.CompilerServices; | ||
|  | using System.Runtime.InteropServices; | ||
|  | using Unity.Burst; | ||
|  | using Unity.Collections.LowLevel.Unsafe; | ||
|  | using Unity.Jobs; | ||
|  | 
 | ||
|  | namespace Unity.Collections | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// A fixed-size circular buffer. For single-threaded uses only. | ||
|  |     /// </summary> | ||
|  |     /// <remarks> | ||
|  |     /// This container can't be used in parallel jobs, just on single-thread (for example: main thread, or single IJob). | ||
|  |     /// </remarks> | ||
|  |     /// <typeparam name="T">The type of the elements.</typeparam> | ||
|  |     [StructLayout(LayoutKind.Sequential)] | ||
|  |     [NativeContainer] | ||
|  |     [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")] | ||
|  |     [DebuggerTypeProxy(typeof(NativeRingQueueDebugView<>))] | ||
|  |     [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] | ||
|  |     public unsafe struct NativeRingQueue<T> | ||
|  |         : INativeDisposable | ||
|  |         where T : unmanaged | ||
|  |     { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         internal AtomicSafetyHandle m_Safety; | ||
|  |         static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeRingQueue<T>>(); | ||
|  | #endif | ||
|  |         [NativeDisableUnsafePtrRestriction] | ||
|  |         internal UnsafeRingQueue<T>* m_RingQueue; | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Whether this queue has been allocated (and not yet deallocated). | ||
|  |         /// </summary> | ||
|  |         /// <value>True if this queue has been allocated (and not yet deallocated).</value> | ||
|  |         public readonly bool IsCreated | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get => m_RingQueue != null && m_RingQueue->IsCreated; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Whether the queue is empty. | ||
|  |         /// </summary> | ||
|  |         /// <value>True if the queue is empty or the queue has not been constructed.</value> | ||
|  |         public readonly bool IsEmpty | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get => m_RingQueue == null || m_RingQueue->Length == 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// The number of elements currently in this queue. | ||
|  |         /// </summary> | ||
|  |         /// <value>The number of elements currently in this queue.</value> | ||
|  |         public readonly int Length | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get | ||
|  |             { | ||
|  |                 CheckRead(); | ||
|  |                 return CollectionHelper.AssumePositive(m_RingQueue->Length); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// The number of elements that fit in the internal buffer. | ||
|  |         /// </summary> | ||
|  |         /// <value>The number of elements that fit in the internal buffer.</value> | ||
|  |         public readonly int Capacity | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get | ||
|  |             { | ||
|  |                 CheckRead(); | ||
|  |                 return CollectionHelper.AssumePositive(m_RingQueue->Capacity); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Initializes and returns an instance of NativeRingQueue. | ||
|  |         /// </summary> | ||
|  |         /// <param name="capacity">The capacity.</param> | ||
|  |         /// <param name="allocator">The allocator to use.</param> | ||
|  |         /// <param name="options">Whether newly allocated bytes should be zeroed out.</param> | ||
|  |         public NativeRingQueue(int capacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) | ||
|  |         { | ||
|  |             CollectionHelper.CheckAllocator(allocator); | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             m_Safety = CollectionHelper.CreateSafetyHandle(allocator); | ||
|  |             CollectionHelper.SetStaticSafetyId<NativeRingQueue<T>>(ref m_Safety, ref s_staticSafetyId.Data); | ||
|  | #endif | ||
|  |             m_RingQueue = UnsafeRingQueue<T>.Alloc(allocator); | ||
|  |             *m_RingQueue = new UnsafeRingQueue<T>(capacity, allocator, options); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Releases all resources (memory and safety handles). | ||
|  |         /// </summary> | ||
|  |         public void Dispose() | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             if (!AtomicSafetyHandle.IsDefaultValue(m_Safety)) | ||
|  |             { | ||
|  |                 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); | ||
|  |             } | ||
|  | #endif | ||
|  |             if (!IsCreated) | ||
|  |             { | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             CollectionHelper.DisposeSafetyHandle(ref m_Safety); | ||
|  | #endif | ||
|  |             UnsafeRingQueue<T>.Free(m_RingQueue); | ||
|  |             m_RingQueue = null; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Creates and schedules a job that will dispose this queue. | ||
|  |         /// </summary> | ||
|  |         /// <param name="inputDeps">The handle of a job which the new job will depend upon.</param> | ||
|  |         /// <returns>The handle of a new job that will dispose this queue. The new job depends upon inputDeps.</returns> | ||
|  |         public JobHandle Dispose(JobHandle inputDeps) | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             if (!AtomicSafetyHandle.IsDefaultValue(m_Safety)) | ||
|  |             { | ||
|  |                 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); | ||
|  |             } | ||
|  | #endif | ||
|  |             if (!IsCreated) | ||
|  |             { | ||
|  |                 return inputDeps; | ||
|  |             } | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             var jobHandle = new NativeRingQueueDisposeJob { Data = new NativeRingQueueDispose { m_QueueData = (UnsafeRingQueue<int>*)m_RingQueue, m_Safety = m_Safety } }.Schedule(inputDeps); | ||
|  |             AtomicSafetyHandle.Release(m_Safety); | ||
|  | #else | ||
|  |             var jobHandle = new NativeRingQueueDisposeJob { Data = new NativeRingQueueDispose { m_QueueData = (UnsafeRingQueue<int>*)m_RingQueue } }.Schedule(inputDeps); | ||
|  | #endif | ||
|  |             m_RingQueue = null; | ||
|  | 
 | ||
|  |             return jobHandle; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Adds an element at the front of the queue. | ||
|  |         /// </summary> | ||
|  |         /// <remarks>Does nothing if the queue is full.</remarks> | ||
|  |         /// <param name="value">The value to be added.</param> | ||
|  |         /// <returns>True if the value was added.</returns> | ||
|  |         public bool TryEnqueue(T value) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  |             return m_RingQueue->TryEnqueue(value); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Adds an element at the front of the queue. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The value to be added.</param> | ||
|  |         /// <exception cref="InvalidOperationException">Thrown if the queue was full.</exception> | ||
|  |         public void Enqueue(T value) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  |             m_RingQueue->Enqueue(value); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Removes the element from the end of the queue. | ||
|  |         /// </summary> | ||
|  |         /// <remarks>Does nothing if the queue is empty.</remarks> | ||
|  |         /// <param name="item">Outputs the element removed.</param> | ||
|  |         /// <returns>True if an element was removed.</returns> | ||
|  |         public bool TryDequeue(out T item) | ||
|  |         { | ||
|  |             CheckRead(); | ||
|  |             return m_RingQueue->TryDequeue(out item); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Removes the element from the end of the queue. | ||
|  |         /// </summary> | ||
|  |         /// <exception cref="InvalidOperationException">Thrown if the queue was empty.</exception> | ||
|  |         /// <returns>Returns the removed element.</returns> | ||
|  |         public T Dequeue() | ||
|  |         { | ||
|  |             CheckRead(); | ||
|  |             return m_RingQueue->Dequeue(); | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         readonly void CheckRead() | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             AtomicSafetyHandle.CheckReadAndThrow(m_Safety); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         readonly void CheckWrite() | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); | ||
|  | #endif | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     internal unsafe sealed class NativeRingQueueDebugView<T> | ||
|  |         where T : unmanaged | ||
|  |     { | ||
|  |         UnsafeRingQueue<T>* Data; | ||
|  | 
 | ||
|  |         public NativeRingQueueDebugView(NativeRingQueue<T> data) | ||
|  |         { | ||
|  |             Data = data.m_RingQueue; | ||
|  |         } | ||
|  | 
 | ||
|  |         public T[] Items | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 T[] result = new T[Data->Length]; | ||
|  | 
 | ||
|  |                 var read = Data->m_Read; | ||
|  |                 var capacity = Data->m_Capacity; | ||
|  | 
 | ||
|  |                 for (var i = 0; i < result.Length; ++i) | ||
|  |                 { | ||
|  |                     result[i] = Data->Ptr[(read + i) % capacity]; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return result; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     [NativeContainer] | ||
|  |     [GenerateTestsForBurstCompatibility] | ||
|  |     internal unsafe struct NativeRingQueueDispose | ||
|  |     { | ||
|  |         [NativeDisableUnsafePtrRestriction] | ||
|  |         public UnsafeRingQueue<int>* m_QueueData; | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         public AtomicSafetyHandle m_Safety; | ||
|  | #endif | ||
|  | 
 | ||
|  |         public void Dispose() | ||
|  |         { | ||
|  |             UnsafeRingQueue<int>.Free(m_QueueData); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     [BurstCompile] | ||
|  |     internal unsafe struct NativeRingQueueDisposeJob : IJob | ||
|  |     { | ||
|  |         public NativeRingQueueDispose Data; | ||
|  | 
 | ||
|  |         public void Execute() | ||
|  |         { | ||
|  |             Data.Dispose(); | ||
|  |         } | ||
|  |     } | ||
|  | } |