private static void SplitQueue(UnsafeSPSCQueue *q) { //Wrap tail back to 0 for (int i = 0; i < 5; i++) { UnsafeSPSCQueue.Enqueue(q, 111); } //First half for (int i = 0; i < 5; i++) { UnsafeSPSCQueue.Enqueue(q, i); } //Move head by 5 for (int i = 0; i < 5; i++) { UnsafeSPSCQueue.Dequeue <int>(q); } //Second half (head and tail are now both 5) for (int i = 5; i < 10; i++) { UnsafeSPSCQueue.Enqueue(q, i); } //Circular buffer now "ends" in the middle of the underlying array }
public static void Clear(UnsafeSPSCQueue *queue) { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); queue->_headAndTail = new HeadAndTail(); }
/// <summary> /// Allocates a new SPSCRingbuffer. Capacity will be set to a power of 2. /// </summary> public static UnsafeSPSCQueue *Allocate <T>(int capacity) where T : unmanaged { if (capacity < 1) { throw new ArgumentOutOfRangeException(nameof(capacity), string.Format(ThrowHelper.ArgumentOutOfRange_MustBePositive, nameof(capacity))); } // Requires one extra element to distinguish between empty and full queue. capacity++; int stride = sizeof(T); var alignment = Memory.GetAlignment(stride); var sizeOfQueue = Memory.RoundToAlignment(sizeof(UnsafeSPSCQueue), alignment); var sizeOfArray = stride * capacity; var ptr = Memory.MallocAndZero(sizeOfQueue + sizeOfArray, alignment); UnsafeSPSCQueue *queue = (UnsafeSPSCQueue *)ptr; // initialize fixed buffer from same block of memory as the stack UnsafeBuffer.InitFixed(&queue->_items, (byte *)ptr + sizeOfQueue, capacity, stride); queue->_headAndTail = new HeadAndTail(); queue->_typeHandle = typeof(T).TypeHandle.Value; return(queue); }
public static int GetCapacity(UnsafeSPSCQueue *queue) { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); return(queue->_items.Length - 1); }
internal Enumerator(UnsafeSPSCQueue *queue) { _queue = queue; _index = -1; _current = default; _headStart = Volatile.Read(ref queue->_headAndTail.Head); _capacity = queue->_items.Length; }
/// <summary> /// Creates an enumerator for the current snapshot of the queue. /// </summary> public static Enumerator <T> GetEnumerator <T>(UnsafeSPSCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); return(new Enumerator <T>(queue)); }
public static bool IsEmpty <T>(UnsafeSPSCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); var nextHead = Volatile.Read(ref queue->_headAndTail.Head) + 1; return(Volatile.Read(ref queue->_headAndTail.Tail) < nextHead); }
public static void Free(UnsafeSPSCQueue *queue) { if (queue == null) { return; } // clear queue memory (just in case) *queue = default; // free queue memory, if this is a fixed queue it frees the items memory at the same time Memory.Free(queue); }
public static int GetCount(UnsafeSPSCQueue *queue) { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); var head = Volatile.Read(ref queue->_headAndTail.Head); var tail = Volatile.Read(ref queue->_headAndTail.Tail); var dif = tail - head; if (dif < 0) { dif += queue->_items.Length; } return(dif); }
/// <summary> /// Peeks the next item in the queue. Blocks the thread until an item is available. /// </summary> public static T Peek <T>(UnsafeSPSCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); SpinWait spinner = default; var head = Volatile.Read(ref queue->_headAndTail.Head); // Queue empty if (Volatile.Read(ref queue->_headAndTail.Tail) == head) { spinner.SpinOnce(); } return(*queue->_items.Element <T>(head)); }
public static bool TryPeek <T>(UnsafeSPSCQueue *queue, out T result) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); var head = Volatile.Read(ref queue->_headAndTail.Head); // Queue empty if (Volatile.Read(ref queue->_headAndTail.Tail) == head) { result = default; return(false); } result = *queue->_items.Element <T>(head); return(true); }
/// <summary> /// Returns a snapshot of the elements in the queue. /// </summary> /// <returns></returns> public static T[] ToArray <T>(UnsafeSPSCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); var head = Volatile.Read(ref queue->_headAndTail.Head); var tail = Volatile.Read(ref queue->_headAndTail.Tail); var count = tail - head; if (count < 0) { count += queue->_items.Length; } if (count <= 0) { return(Array.Empty <T>()); } var arr = new T[count]; int numToCopy = count; int bufferLength = queue->_items.Length; int ihead = head; int firstPart = Math.Min(bufferLength - ihead, numToCopy); fixed(void *ptr = arr) { UnsafeBuffer.CopyTo <T>(queue->_items, ihead, ptr, 0, firstPart); numToCopy -= firstPart; if (numToCopy > 0) { UnsafeBuffer.CopyTo <T>(queue->_items, 0, ptr, 0 + bufferLength - ihead, numToCopy); } } return(arr); }
/// <summary> /// Tries to enqueue an item in the queue. Returns false if there's no space in the queue. /// </summary> public static bool TryEnqueue <T>(UnsafeSPSCQueue *queue, T item) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); var tail = Volatile.Read(ref queue->_headAndTail.Tail); var nextTail = GetNext(tail, queue->_items.Length); // Full Queue if (nextTail == Volatile.Read(ref queue->_headAndTail.Head)) { return(false); } *queue->_items.Element <T>(tail) = item; Volatile.Write(ref queue->_headAndTail.Tail, nextTail); return(true); }
/// <summary> /// Enqueues an item in the queue. Blocks the thread until there is space in the queue. /// </summary> public static void Enqueue <T>(UnsafeSPSCQueue *queue, T item) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); SpinWait spinner = default; var tail = Volatile.Read(ref queue->_headAndTail.Tail); var nextTail = GetNext(tail, queue->_items.Length); // Full Queue while (nextTail == Volatile.Read(ref queue->_headAndTail.Head)) { spinner.SpinOnce(); } *queue->_items.Element <T>(tail) = item; Volatile.Write(ref queue->_headAndTail.Tail, nextTail); }
/// <summary> /// Dequeues an item from the queue. Blocks the thread until there is space in the queue. /// </summary> public static T Dequeue <T>(UnsafeSPSCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_items.Ptr != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); SpinWait spinner = default; var head = Volatile.Read(ref queue->_headAndTail.Head); // Queue empty while (Volatile.Read(ref queue->_headAndTail.Tail) == head) { spinner.SpinOnce(); } var result = queue->_items.Element <T>(head); var nextHead = GetNext(head, queue->_items.Length); Volatile.Write(ref queue->_headAndTail.Head, nextHead); return(*result); }
public NativeSPSCQueue(int capacity) { m_inner = UnsafeSPSCQueue.Allocate <T>(capacity); }
public void Dispose() { UnsafeSPSCQueue.Free(m_inner); m_inner = null; }