/// <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 :) }
/// <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); }
/// <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);