File: System\Collections\Concurrent\ConcurrentStack.cs
Web Access
Project: src\src\libraries\System.Collections.Concurrent\src\System.Collections.Concurrent.csproj (System.Collections.Concurrent)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// ConcurrentStack.cs
//
// A lock-free, concurrent stack primitive, and its associated debugger view type.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
 
namespace System.Collections.Concurrent
{
    // A stack that uses CAS operations internally to maintain thread-safety in a lock-free
    // manner. Attempting to push or pop concurrently from the stack will not trigger waiting,
    // although some optimistic concurrency and retry is used, possibly leading to lack of
    // fairness and/or livelock. The stack uses spinning and backoff to add some randomization,
    // in hopes of statistically decreasing the possibility of livelock.
    //
    // Note that we currently allocate a new node on every push. This avoids having to worry
    // about potential ABA issues, since the CLR GC ensures that a memory address cannot be
    // reused before all references to it have died.
 
    /// <summary>
    /// Represents a thread-safe last-in, first-out collection of objects.
    /// </summary>
    /// <typeparam name="T">Specifies the type of elements in the stack.</typeparam>
    /// <remarks>
    /// All public and protected members of <see cref="ConcurrentStack{T}"/> are thread-safe and may be used
    /// concurrently from multiple threads.
    /// </remarks>
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(IProducerConsumerCollectionDebugView<>))]
    public class ConcurrentStack<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
    {
        /// <summary>
        /// A simple (internal) node type used to store elements of concurrent stacks and queues.
        /// </summary>
        private sealed class Node
        {
            internal readonly T _value; // Value of the node.
            internal Node? _next; // Next pointer.
 
            /// <summary>
            /// Constructs a new node with the specified value and no next node.
            /// </summary>
            /// <param name="value">The value of the node.</param>
            internal Node(T value)
            {
                _value = value;
                _next = null;
            }
        }
 
        private volatile Node? _head; // The stack is a singly linked list, and only remembers the head.
        private const int BACKOFF_MAX_YIELDS = 8; // Arbitrary number to cap backoff.
 
        /// <summary>
        /// Initializes a new instance of the <see cref="ConcurrentStack{T}"/>
        /// class.
        /// </summary>
        public ConcurrentStack()
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="ConcurrentStack{T}"/>
        /// class that contains elements copied from the specified collection
        /// </summary>
        /// <param name="collection">The collection whose elements are copied to the new <see
        /// cref="ConcurrentStack{T}"/>.</param>
        /// <exception cref="System.ArgumentNullException">The <paramref name="collection"/> argument is
        /// null.</exception>
        public ConcurrentStack(IEnumerable<T> collection)
        {
            ArgumentNullException.ThrowIfNull(collection);
 
            InitializeFromCollection(collection);
        }
 
        /// <summary>
        /// Initializes the contents of the stack from an existing collection.
        /// </summary>
        /// <param name="collection">A collection from which to copy elements.</param>
        private void InitializeFromCollection(IEnumerable<T> collection)
        {
            // We just copy the contents of the collection to our stack.
            Node? lastNode = null;
            foreach (T element in collection)
            {
                Node newNode = new Node(element);
                newNode._next = lastNode;
                lastNode = newNode;
            }
 
            _head = lastNode;
        }
 
 
        /// <summary>
        /// Gets a value that indicates whether the <see cref="ConcurrentStack{T}"/> is empty.
        /// </summary>
        /// <value>true if the <see cref="ConcurrentStack{T}"/> is empty; otherwise, false.</value>
        /// <remarks>
        /// For determining whether the collection contains any items, use of this property is recommended
        /// rather than retrieving the number of items from the <see cref="Count"/> property and comparing it
        /// to 0.  However, as this collection is intended to be accessed concurrently, it may be the case
        /// that another thread will modify the collection after <see cref="IsEmpty"/> returns, thus invalidating
        /// the result.
        /// </remarks>
        public bool IsEmpty
        {
            // Checks whether the stack is empty. Clearly the answer may be out of date even prior to
            // the function returning (i.e. if another thread concurrently adds to the stack). It does
            // guarantee, however, that, if another thread does not mutate the stack, a subsequent call
            // to TryPop will return true -- i.e. it will also read the stack as non-empty.
            get { return _head == null; }
        }
 
        /// <summary>
        /// Gets the number of elements contained in the <see cref="ConcurrentStack{T}"/>.
        /// </summary>
        /// <value>The number of elements contained in the <see cref="ConcurrentStack{T}"/>.</value>
        /// <remarks>
        /// For determining whether the collection contains any items, use of the <see cref="IsEmpty"/>
        /// property is recommended rather than retrieving the number of items from the <see cref="Count"/>
        /// property and comparing it to 0.
        /// </remarks>
        public int Count
        {
            // Counts the number of entries in the stack. This is an O(n) operation. The answer may be out
            // of date before returning, but guarantees to return a count that was once valid. Conceptually,
            // the implementation snaps a copy of the list and then counts the entries, though physically
            // this is not what actually happens.
            get
            {
                int count = 0;
 
                // Just whip through the list and tally up the number of nodes. We rely on the fact that
                // node next pointers are immutable after being enqueued for the first time, even as
                // they are being dequeued. If we ever changed this (e.g. to pool nodes somehow),
                // we'd need to revisit this implementation.
 
                for (Node? curr = _head; curr != null; curr = curr._next)
                {
                    count++; //we don't handle overflow, to be consistent with existing generic collection types in CLR
                }
 
                return count;
            }
        }
 
 
        /// <summary>
        /// Gets a value indicating whether access to the <see cref="System.Collections.ICollection"/> is
        /// synchronized with the SyncRoot.
        /// </summary>
        /// <value>true if access to the <see cref="System.Collections.ICollection"/> is synchronized
        /// with the SyncRoot; otherwise, false. For <see cref="ConcurrentStack{T}"/>, this property always
        /// returns false.</value>
        bool ICollection.IsSynchronized
        {
            // Gets a value indicating whether access to this collection is synchronized. Always returns
            // false. The reason is subtle. While access is in face thread safe, it's not the case that
            // locking on the SyncRoot would have prevented concurrent pushes and pops, as this property
            // would typically indicate; that's because we internally use CAS operations vs. true locks.
            get { return false; }
        }
 
        /// <summary>
        /// Gets an object that can be used to synchronize access to the <see
        /// cref="System.Collections.ICollection"/>. This property is not supported.
        /// </summary>
        /// <exception cref="System.NotSupportedException">The SyncRoot property is not supported</exception>
        object ICollection.SyncRoot
        {
            get
            {
                throw new NotSupportedException(SR.ConcurrentCollection_SyncRoot_NotSupported);
            }
        }
 
        /// <summary>
        /// Removes all objects from the <see cref="ConcurrentStack{T}"/>.
        /// </summary>
        public void Clear()
        {
            // Clear the list by setting the head to null. We don't need to use an atomic
            // operation for this: anybody who is mutating the head by pushing or popping
            // will need to use an atomic operation to guarantee they serialize and don't
            // overwrite our setting of the head to null.
            _head = null;
        }
 
        /// <summary>
        /// Copies the elements of the <see cref="System.Collections.ICollection"/> to an <see
        /// cref="System.Array"/>, starting at a particular
        /// <see cref="System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="System.Array"/> that is the destination of
        /// the elements copied from the
        /// <see cref="ConcurrentStack{T}"/>. The <see cref="System.Array"/> must
        /// have zero-based indexing.</param>
        /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
        /// begins.</param>
        /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
        /// Visual Basic).</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
        /// zero.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="array"/> is multidimensional. -or-
        /// <paramref name="array"/> does not have zero-based indexing. -or-
        /// <paramref name="index"/> is equal to or greater than the length of the <paramref name="array"/>
        /// -or- The number of elements in the source <see cref="System.Collections.ICollection"/> is
        /// greater than the available space from <paramref name="index"/> to the end of the destination
        /// <paramref name="array"/>. -or- The type of the source <see
        /// cref="System.Collections.ICollection"/> cannot be cast automatically to the type of the
        /// destination <paramref name="array"/>.
        /// </exception>
        void ICollection.CopyTo(Array array, int index)
        {
            ArgumentNullException.ThrowIfNull(array);
 
            // We must be careful not to corrupt the array, so we will first accumulate an
            // internal list of elements that we will then copy to the array. This requires
            // some extra allocation, but is necessary since we don't know up front whether
            // the array is sufficiently large to hold the stack's contents.
            ((ICollection)ToList()).CopyTo(array, index);
        }
 
        /// <summary>
        /// Copies the <see cref="ConcurrentStack{T}"/> elements to an existing one-dimensional <see
        /// cref="System.Array"/>, starting at the specified array index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="System.Array"/> that is the destination of
        /// the elements copied from the
        /// <see cref="ConcurrentStack{T}"/>. The <see cref="System.Array"/> must have zero-based
        /// indexing.</param>
        /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
        /// begins.</param>
        /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
        /// Visual Basic).</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
        /// zero.</exception>
        /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
        /// length of the <paramref name="array"/>
        /// -or- The number of elements in the source <see cref="ConcurrentStack{T}"/> is greater than the
        /// available space from <paramref name="index"/> to the end of the destination <paramref
        /// name="array"/>.
        /// </exception>
        public void CopyTo(T[] array, int index)
        {
            ArgumentNullException.ThrowIfNull(array);
 
            // We must be careful not to corrupt the array, so we will first accumulate an
            // internal list of elements that we will then copy to the array. This requires
            // some extra allocation, but is necessary since we don't know up front whether
            // the array is sufficiently large to hold the stack's contents.
            ToList().CopyTo(array, index);
        }
 
        /// <summary>
        /// Inserts an object at the top of the <see cref="ConcurrentStack{T}"/>.
        /// </summary>
        /// <param name="item">The object to push onto the <see cref="ConcurrentStack{T}"/>. The value can be
        /// a null reference (Nothing in Visual Basic) for reference types.
        /// </param>
        public void Push(T item)
        {
            // Pushes a node onto the front of the stack thread-safely. Internally, this simply
            // swaps the current head pointer using a (thread safe) CAS operation to accomplish
            // lock freedom. If the CAS fails, we add some back off to statistically decrease
            // contention at the head, and then go back around and retry.
 
            Node newNode = new Node(item);
            newNode._next = _head;
            if (Interlocked.CompareExchange(ref _head, newNode, newNode._next) == newNode._next)
            {
                return;
            }
 
            // If we failed, go to the slow path and loop around until we succeed.
            PushCore(newNode, newNode);
        }
 
        /// <summary>
        /// Inserts multiple objects at the top of the <see cref="ConcurrentStack{T}"/> atomically.
        /// </summary>
        /// <param name="items">The objects to push onto the <see cref="ConcurrentStack{T}"/>.</param>
        /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null reference
        /// (Nothing in Visual Basic).</exception>
        /// <remarks>
        /// When adding multiple items to the stack, using PushRange is a more efficient
        /// mechanism than using <see cref="Push"/> one item at a time.  Additionally, PushRange
        /// guarantees that all of the elements will be added atomically, meaning that no other threads will
        /// be able to inject elements between the elements being pushed.  Items at lower indices in
        /// the <paramref name="items"/> array will be pushed before items at higher indices.
        /// </remarks>
        public void PushRange(T[] items)
        {
            ArgumentNullException.ThrowIfNull(items);
 
            PushRange(items, 0, items.Length);
        }
 
        /// <summary>
        /// Inserts multiple objects at the top of the <see cref="ConcurrentStack{T}"/> atomically.
        /// </summary>
        /// <param name="items">The objects to push onto the <see cref="ConcurrentStack{T}"/>.</param>
        /// <param name="startIndex">The zero-based offset in <paramref name="items"/> at which to begin
        /// inserting elements onto the top of the <see cref="ConcurrentStack{T}"/>.</param>
        /// <param name="count">The number of elements to be inserted onto the top of the <see
        /// cref="ConcurrentStack{T}"/>.</param>
        /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null reference
        /// (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref
        /// name="count"/> is negative. Or <paramref name="startIndex"/> is greater than or equal to the length
        /// of <paramref name="items"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="startIndex"/> + <paramref name="count"/> is
        /// greater than the length of <paramref name="items"/>.</exception>
        /// <remarks>
        /// When adding multiple items to the stack, using PushRange is a more efficient
        /// mechanism than using <see cref="Push"/> one item at a time. Additionally, PushRange
        /// guarantees that all of the elements will be added atomically, meaning that no other threads will
        /// be able to inject elements between the elements being pushed. Items at lower indices in the
        /// <paramref name="items"/> array will be pushed before items at higher indices.
        /// </remarks>
        public void PushRange(T[] items, int startIndex, int count)
        {
            ValidatePushPopRangeInput(items, startIndex, count);
 
            // No op if the count is zero
            if (count == 0)
                return;
 
            Node head, tail;
            head = tail = new Node(items[startIndex]);
            for (int i = startIndex + 1; i < startIndex + count; i++)
            {
                Node node = new Node(items[i]);
                node._next = head;
                head = node;
            }
 
            tail._next = _head;
            if (Interlocked.CompareExchange(ref _head, head, tail._next) == tail._next)
            {
                return;
            }
 
            // If we failed, go to the slow path and loop around until we succeed.
            PushCore(head, tail);
        }
 
 
        /// <summary>
        /// Push one or many nodes into the stack, if head and tails are equal then push one node to the stack other wise push the list between head
        /// and tail to the stack
        /// </summary>
        /// <param name="head">The head pointer to the new list</param>
        /// <param name="tail">The tail pointer to the new list</param>
        private void PushCore(Node head, Node tail)
        {
            SpinWait spin = default;
 
            // Keep trying to CAS the existing head with the new node until we succeed.
            do
            {
                spin.SpinOnce(sleep1Threshold: -1);
                // Reread the head and link our new node.
                tail._next = _head;
            }
            while (Interlocked.CompareExchange(
                ref _head, head, tail._next) != tail._next);
 
            if (CDSCollectionETWBCLProvider.Log.IsEnabled())
            {
                CDSCollectionETWBCLProvider.Log.ConcurrentStack_FastPushFailed(spin.Count);
            }
        }
 
        /// <summary>
        /// Local helper function to validate the Pop Push range methods input
        /// </summary>
        private static void ValidatePushPopRangeInput(T[] items, int startIndex, int count)
        {
            ArgumentNullException.ThrowIfNull(items);
 
            ArgumentOutOfRangeException.ThrowIfNegative(count);
 
            int length = items.Length;
            ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, length);
            ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
 
            if (length - count < startIndex) //instead of (startIndex + count > items.Length) to prevent overflow
            {
                throw new ArgumentException(SR.ConcurrentStack_PushPopRange_InvalidCount);
            }
        }
 
        /// <summary>
        /// Attempts to add an object to the <see
        /// cref="System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
        /// </summary>
        /// <param name="item">The object to add to the <see
        /// cref="System.Collections.Concurrent.IProducerConsumerCollection{T}"/>. The value can be a null
        /// reference (Nothing in Visual Basic) for reference types.
        /// </param>
        /// <returns>true if the object was added successfully; otherwise, false.</returns>
        /// <remarks>For <see cref="ConcurrentStack{T}"/>, this operation
        /// will always insert the object onto the top of the <see cref="ConcurrentStack{T}"/>
        /// and return true.</remarks>
        bool IProducerConsumerCollection<T>.TryAdd(T item)
        {
            Push(item);
            return true;
        }
 
        /// <summary>
        /// Attempts to return an object from the top of the <see cref="ConcurrentStack{T}"/>
        /// without removing it.
        /// </summary>
        /// <param name="result">When this method returns, <paramref name="result"/> contains an object from
        /// the top of the <see cref="System.Collections.Concurrent.ConcurrentStack{T}"/> or an
        /// unspecified value if the operation failed.</param>
        /// <returns>true if and object was returned successfully; otherwise, false.</returns>
        public bool TryPeek([MaybeNullWhen(false)] out T result)
        {
            Node? head = _head;
 
            // If the stack is empty, return false; else return the element and true.
            if (head == null)
            {
                result = default(T)!;
                return false;
            }
            else
            {
                result = head._value;
                return true;
            }
        }
 
        /// <summary>
        /// Attempts to pop and return the object at the top of the <see cref="ConcurrentStack{T}"/>.
        /// </summary>
        /// <param name="result">
        /// When this method returns, if the operation was successful, <paramref name="result"/> contains the
        /// object removed. If no object was available to be removed, the value is unspecified.
        /// </param>
        /// <returns>true if an element was removed and returned from the top of the <see
        /// cref="ConcurrentStack{T}"/>
        /// successfully; otherwise, false.</returns>
        public bool TryPop([MaybeNullWhen(false)] out T result)
        {
            Node? head = _head;
            //stack is empty
            if (head == null)
            {
                result = default(T)!;
                return false;
            }
            if (Interlocked.CompareExchange(ref _head, head._next, head) == head)
            {
                result = head._value;
                return true;
            }
 
            // Fall through to the slow path.
            return TryPopCore(out result);
        }
 
        /// <summary>
        /// Attempts to pop and return multiple objects from the top of the <see cref="ConcurrentStack{T}"/>
        /// atomically.
        /// </summary>
        /// <param name="items">
        /// The <see cref="System.Array"/> to which objects popped from the top of the <see
        /// cref="ConcurrentStack{T}"/> will be added.
        /// </param>
        /// <returns>The number of objects successfully popped from the top of the <see
        /// cref="ConcurrentStack{T}"/> and inserted in
        /// <paramref name="items"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null argument (Nothing
        /// in Visual Basic).</exception>
        /// <remarks>
        /// When popping multiple items, if there is little contention on the stack, using
        /// TryPopRange can be more efficient than using <see cref="TryPop"/>
        /// once per item to be removed.  Nodes fill the <paramref name="items"/>
        /// with the first node to be popped at the startIndex, the second node to be popped
        /// at startIndex + 1, and so on.
        /// </remarks>
        public int TryPopRange(T[] items)
        {
            ArgumentNullException.ThrowIfNull(items);
 
            return TryPopRange(items, 0, items.Length);
        }
 
        /// <summary>
        /// Attempts to pop and return multiple objects from the top of the <see cref="ConcurrentStack{T}"/>
        /// atomically.
        /// </summary>
        /// <param name="items">
        /// The <see cref="System.Array"/> to which objects popped from the top of the <see
        /// cref="ConcurrentStack{T}"/> will be added.
        /// </param>
        /// <param name="startIndex">The zero-based offset in <paramref name="items"/> at which to begin
        /// inserting elements from the top of the <see cref="ConcurrentStack{T}"/>.</param>
        /// <param name="count">The number of elements to be popped from top of the <see
        /// cref="ConcurrentStack{T}"/> and inserted into <paramref name="items"/>.</param>
        /// <returns>The number of objects successfully popped from the top of
        /// the <see cref="ConcurrentStack{T}"/> and inserted in <paramref name="items"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="items"/> is a null reference
        /// (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="startIndex"/> or <paramref
        /// name="count"/> is negative. Or <paramref name="startIndex"/> is greater than or equal to the length
        /// of <paramref name="items"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="startIndex"/> + <paramref name="count"/> is
        /// greater than the length of <paramref name="items"/>.</exception>
        /// <remarks>
        /// When popping multiple items, if there is little contention on the stack, using
        /// TryPopRange can be more efficient than using <see cref="TryPop"/>
        /// once per item to be removed.  Nodes fill the <paramref name="items"/>
        /// with the first node to be popped at the startIndex, the second node to be popped
        /// at startIndex + 1, and so on.
        /// </remarks>
        public int TryPopRange(T[] items, int startIndex, int count)
        {
            ValidatePushPopRangeInput(items, startIndex, count);
 
            // No op if the count is zero
            if (count == 0)
                return 0;
 
            Node? poppedHead;
            int nodesCount = TryPopCore(count, out poppedHead);
            if (nodesCount > 0)
            {
                CopyRemovedItems(poppedHead!, items, startIndex, nodesCount);
            }
            return nodesCount;
        }
 
        /// <summary>
        /// Local helper function to Pop an item from the stack, slow path
        /// </summary>
        /// <param name="result">The popped item</param>
        /// <returns>True if succeeded, false otherwise</returns>
        private bool TryPopCore([MaybeNullWhen(false)] out T result)
        {
            Node? poppedNode;
 
            if (TryPopCore(1, out poppedNode) == 1)
            {
                result = poppedNode!._value;
                return true;
            }
 
            result = default(T)!;
            return false;
        }
 
        /// <summary>
        /// Slow path helper for TryPop. This method assumes an initial attempt to pop an element
        /// has already occurred and failed, so it begins spinning right away.
        /// </summary>
        /// <param name="count">The number of items to pop.</param>
        /// <param name="poppedHead">
        /// When this method returns, if the pop succeeded, contains the removed object. If no object was
        /// available to be removed, the value is unspecified. This parameter is passed uninitialized.
        /// </param>
        /// <returns>The number of objects successfully popped from the top of
        /// the <see cref="ConcurrentStack{T}"/>.</returns>
        private int TryPopCore(int count, out Node? poppedHead)
        {
            SpinWait spin = default;
 
            // Try to CAS the head with its current next.  We stop when we succeed or
            // when we notice that the stack is empty, whichever comes first.
            Node? head;
            Node next;
            int backoff = 1;
            while (true)
            {
                head = _head;
                // Is the stack empty?
                if (head == null)
                {
                    if (count == 1 && CDSCollectionETWBCLProvider.Log.IsEnabled())
                    {
                        CDSCollectionETWBCLProvider.Log.ConcurrentStack_FastPopFailed(spin.Count);
                    }
 
                    poppedHead = null;
                    return 0;
                }
                next = head;
                int nodesCount = 1;
                for (; nodesCount < count && next._next != null; nodesCount++)
                {
                    next = next._next;
                }
 
                // Try to swap the new head.  If we succeed, break out of the loop.
                if (Interlocked.CompareExchange(ref _head, next._next, head) == head)
                {
                    if (count == 1 && CDSCollectionETWBCLProvider.Log.IsEnabled())
                    {
                        CDSCollectionETWBCLProvider.Log.ConcurrentStack_FastPopFailed(spin.Count);
                    }
 
                    // Return the popped Node.
                    poppedHead = head;
                    return nodesCount;
                }
 
                // We failed to CAS the new head.  Spin briefly and retry.
                for (int i = 0; i < backoff; i++)
                {
                    spin.SpinOnce(sleep1Threshold: -1);
                }
 
                if (spin.NextSpinWillYield)
                {
                    backoff = Random.Shared.Next(1, BACKOFF_MAX_YIELDS);
                }
                else
                {
                    backoff *= 2;
                }
            }
        }
 
        /// <summary>
        /// Local helper function to copy the popped elements into a given collection
        /// </summary>
        /// <param name="head">The head of the list to be copied</param>
        /// <param name="collection">The collection to place the popped items in</param>
        /// <param name="startIndex">the beginning of index of where to place the popped items</param>
        /// <param name="nodesCount">The number of nodes.</param>
        private static void CopyRemovedItems(Node head, T[] collection, int startIndex, int nodesCount)
        {
            Node? current = head;
            for (int i = startIndex; i < startIndex + nodesCount; i++)
            {
                collection[i] = current!._value;
                current = current._next;
            }
        }
 
        /// <summary>
        /// Attempts to remove and return an object from the <see
        /// cref="System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
        /// </summary>
        /// <param name="item">
        /// When this method returns, if the operation was successful, <paramref name="item"/> contains the
        /// object removed. If no object was available to be removed, the value is unspecified.
        /// </param>
        /// <returns>true if an element was removed and returned successfully; otherwise, false.</returns>
        /// <remarks>For <see cref="ConcurrentStack{T}"/>, this operation will attempt to pope the object at
        /// the top of the <see cref="ConcurrentStack{T}"/>.
        /// </remarks>
        bool IProducerConsumerCollection<T>.TryTake([MaybeNullWhen(false)] out T item)
        {
            return TryPop(out item);
        }
 
        /// <summary>
        /// Copies the items stored in the <see cref="ConcurrentStack{T}"/> to a new array.
        /// </summary>
        /// <returns>A new array containing a snapshot of elements copied from the <see
        /// cref="ConcurrentStack{T}"/>.</returns>
        public T[] ToArray()
        {
            Node? curr = _head;
            return curr == null ?
                Array.Empty<T>() :
                ToList(curr).ToArray();
        }
 
        /// <summary>
        /// Returns an array containing a snapshot of the list's contents, using
        /// the target list node as the head of a region in the list.
        /// </summary>
        /// <returns>A list of the stack's contents.</returns>
        private List<T> ToList()
        {
            return ToList(_head);
        }
 
        /// <summary>
        /// Returns an array containing a snapshot of the list's contents starting at the specified node.
        /// </summary>
        /// <returns>A list of the stack's contents starting at the specified node.</returns>
        private static List<T> ToList(Node? curr)
        {
            List<T> list = new List<T>();
 
            while (curr != null)
            {
                list.Add(curr._value);
                curr = curr._next;
            }
 
            return list;
        }
 
        /// <summary>
        /// Returns an enumerator that iterates through the <see cref="ConcurrentStack{T}"/>.
        /// </summary>
        /// <returns>An enumerator for the <see cref="ConcurrentStack{T}"/>.</returns>
        /// <remarks>
        /// The enumeration represents a moment-in-time snapshot of the contents
        /// of the stack.  It does not reflect any updates to the collection after
        /// <see cref="GetEnumerator()"/> was called.  The enumerator is safe to use
        /// concurrently with reads from and writes to the stack.
        /// </remarks>
        public IEnumerator<T> GetEnumerator()
        {
            // Returns an enumerator for the stack. This effectively takes a snapshot
            // of the stack's contents at the time of the call, i.e. subsequent modifications
            // (pushes or pops) will not be reflected in the enumerator's contents.
 
            //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of
            //the stack is not taken when GetEnumerator is initialized but when MoveNext() is first called.
            //This is inconsistent with existing generic collections. In order to prevent it, we capture the
            //value of _head in a buffer and call out to a helper method
            return GetEnumerator(_head);
        }
 
        private static IEnumerator<T> GetEnumerator(Node? head)
        {
            Node? current = head;
            while (current != null)
            {
                yield return current._value;
                current = current._next;
            }
        }
 
        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>An <see cref="System.Collections.IEnumerator"/> that can be used to iterate through
        /// the collection.</returns>
        /// <remarks>
        /// The enumeration represents a moment-in-time snapshot of the contents of the stack. It does not
        /// reflect any updates to the collection after
        /// <see cref="GetEnumerator()"/> was called. The enumerator is safe to use concurrently with reads
        /// from and writes to the stack.
        /// </remarks>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<T>)this).GetEnumerator();
        }
    }
}