|
|
using System.Threading;
|
|
|
using System.Collections.Generic;
|
|
|
using System;
|
|
|
using System.Collections;
|
|
|
|
|
|
namespace Implab.Parallels {
|
|
|
public class MTQueue<T> : IEnumerable<T> {
|
|
|
class Node {
|
|
|
public Node(T value) {
|
|
|
this.value = value;
|
|
|
}
|
|
|
public readonly T value;
|
|
|
public Node next;
|
|
|
}
|
|
|
|
|
|
Node m_first;
|
|
|
Node m_last;
|
|
|
|
|
|
public void Enqueue(T value) {
|
|
|
Thread.MemoryBarrier();
|
|
|
|
|
|
var last = m_last;
|
|
|
var next = new Node(value);
|
|
|
|
|
|
// 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 T value) {
|
|
|
Node first;
|
|
|
Node next;
|
|
|
value = default(T);
|
|
|
|
|
|
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);
|
|
|
|
|
|
value = first.value;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
#region IEnumerable implementation
|
|
|
|
|
|
class Enumerator : IEnumerator<T> {
|
|
|
Node m_current;
|
|
|
Node m_first;
|
|
|
|
|
|
public Enumerator(Node 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.value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IDisposable implementation
|
|
|
|
|
|
public void Dispose() {
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IEnumerator implementation
|
|
|
|
|
|
public T Current {
|
|
|
get {
|
|
|
if (m_current == null)
|
|
|
throw new InvalidOperationException();
|
|
|
return m_current.value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
|
|
|
public IEnumerator<T> GetEnumerator() {
|
|
|
return new Enumerator(m_first);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IEnumerable implementation
|
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() {
|
|
|
return GetEnumerator();
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
}
|
|
|
|