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
{
///
/// A fixed-size circular buffer. For single-threaded uses only.
///
///
/// This container can't be used in parallel jobs, just on single-thread (for example: main thread, or single IJob).
///
/// The type of the elements.
[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
: INativeDisposable
where T : unmanaged
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
internal AtomicSafetyHandle m_Safety;
static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate>();
#endif
[NativeDisableUnsafePtrRestriction]
internal UnsafeRingQueue* m_RingQueue;
///
/// Whether this queue has been allocated (and not yet deallocated).
///
/// True if this queue has been allocated (and not yet deallocated).
public readonly bool IsCreated
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => m_RingQueue != null && m_RingQueue->IsCreated;
}
///
/// Whether the queue is empty.
///
/// True if the queue is empty or the queue has not been constructed.
public readonly bool IsEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => m_RingQueue == null || m_RingQueue->Length == 0;
}
///
/// The number of elements currently in this queue.
///
/// The number of elements currently in this queue.
public readonly int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
CheckRead();
return CollectionHelper.AssumePositive(m_RingQueue->Length);
}
}
///
/// The number of elements that fit in the internal buffer.
///
/// The number of elements that fit in the internal buffer.
public readonly int Capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
CheckRead();
return CollectionHelper.AssumePositive(m_RingQueue->Capacity);
}
}
///
/// Initializes and returns an instance of NativeRingQueue.
///
/// The capacity.
/// The allocator to use.
/// Whether newly allocated bytes should be zeroed out.
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>(ref m_Safety, ref s_staticSafetyId.Data);
#endif
m_RingQueue = UnsafeRingQueue.Alloc(allocator);
*m_RingQueue = new UnsafeRingQueue(capacity, allocator, options);
}
///
/// Releases all resources (memory and safety handles).
///
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.Free(m_RingQueue);
m_RingQueue = null;
}
///
/// Creates and schedules a job that will dispose this queue.
///
/// The handle of a job which the new job will depend upon.
/// The handle of a new job that will dispose this queue. The new job depends upon inputDeps.
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*)m_RingQueue, m_Safety = m_Safety } }.Schedule(inputDeps);
AtomicSafetyHandle.Release(m_Safety);
#else
var jobHandle = new NativeRingQueueDisposeJob { Data = new NativeRingQueueDispose { m_QueueData = (UnsafeRingQueue*)m_RingQueue } }.Schedule(inputDeps);
#endif
m_RingQueue = null;
return jobHandle;
}
///
/// Adds an element at the front of the queue.
///
/// Does nothing if the queue is full.
/// The value to be added.
/// True if the value was added.
public bool TryEnqueue(T value)
{
CheckWrite();
return m_RingQueue->TryEnqueue(value);
}
///
/// Adds an element at the front of the queue.
///
/// The value to be added.
/// Thrown if the queue was full.
public void Enqueue(T value)
{
CheckWrite();
m_RingQueue->Enqueue(value);
}
///
/// Removes the element from the end of the queue.
///
/// Does nothing if the queue is empty.
/// Outputs the element removed.
/// True if an element was removed.
public bool TryDequeue(out T item)
{
CheckRead();
return m_RingQueue->TryDequeue(out item);
}
///
/// Removes the element from the end of the queue.
///
/// Thrown if the queue was empty.
/// Returns the removed element.
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
where T : unmanaged
{
UnsafeRingQueue* Data;
public NativeRingQueueDebugView(NativeRingQueue 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* m_QueueData;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
public AtomicSafetyHandle m_Safety;
#endif
public void Dispose()
{
UnsafeRingQueue.Free(m_QueueData);
}
}
[BurstCompile]
internal unsafe struct NativeRingQueueDisposeJob : IJob
{
public NativeRingQueueDispose Data;
public void Execute()
{
Data.Dispose();
}
}
}