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