public static bool TryPeek <T>(UnsafeMPSCQueue *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);

            // Temp copy of inner buffer
            var items = queue->_items;

            int head   = Volatile.Read(ref queue->_headAndTail.Head);
            int index  = head & queue->_mask;
            int offset = queue->_slotOffset;

            int seq = Volatile.Read(ref *(int *)items.Element(index, offset));
            int dif = seq - (head + 1);

            if (dif == 0)
            {
                result = *items.Element <T>(index);
                return(true);
            }

            result = default;
            return(false);
        }
        private static void SplitQueue(UnsafeMPSCQueue *q)
        {
            //Wrap tail back to 0
            for (int i = 0; i < 5; i++)
            {
                UnsafeMPSCQueue.TryEnqueue(q, 111);
            }

            //First half
            for (int i = 0; i < 5; i++)
            {
                UnsafeMPSCQueue.TryEnqueue(q, i);
            }

            //Move head by 5
            for (int i = 0; i < 5; i++)
            {
                UnsafeMPSCQueue.Dequeue <int>(q);
            }

            //Second half (head and tail are now both 5)
            for (int i = 5; i < 10; i++)
            {
                UnsafeMPSCQueue.TryEnqueue(q, i);
            }

            //Circular buffer now "ends" in the middle of the underlying array
        }
        /// <summary>
        /// Dequeues an item from the queue. Blocks the thread until there is space in the queue.
        /// </summary>
        public static T Dequeue <T>(UnsafeMPSCQueue *queue) where T : unmanaged
        {
            UDebug.Assert(queue != null);
            UDebug.Assert(queue->_items.Ptr != null);
            UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle);

            SpinWait spinner = new SpinWait();

            // Temp copy of inner buffer
            var items = queue->_items;

            int head   = Volatile.Read(ref queue->_headAndTail.Head);
            int index  = head & queue->_mask;
            int offset = queue->_slotOffset;

            while (true)
            {
                int seq = Volatile.Read(ref *(int *)items.Element(index, offset));
                int dif = seq - (head + 1);

                if (dif == 0)
                {
                    // Update head
                    Volatile.Write(ref queue->_headAndTail.Head, head + 1);
                    var item = *items.Element <T>(index);

                    // Update slot after reading
                    Volatile.Write(ref *(int *)items.Element(index, offset), head + items.Length);

                    return(item);
                }

                spinner.SpinOnce();
            }
        }
        public static int GetCapacity(UnsafeMPSCQueue *queue)
        {
            UDebug.Assert(queue != null);
            UDebug.Assert(queue->_items.Ptr != null);

            return(queue->_items.Length);
        }
        /// <summary>
        /// Creates an enumerator for the current snapshot of the queue.
        /// </summary>
        public static Enumerator <T> GetEnumerator <T>(UnsafeMPSCQueue *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));
        }
 internal Enumerator(UnsafeMPSCQueue *queue)
 {
     _queue     = queue;
     _index     = -1;
     _current   = default;
     _headStart = Volatile.Read(ref queue->_headAndTail.Head);
     _mask      = queue->_mask;
     _seqOffset = queue->_slotOffset;
 }
        public static bool IsEmpty <T>(UnsafeMPSCQueue *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(UnsafeMPSCQueue *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 void Clear(UnsafeMPSCQueue *queue)
        {
            UDebug.Assert(queue != null);
            UDebug.Assert(queue->_items.Ptr != null);

            queue->_headAndTail = new HeadAndTail();

            // Initialize the sequence number for each slot.
            // This is used to synchronize between consumer and producer threads.
            var offset = queue->_slotOffset;
            var items  = queue->_items;

            for (int i = 0; i < queue->_items.Length; i++)
            {
                *(int *)items.Element(i, offset) = i;
            }
        }
        /// <summary>
        /// Gets the current count of the queue.
        /// Value becomes stale if enqueue/dequeue operations happen.
        /// </summary>
        public static int GetCount(UnsafeMPSCQueue *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);
            int mask = queue->_mask;

            if (head != tail)
            {
                head &= mask;
                tail &= mask;

                return(head < tail ? tail - head : queue->_items.Length - head + tail);
            }
            return(0);
        }
        /// <summary>
        /// Returns a snapshot of the elements.
        /// </summary>
        /// <returns></returns>
        internal static T[] ToArray <T>(UnsafeMPSCQueue *queue) where T : unmanaged
        {
            UDebug.Assert(queue != null);
            UDebug.Assert(queue->_items.Ptr != null);
            UDebug.Assert(typeof(T).TypeHandle.Value == queue->_typeHandle);

            int count = GetCount(queue);
            var arr   = new T[count];

            var enumerator = GetEnumerator <T>(queue);

            int i = 0;

            while (enumerator.MoveNext() && i < count)
            {
                arr[i++] = enumerator.Current;
            }


            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>(UnsafeMPSCQueue *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;

            // Temp copy of inner buffer
            var items  = queue->_items;
            var offset = queue->_slotOffset;

            while (true)
            {
                int tail  = Volatile.Read(ref queue->_headAndTail.Tail);
                int index = tail & queue->_mask;

                int seq = Volatile.Read(ref *(int *)items.Element(index, offset));
                int dif = seq - tail;

                if (dif == 0)
                {
                    // Reserve the slot
                    if (Interlocked.CompareExchange(ref queue->_headAndTail.Tail, tail + 1, tail) == tail)
                    {
                        // Write the value and update the seq
                        *items.Element <T>(index) = item;
                        Volatile.Write(ref *(int *)items.Element(index, offset), tail + 1);
                        return(true);
                    }
                }
                else if (dif < 0)
                {
                    // Slot was full
                    return(false);
                }
                // Lost the race, try again
                spinner.SpinOnce();
            }
        }
        int          _slotOffset; // Readonly

        /// <summary>
        /// Allocates a new SPSCRingbuffer. Capacity will be set to a power of 2.
        /// </summary>
        public static UnsafeMPSCQueue *Allocate <T>(int capacity) where T : unmanaged
        {
            if (capacity < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(capacity), string.Format(ThrowHelper.ArgumentOutOfRange_MustBePositive, nameof(capacity)));
            }

            capacity = Memory.RoundUpToPowerOf2(capacity);

            // Required to get the memory size of the Slot + Value
            int slotStride = Marshal.SizeOf(new QueueSlot <T>());
            int slotAlign  = Memory.GetMaxAlignment(sizeof(T), sizeof(int));
            int slotOffset = Memory.RoundToAlignment(sizeof(T), slotAlign);

            int alignment = Memory.GetAlignment(slotStride);

            var sizeOfQueue = Memory.RoundToAlignment(sizeof(UnsafeMPSCQueue), alignment);
            var sizeOfArray = slotStride * capacity;

            var ptr = Memory.MallocAndZero(sizeOfQueue + sizeOfArray, alignment);

            UnsafeMPSCQueue *queue = (UnsafeMPSCQueue *)ptr;

            // initialize fixed buffer from same block of memory as the stack
            UnsafeBuffer.InitFixed(&queue->_items, (byte *)ptr + sizeOfQueue, capacity, slotStride);

            // Read-only values
            queue->_mask       = capacity - 1;
            queue->_slotOffset = slotOffset;
            queue->_typeHandle = typeof(T).TypeHandle.Value;

            // Reset the queue for use.
            Clear(queue);

            return(queue);
        }
Пример #14
0
 public NativeMPSCQueue(int capacity)
 {
     m_inner = UnsafeMPSCQueue.Allocate <T>(capacity);
 }
Пример #15
0
 public void Dispose()
 {
     UnsafeMPSCQueue.Free(m_inner);
     m_inner = null;
 }