A game about forced loneliness, made by TACStudios
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}