Mercurial > pub > ImplabNet
comparison Implab/Parallels/SimpleAsyncQueue.cs @ 233:d6fe09f5592c v2
Improved AsyncQueue
Removed ImplabFx
| author | cin |
|---|---|
| date | Wed, 04 Oct 2017 15:44:47 +0300 |
| parents | |
| children | cbe10ac0731e |
comparison
equal
deleted
inserted
replaced
| 229:5f7a3e1d32b9 | 233:d6fe09f5592c |
|---|---|
| 1 using System.Threading; | |
| 2 using System.Collections.Generic; | |
| 3 using System; | |
| 4 using System.Collections; | |
| 5 | |
| 6 namespace Implab.Parallels { | |
| 7 public class SimpleAsyncQueue<T> : IEnumerable<T> { | |
| 8 class Node { | |
| 9 public Node(T value) { | |
| 10 this.value = value; | |
| 11 } | |
| 12 public readonly T value; | |
| 13 public volatile Node next; | |
| 14 } | |
| 15 | |
| 16 // the reader and the writer are mainteined completely independent, | |
| 17 // the reader can read next item when m_first.next is not null | |
| 18 // the writer creates the a new node, moves m_last to this node and | |
| 19 // only after that restores the reference from the previous node | |
| 20 // making available the reader to read the new node. | |
| 21 | |
| 22 Node m_first; // position on the node which is already read | |
| 23 Node m_last; // position on the node which is already written | |
| 24 | |
| 25 public SimpleAsyncQueue() { | |
| 26 m_first = m_last = new Node(default(T)); | |
| 27 } | |
| 28 | |
| 29 public void Enqueue(T value) { | |
| 30 var next = new Node(value); | |
| 31 | |
| 32 // Interlocaked.CompareExchange implies Thread.MemoryBarrier(); | |
| 33 // to ensure that the next node is completely constructed | |
| 34 var last = Interlocked.Exchange(ref m_last, next); | |
| 35 | |
| 36 // release-fence | |
| 37 last.next = next; | |
| 38 | |
| 39 } | |
| 40 | |
| 41 public bool TryDequeue(out T value) { | |
| 42 Node first; | |
| 43 Node next; | |
| 44 | |
| 45 Thread.MemoryBarrier(); // ensure m_first is fresh | |
| 46 SpinWait spin = new SpinWait(); | |
| 47 do { | |
| 48 first = m_first; | |
| 49 // aquire-fence | |
| 50 next = first.next; | |
| 51 if (next == null) { | |
| 52 value = default(T); | |
| 53 return false; | |
| 54 } | |
| 55 | |
| 56 if (first == Interlocked.CompareExchange(ref m_first, next, first)) | |
| 57 // head succesfully updated | |
| 58 break; | |
| 59 spin.SpinOnce(); | |
| 60 } while (true); | |
| 61 | |
| 62 value = next.value; | |
| 63 return true; | |
| 64 } | |
| 65 | |
| 66 #region IEnumerable implementation | |
| 67 | |
| 68 class Enumerator : IEnumerator<T> { | |
| 69 Node m_current; | |
| 70 Node m_first; | |
| 71 | |
| 72 public Enumerator(Node first) { | |
| 73 m_first = first; | |
| 74 } | |
| 75 | |
| 76 #region IEnumerator implementation | |
| 77 | |
| 78 public bool MoveNext() { | |
| 79 m_current = m_current == null ? m_first : m_current.next; | |
| 80 return m_current != null; | |
| 81 } | |
| 82 | |
| 83 public void Reset() { | |
| 84 m_current = null; | |
| 85 } | |
| 86 | |
| 87 object IEnumerator.Current { | |
| 88 get { | |
| 89 if (m_current == null) | |
| 90 throw new InvalidOperationException(); | |
| 91 return m_current.value; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 #endregion | |
| 96 | |
| 97 #region IDisposable implementation | |
| 98 | |
| 99 public void Dispose() { | |
| 100 } | |
| 101 | |
| 102 #endregion | |
| 103 | |
| 104 #region IEnumerator implementation | |
| 105 | |
| 106 public T Current { | |
| 107 get { | |
| 108 if (m_current == null) | |
| 109 throw new InvalidOperationException(); | |
| 110 return m_current.value; | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 #endregion | |
| 115 } | |
| 116 | |
| 117 public IEnumerator<T> GetEnumerator() { | |
| 118 return new Enumerator(m_first); | |
| 119 } | |
| 120 | |
| 121 #endregion | |
| 122 | |
| 123 #region IEnumerable implementation | |
| 124 | |
| 125 IEnumerator IEnumerable.GetEnumerator() { | |
| 126 return GetEnumerator(); | |
| 127 } | |
| 128 | |
| 129 #endregion | |
| 130 } | |
| 131 } |
