Mercurial > pub > ImplabNet
view Implab/Parallels/MTCustomQueue.cs @ 107:f5220e5472ef v2
minor fixes and optimizations
author | cin |
---|---|
date | Tue, 11 Nov 2014 04:14:21 +0300 |
parents | d4e38929ce36 |
children |
line wrap: on
line source
using System; using System.Collections.Generic; using System.Threading; using System.Collections; namespace Implab.Parallels { public class MTCustomQueue<TNode> : IEnumerable<TNode> where TNode : MTCustomQueueNode<TNode> { TNode m_first; TNode m_last; public void Enqueue(TNode next) { Thread.MemoryBarrier(); var last = m_last; // Interlocaked.CompareExchange implies Thread.MemoryBarrier(); // to ensure that the next node is completely constructed while (last != Interlocked.CompareExchange(ref m_last, next, last)) last = m_last; if (last != null) last.next = next; else m_first = next; } public bool TryDequeue(out TNode node) { TNode first; TNode next; node = null; Thread.MemoryBarrier(); do { first = m_first; if (first == null) return false; next = first.next; if (next == null) { // this is the last element, // then try to update the tail if (first != Interlocked.CompareExchange(ref m_last, null, first)) { // this is the race condition if (m_last == null) // the queue is empty return false; // tail has been changed, we need to restart continue; } // tail succesfully updated and first.next will never be changed // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null // however the parallel writer may update the m_first since the m_last is null // so we need to fix inconsistency by setting m_first to null or if it has been // updated by the writer already then we should just to give up Interlocked.CompareExchange(ref m_first, null, first); break; } if (first == Interlocked.CompareExchange(ref m_first, next, first)) // head succesfully updated break; } while (true); node = first; return true; } #region IEnumerable implementation class Enumerator : IEnumerator<TNode> { TNode m_current; TNode m_first; public Enumerator(TNode first) { m_first = first; } #region IEnumerator implementation public bool MoveNext() { m_current = m_current == null ? m_first : m_current.next; return m_current != null; } public void Reset() { m_current = null; } object IEnumerator.Current { get { if (m_current == null) throw new InvalidOperationException(); return m_current; } } #endregion #region IDisposable implementation public void Dispose() { } #endregion #region IEnumerator implementation public TNode Current { get { if (m_current == null) throw new InvalidOperationException(); return m_current; } } #endregion } public IEnumerator<TNode> GetEnumerator() { return new Enumerator(m_first); } #endregion #region IEnumerable implementation IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } }