예제 #1
0
        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
        }
예제 #2
0
        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();
        }
예제 #3
0
        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);
        }
예제 #4
0
        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));
        }
예제 #5
0
        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();
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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));
        }
예제 #9
0
        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);
        }
예제 #10
0
        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);
        }
예제 #11
0
        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);
        }
예제 #12
0
        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;
 }