private static void SplitQueue(UnsafeMPMCQueue *q) { //Wrap tail back to 0 for (int i = 0; i < 5; i++) { UnsafeMPMCQueue.TryEnqueue(q, 111); } //First half for (int i = 0; i < 5; i++) { UnsafeMPMCQueue.TryEnqueue(q, i); } //Move head by 5 for (int i = 0; i < 5; i++) { UnsafeMPMCQueue.TryDequeue <int>(q, out int num); } //Second half (head and tail are now both 5) for (int i = 5; i < 10; i++) { UnsafeMPMCQueue.TryEnqueue(q, i); } //Circular buffer now "ends" in the middle of the underlying array }
public static void Clear(UnsafeMPMCQueue *queue) { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); queue->_crossSegmentLock.Lock(); queue->_tail->EnsureFrozenForEnqueues(); var segmentsize = INITIAL_SEGMENT_LENGTH; if (queue->_fixedSize) { segmentsize = queue->_tail->Capacity; } // Free all segments var segment = queue->_head; do { var next = segment->_nextSegment; QueueSegment.Free(segment); segment = next; }while (segment != null); queue->_tail = queue->_head = QueueSegment.Allocate(segmentsize, queue->_slotStride, queue->_slotOffset); queue->_crossSegmentLock.Unlock(); }
public static T[] ToArray <T>(UnsafeMPMCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); queue->SnapForObservation(out QueueSegment * head, out int headHead, out QueueSegment * tail, out int tailTail); long count = GetCount(head, headHead, tail, tailTail); var arr = new T[count]; int i = 0; var enumerator = GetEnumerator <T>(queue); while (enumerator.MoveNext()) { arr[i++] = enumerator.Current; } UDebug.Assert(count == i); return(arr); }
public static bool IsEmpty <T>(UnsafeMPMCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); return(!queue->TryPeek(out T result, false)); }
public static int GetCapacity(UnsafeMPMCQueue *queue) { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); SpinWait spinner = default; while (true) { // Capture head and tail var headSeg = queue->_head; var tailSeg = queue->_tail; // Single QueueSegment if (headSeg == tailSeg) { if (headSeg == queue->_head && tailSeg == queue->_tail) { return(headSeg->Capacity); } } // Two QueueSegments else if (headSeg->_nextSegment == tailSeg) { if (headSeg == queue->_head && tailSeg == queue->_tail) { return(headSeg->Capacity + tailSeg->Capacity); } } // Mutliple QueueSegments (requires locking) else { // Aquire cross-segment lock queue->_crossSegmentLock.Lock(); if (headSeg == queue->_head && tailSeg == queue->_tail) { int capacity = headSeg->Capacity + tailSeg->Capacity; for (QueueSegment *s = headSeg->_nextSegment; s != tailSeg; s = s->_nextSegment) { UDebug.Assert(s->_frozen, "Internal segment must be frozen as there's a following segment."); capacity += s->Capacity; } // Release cross-segment lock queue->_crossSegmentLock.Unlock(); return(capacity); } // Release cross-segment lock queue->_crossSegmentLock.Unlock(); } spinner.SpinOnce(); } }
public static Enumerator <T> GetEnumerator <T>(UnsafeMPMCQueue *queue) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); queue->SnapForObservation(out QueueSegment * headSeg, out int headSeg_Head, out QueueSegment * tailSeg, out int tailSeg_Tail); var enumerator = new Enumerator <T>(headSeg, headSeg_Head, tailSeg, tailSeg_Tail); return(enumerator); }
/// <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>(UnsafeMPMCQueue *queue, T item) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); if (!queue->_tail->TryEnqueue(item)) { return(queue->TryEnqueueSlow(item)); } return(true); }
/// <summary> /// Tries to dequeue an item from the queue. Returns false if there's no items in the queue. /// </summary> public static bool TryDequeue <T>(UnsafeMPMCQueue *queue, out T result) where T : unmanaged { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle); QueueSegment *head = queue->_head; if (head->TryDequeue(out result)) { return(true); } if (head->_nextSegment == null) { result = default; return(false); } return(queue->TryDequeueSlow(out result)); }
public void IteratorSingleSegmentTest() { UnsafeMPMCQueue *q = UnsafeMPMCQueue.Allocate <int>(10); for (int i = 0; i < 10; i++) { UnsafeMPMCQueue.TryEnqueue <int>(q, i); } var enumerator = UnsafeMPMCQueue.GetEnumerator <int>(q); int ii = 0; foreach (int num in enumerator) { Assert.AreEqual(ii, num); ii++; } Assert.AreEqual(10, ii); UnsafeMPMCQueue.Free(q); }
public static void Free(UnsafeMPMCQueue *queue) { if (queue == null) { return; } // Free all segments var segment = queue->_head; do { var next = segment->_nextSegment; QueueSegment.Free(segment); segment = next; }while (segment != null); // 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 void IteratorMultiSegmentTest() { UnsafeMPMCQueue *q = UnsafeMPMCQueue.Allocate <int>(8); // Enqueue large amount so we get multiple segments. for (int i = 0; i < 50; i++) { UnsafeMPMCQueue.TryEnqueue(q, i); } var enumerator = UnsafeMPMCQueue.GetEnumerator <int>(q); int ii = 0; foreach (int num in enumerator) { Assert.AreEqual(ii, num); ii++; } Assert.AreEqual(50, ii); UnsafeMPMCQueue.Free(q); }
public static int GetCount(UnsafeMPMCQueue *queue) { UDebug.Assert(queue != null); UDebug.Assert(queue->_head != null); SpinWait spinner = default; while (true) { // Capture head and tail var headSeg = queue->_head; var tailSeg = queue->_tail; int headSeg_Head = Volatile.Read(ref headSeg->_headAndTail.Head); int headSeg_Tail = Volatile.Read(ref headSeg->_headAndTail.Tail); // Single QueueSegment if (headSeg == tailSeg) { // Count can be reliably determined if the captured segments still match. if (headSeg == queue->_head && tailSeg == queue->_tail && headSeg_Head == Volatile.Read(ref headSeg->_headAndTail.Head) && headSeg_Tail == Volatile.Read(ref headSeg->_headAndTail.Tail)) { return(GetCount(headSeg, headSeg_Head, headSeg_Tail)); } } // Two QueueSegments else if (headSeg->_nextSegment == tailSeg) { int tailSeg_Head = Volatile.Read(ref tailSeg->_headAndTail.Head); int tailSeg_Tail = Volatile.Read(ref tailSeg->_headAndTail.Tail); if (headSeg == queue->_head && tailSeg == queue->_tail && headSeg_Head == Volatile.Read(ref headSeg->_headAndTail.Head) && headSeg_Tail == Volatile.Read(ref headSeg->_headAndTail.Tail) && tailSeg_Head == Volatile.Read(ref tailSeg->_headAndTail.Head) && tailSeg_Tail == Volatile.Read(ref tailSeg->_headAndTail.Tail)) { return(GetCount(headSeg, headSeg_Head, headSeg_Tail) + GetCount(tailSeg, tailSeg_Head, tailSeg_Tail)); } } // Mutliple QueueSegments (requires locking) else { // Aquire cross-segment lock queue->_crossSegmentLock.Lock(); if (headSeg == queue->_head && tailSeg == queue->_tail) { int tailSeg_Head = Volatile.Read(ref tailSeg->_headAndTail.Head); int tailSeg_Tail = Volatile.Read(ref tailSeg->_headAndTail.Tail); if (headSeg_Head == Volatile.Read(ref headSeg->_headAndTail.Head) && headSeg_Tail == Volatile.Read(ref headSeg->_headAndTail.Tail) && tailSeg_Head == Volatile.Read(ref tailSeg->_headAndTail.Head) && tailSeg_Tail == Volatile.Read(ref tailSeg->_headAndTail.Tail)) { int count = GetCount(headSeg, headSeg_Head, headSeg_Tail) + GetCount(tailSeg, tailSeg_Head, tailSeg_Tail); for (QueueSegment *s = headSeg->_nextSegment; s != tailSeg; s = s->_nextSegment) { UDebug.Assert(s->_frozen, "Internal segment must be frozen as there's a following segment."); count += s->_headAndTail.Tail - s->FreezeOffset; } // Release cross-segment lock queue->_crossSegmentLock.Unlock(); return(count); } } // Release cross-segment lock queue->_crossSegmentLock.Unlock(); } spinner.SpinOnce(); } }
public NativeMPMCQueue(int segmentSize, bool fixedSize) { m_inner = UnsafeMPMCQueue.Allocate <T>(segmentSize, fixedSize); }
public void Dispose() { UnsafeMPMCQueue.Free(m_inner); m_inner = null; }