Пример #1
0
        /// <summary>
        /// A wait-free alternative to <see cref="TryEnqueue"/>, which fails on compare-and-swap failure.
        /// </summary>
        /// <param name="e">The item to enqueue.</param>
        /// <returns><c>1</c> if next element cannot be filled, <c>-1</c> if CAS failed, and <c>0</c> if successful.</returns>
        public int WeakEnqueue(T e)
        {
            Contract.Requires(e != null);

            long mask               = this.Mask;
            long capacity           = mask + 1;
            long currentTail        = this.ProducerIndex;      // LoadLoad
            long consumerIndexCache = this.ConsumerIndexCache; // LoadLoad
            long wrapPoint          = currentTail - capacity;

            if (consumerIndexCache <= wrapPoint)
            {
                long currHead = this.ConsumerIndex; // LoadLoad
                if (currHead <= wrapPoint)
                {
                    return(1); // FULL :(
                }
                else
                {
                    this.ConsumerIndexCache = currHead; // StoreLoad
                }
            }

            // look Ma, no loop!
            if (!this.TrySetProducerIndex(currentTail, currentTail + 1))
            {
                return(-1); // CAS FAIL :(
            }

            // Won CAS, move on to storing
            long offset = RefArrayAccessUtil.CalcElementOffset(currentTail, mask);

            this.SoElement(offset, e);
            return(0); // AWESOME :)
        }
Пример #2
0
        /// <summary>
        /// Lock free peek using ordered loads. As class name suggests access is limited to a single thread.
        /// </summary>
        /// <param name="item">The peeked item.</param>
        /// <returns><c>true</c> if an item was retrieved, otherwise <c>false</c>.</returns>
        /// <seealso cref="IQueue{T}.TryPeek"/>
        public override bool TryPeek(out T item)
        {
            // Copy field to avoid re-reading after volatile load
            T[] buffer = this.Buffer;

            long consumerIndex = this.ConsumerIndex; // LoadLoad
            long offset        = this.CalcElementOffset(consumerIndex);
            T    e             = RefArrayAccessUtil.LvElement(buffer, offset);

            if (null == e)
            {
                // NOTE: Queue may not actually be empty in the case of a producer (P1) being interrupted after
                // winning the CAS on offer but before storing the element in the queue. Other producers may go on
                // to fill up the queue after this element.

                if (consumerIndex != this.ProducerIndex)
                {
                    do
                    {
                        e = RefArrayAccessUtil.LvElement(buffer, offset);
                    } while (e == null);
                }
                else
                {
                    item = default(T);
                    return(false);
                }
            }

            item = e;

            return(true);
        }
Пример #3
0
        /// <summary>
        /// Lock free Enqueue operation, using a single compare-and-swap. As the class name suggests, access is
        /// permitted to many threads concurrently.
        /// </summary>
        /// <param name="e">The item to enqueue.</param>
        /// <returns><c>true</c> if the item was added successfully, otherwise <c>false</c>.</returns>
        /// <seealso cref="IQueue{T}.TryEnqueue"/>
        public override bool TryEnqueue(T e)
        {
            Contract.Requires(e != null);

            // use a cached view on consumer index (potentially updated in loop)
            long mask               = this.Mask;
            long capacity           = mask + 1;
            long consumerIndexCache = this.ConsumerIndexCache; // LoadLoad
            long currentProducerIndex;

            do
            {
                currentProducerIndex = this.ProducerIndex; // LoadLoad
                long wrapPoint = currentProducerIndex - capacity;
                if (consumerIndexCache <= wrapPoint)
                {
                    long currHead = this.ConsumerIndex; // LoadLoad
                    if (currHead <= wrapPoint)
                    {
                        return(false); // FULL :(
                    }
                    else
                    {
                        // update shared cached value of the consumerIndex
                        this.ConsumerIndexCache = currHead; // StoreLoad
                        // update on stack copy, we might need this value again if we lose the CAS.
                        consumerIndexCache = currHead;
                    }
                }
            } while (!this.TrySetProducerIndex(currentProducerIndex, currentProducerIndex + 1));

            // NOTE: the new producer index value is made visible BEFORE the element in the array. If we relied on
            // the index visibility to poll() we would need to handle the case where the element is not visible.

            // Won CAS, move on to storing
            long offset = RefArrayAccessUtil.CalcElementOffset(currentProducerIndex, mask);

            this.SoElement(offset, e); // StoreStore
            return(true);              // AWESOME :)
        }
 /// <summary>
 /// A volatile load (load + LoadLoad barrier) of an element from a given offset.
 /// </summary>
 /// <param name="offset">Computed via <see cref="CalcElementOffset"/>.</param>
 /// <returns>The element at the offset.</returns>
 protected T LvElement(long offset) => RefArrayAccessUtil.LvElement(this.Buffer, offset);
 /// <summary>
 /// An ordered store(store + StoreStore barrier) of an element to a given offset.
 /// </summary>
 /// <param name="offset">Computed via <see cref="CalcElementOffset"/>.</param>
 /// <param name="e">An orderly kitty.</param>
 protected void SoElement(long offset, T e) => RefArrayAccessUtil.SoElement(this.Buffer, offset, e);
 /// <summary>
 /// Calculates an element offset based on a given array index.
 /// </summary>
 /// <param name="index">The desirable element index.</param>
 /// <returns>The offset in bytes within the array for a given index.</returns>
 protected long CalcElementOffset(long index) => RefArrayAccessUtil.CalcElementOffset(index, this.Mask);