Mercurial > pub > ImplabNet
comparison Implab/Parallels/MTCustomQueue.cs @ 106:d4e38929ce36 v2
promises refactoring
author | cin |
---|---|
date | Mon, 10 Nov 2014 18:00:28 +0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
105:4d308952fd5e | 106:d4e38929ce36 |
---|---|
1 using System; | |
2 using System.Collections.Generic; | |
3 using System.Threading; | |
4 using System.Collections; | |
5 | |
6 namespace Implab.Parallels { | |
7 public class MTCustomQueue<TNode> : IEnumerable<TNode> where TNode : MTCustomQueueNode<TNode> { | |
8 TNode m_first; | |
9 TNode m_last; | |
10 | |
11 public void Enqueue(TNode next) { | |
12 Thread.MemoryBarrier(); | |
13 | |
14 var last = m_last; | |
15 | |
16 // Interlocaked.CompareExchange implies Thread.MemoryBarrier(); | |
17 // to ensure that the next node is completely constructed | |
18 while (last != Interlocked.CompareExchange(ref m_last, next, last)) | |
19 last = m_last; | |
20 | |
21 if (last != null) | |
22 last.next = next; | |
23 else | |
24 m_first = next; | |
25 } | |
26 | |
27 public bool TryDequeue(out TNode node) { | |
28 TNode first; | |
29 TNode next; | |
30 node = null; | |
31 | |
32 Thread.MemoryBarrier(); | |
33 do { | |
34 first = m_first; | |
35 if (first == null) | |
36 return false; | |
37 next = first.next; | |
38 if (next == null) { | |
39 // this is the last element, | |
40 // then try to update the tail | |
41 if (first != Interlocked.CompareExchange(ref m_last, null, first)) { | |
42 // this is the race condition | |
43 if (m_last == null) | |
44 // the queue is empty | |
45 return false; | |
46 // tail has been changed, we need to restart | |
47 continue; | |
48 } | |
49 | |
50 // tail succesfully updated and first.next will never be changed | |
51 // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null | |
52 // however the parallel writer may update the m_first since the m_last is null | |
53 | |
54 // so we need to fix inconsistency by setting m_first to null or if it has been | |
55 // updated by the writer already then we should just to give up | |
56 Interlocked.CompareExchange(ref m_first, null, first); | |
57 break; | |
58 | |
59 } | |
60 if (first == Interlocked.CompareExchange(ref m_first, next, first)) | |
61 // head succesfully updated | |
62 break; | |
63 } while (true); | |
64 | |
65 node = first; | |
66 return true; | |
67 } | |
68 | |
69 #region IEnumerable implementation | |
70 | |
71 class Enumerator : IEnumerator<TNode> { | |
72 TNode m_current; | |
73 TNode m_first; | |
74 | |
75 public Enumerator(TNode first) { | |
76 m_first = first; | |
77 } | |
78 | |
79 #region IEnumerator implementation | |
80 | |
81 public bool MoveNext() { | |
82 m_current = m_current == null ? m_first : m_current.next; | |
83 return m_current != null; | |
84 } | |
85 | |
86 public void Reset() { | |
87 m_current = null; | |
88 } | |
89 | |
90 object IEnumerator.Current { | |
91 get { | |
92 if (m_current == null) | |
93 throw new InvalidOperationException(); | |
94 return m_current; | |
95 } | |
96 } | |
97 | |
98 #endregion | |
99 | |
100 #region IDisposable implementation | |
101 | |
102 public void Dispose() { | |
103 } | |
104 | |
105 #endregion | |
106 | |
107 #region IEnumerator implementation | |
108 | |
109 public TNode Current { | |
110 get { | |
111 if (m_current == null) | |
112 throw new InvalidOperationException(); | |
113 return m_current; | |
114 } | |
115 } | |
116 | |
117 #endregion | |
118 } | |
119 | |
120 public IEnumerator<TNode> GetEnumerator() { | |
121 return new Enumerator(m_first); | |
122 } | |
123 | |
124 #endregion | |
125 | |
126 #region IEnumerable implementation | |
127 | |
128 IEnumerator IEnumerable.GetEnumerator() { | |
129 return GetEnumerator(); | |
130 } | |
131 | |
132 #endregion | |
133 } | |
134 } | |
135 |