MTQueue.cs
73 lines
| 2.4 KiB
| text/x-csharp
|
CSharpLexer
|
|
r93 | using System.Threading; | ||
|
|
r14 | |||
| namespace Implab.Parallels { | ||||
| public class MTQueue<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) { | ||||
|
|
r80 | Thread.MemoryBarrier(); | ||
|
|
r14 | var last = m_last; | ||
| var next = new Node(value); | ||||
| 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; | ||||
|
|
r93 | Node next; | ||
|
|
r14 | value = default(T); | ||
|
|
r80 | Thread.MemoryBarrier(); | ||
|
|
r14 | do { | ||
| first = m_first; | ||||
| if (first == null) | ||||
| return false; | ||||
| next = first.next; | ||||
| if (next == null) { | ||||
| // this is the last element, | ||||
|
|
r19 | // then try to update the tail | ||
|
|
r14 | if (first != Interlocked.CompareExchange(ref m_last, null, first)) { | ||
|
|
r71 | // this is the race condition | ||
|
|
r14 | if (m_last == null) | ||
|
|
r19 | // the queue is empty | ||
|
|
r14 | return false; | ||
|
|
r71 | // tail has been changed, we need to restart | ||
|
|
r14 | continue; | ||
| } | ||||
| // tail succesfully updated and first.next will never be changed | ||||
|
|
r71 | // 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 | ||||
|
|
r14 | |||
|
|
r71 | // 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 | ||||
|
|
r14 | Interlocked.CompareExchange(ref m_first, null, first); | ||
| break; | ||||
| } | ||||
|
|
r93 | if (first == Interlocked.CompareExchange(ref m_first, next, first)) | ||
| // head succesfully updated | ||||
| break; | ||||
|
|
r14 | } while (true); | ||
| value = first.value; | ||||
| return true; | ||||
| } | ||||
| } | ||||
| } | ||||
