##// END OF EJS Templates
working on diagnostics
cin -
r285:b6924f444abd v3
parent child
Show More
@@ -0,0 +1,22
1 using System.Diagnostics;
2
3 namespace Implab.Diagnostics
4 {
5 public class ChannelInfo
6 {
7 internal ChannelInfo(object id, TraceSource source) {
8 Debug.Assert(id != null);
9 Debug.Assert(source != null);
10
11 Id = id;
12 Source = source;
13
14 }
15
16 public object Id { get; private set; }
17
18 public TraceSource Source { get; private set; }
19
20
21 }
22 } No newline at end of file
@@ -0,0 +1,76
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using Implab.Parallels;
5
6 namespace Implab.Diagnostics {
7 public class TraceChannelRegistry {
8
9 class Subscription : IDisposable {
10 readonly Action<object> m_unsubscribe;
11
12 public Subscription(Action<object> unsubscribe) {
13 m_unsubscribe = unsubscribe;
14 }
15
16 public void Dispose() {
17 m_unsubscribe(this);
18 }
19 }
20
21 public static TraceChannelRegistry AllCannels { get; } = new TraceChannelRegistry();
22
23 readonly object m_lock = new object();
24
25 readonly Dictionary<object, Action<ChannelInfo>> m_subscriptions = new Dictionary<object, Action<ChannelInfo>>();
26 readonly SimpleAsyncQueue<ChannelInfo> m_channels = new SimpleAsyncQueue<ChannelInfo>();
27
28 internal void NotifyChannelCreated(ChannelInfo channel) {
29 // notifications can run in parallel
30 Action<ChannelInfo>[] handlers = null;
31
32 lock(m_lock) {
33 m_channels.Enqueue(channel);
34 if (m_subscriptions.Count > 0) {
35 handlers = new Action<ChannelInfo>[m_subscriptions.Count];
36 m_subscriptions.Values.CopyTo(handlers, 0);
37 }
38 }
39
40 if (handlers != null)
41 foreach(var h in handlers)
42 h(channel);
43 }
44
45 /// <summary>
46 /// Subscribes the specified handler to notifications about new trace
47 /// channels
48 /// </summary>
49 /// <param name="handler"></param>
50 /// <returns></returns>
51 public IDisposable Subscribe(Action<ChannelInfo> handler, bool existing) {
52 Safe.ArgumentNotNull(handler, nameof(handler));
53
54 var cookie = new Subscription(RemoveSubscription);
55
56 IEnumerable<ChannelInfo> snap;
57
58 lock(m_lock) {
59 m_subscriptions.Add(cookie, handler);
60 snap = m_channels.Snapshot();
61 }
62
63 if (existing) {
64 foreach(var c in snap)
65 handler(c);
66 }
67
68 return cookie;
69 }
70
71 void RemoveSubscription(object cookie) {
72 lock(m_lock)
73 m_subscriptions.Remove(cookie);
74 }
75 }
76 } No newline at end of file
@@ -1,191 +1,191
1 1 using Xunit;
2 2 using System;
3 3 using Implab.Automaton;
4 4 using Implab.Xml;
5 5 using System.Xml;
6 6 using Implab.Formats;
7 7 using Implab.Formats.Json;
8 8 using System.IO;
9 9 using Implab.Test.Model;
10 10
11 11 namespace Implab.Test {
12 12 public class JsonTests {
13 13
14 14 [Fact]
15 15 public void TestScannerValidTokens() {
16 16 using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
17 17
18 18 Tuple<JsonTokenType, object>[] expexted = {
19 19 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "9123"),
20 20 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
21 21 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-123"),
22 22 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
23 23 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0"),
24 24 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
25 25 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0.1"),
26 26 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
27 27 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.2"),
28 28 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
29 29 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.1e3"),
30 30 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
31 31 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "1.3E-3"),
32 32 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
33 33 new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"),
34 34 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
35 35 new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"),
36 36 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, null),
37 37 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, null),
38 38 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, null),
39 39 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, null),
40 40 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, null)
41 41 };
42 42
43 43 string value;
44 44 JsonTokenType tokenType;
45 45 for (var i = 0; i < expexted.Length; i++) {
46 46
47 47 Assert.True(scanner.ReadToken(out value, out tokenType));
48 48 Assert.Equal(expexted[i].Item1, tokenType);
49 49 Assert.Equal(expexted[i].Item2, value);
50 50 }
51 51
52 52 Assert.False(scanner.ReadToken(out value, out tokenType));
53 53 }
54 54 }
55 55
56 56 [Fact]
57 57 public void TestScannerBadTokens() {
58 58 var bad = new[] {
59 59 " 1",
60 60 " literal",
61 61 " \"",
62 62 "\"unclosed string",
63 63 "1.bad",
64 64 "001", // should be read as three numbers
65 65 "--10",
66 66 "+10",
67 67 "1.0.0",
68 68 "1e1.0",
69 69 "l1teral0",
70 70 ".123",
71 71 "-.123"
72 72 };
73 73
74 74 foreach (var json in bad) {
75 75 using (var scanner = JsonStringScanner.Create(json)) {
76 76 try {
77 77 string value;
78 78 JsonTokenType token;
79 79 scanner.ReadToken(out value, out token);
80 80 if (!Object.Equals(value, json)) {
81 81 Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value);
82 82 continue;
83 83 }
84 84 Assert.True(false, $"Token '{json}' shouldn't pass");
85 85 } catch (ParserException e) {
86 86 Console.WriteLine(e.Message);
87 87 }
88 88 }
89 89 }
90 90 }
91 91
92 92 [Fact]
93 93 public void JsonXmlReaderSimpleTest() {
94 var json = "\"some text\"";
94 //var json = "\"some text\"";
95 95 //Console.WriteLine($"JSON: {json}");
96 96 //Console.WriteLine("XML");
97 97 /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) {
98 98 Assert.AreEqual(xmlReader.ReadState, System.Xml.ReadState.Initial);
99 99
100 100 AssertRead(xmlReader, XmlNodeType.XmlDeclaration);
101 101 AssertRead(xmlReader, XmlNodeType.Element);
102 102 AssertRead(xmlReader, XmlNodeType.Text);
103 103 AssertRead(xmlReader, XmlNodeType.EndElement);
104 104 Assert.IsFalse(xmlReader.Read());
105 105 }*/
106 106
107 107 DumpJsonParse("\"text value\"");
108 108 DumpJsonParse("null");
109 109 DumpJsonParse("true");
110 110 DumpJsonParse("{}");
111 111 DumpJsonParse("[]");
112 112 DumpJsonParse("{\"one\":1, \"two\":2}");
113 113 DumpJsonParse("[1,\"\",2,3]");
114 114 DumpJsonParse("[{\"info\": [7,8,9]}]");
115 115 DumpJsonFlatParse("[1,2,\"\",[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
116 116 }
117 117
118 118 [Fact]
119 119 public void XmlToJsonTransform() {
120 120 var person = new Person {
121 121 FirstName = "Charlie",
122 122 LastName = "Brown",
123 123 Age = 19,
124 124 AgeSpecified = true
125 125 };
126 126
127 127 var doc = SerializationHelpers.SerializeAsXmlDocument(person);
128 128
129 129 using (var writer = new StringWriter()) {
130 130 XmlToJson.Default.Transform(doc,null, writer);
131 131 Console.WriteLine(writer.ToString());
132 132 }
133 133 }
134 134
135 135 [Fact]
136 136 public void JsonSerialization() {
137 137 var person = new Person {
138 138 FirstName = "Charlie",
139 139 LastName = "Brown",
140 140 Age = 19,
141 141 AgeSpecified = true,
142 142 Tags = new [] { "brave", "stupid" }
143 143 };
144 144
145 145 var data = SerializationHelpers.SerializeJsonAsString(person);
146 146 Console.WriteLine(data);
147 147 var clone = SerializationHelpers.DeserializeJsonFromString<Person>(data);
148 148
149 149 Assert.Equal(person.FirstName, clone.FirstName);
150 150 Assert.Equal(person.LastName, clone.LastName);
151 151 Assert.Equal(person.Age, clone.Age);
152 152 Assert.Equal(person.AgeSpecified, clone.AgeSpecified);
153 153 Assert.Equal(person.Tags, person.Tags);
154 154 }
155 155
156 156 void AssertRead(XmlReader reader, XmlNodeType expected) {
157 157 Assert.True(reader.Read());
158 158 Console.WriteLine($"{new string(' ', reader.Depth * 2)}{reader}");
159 159 Assert.Equal(expected, reader.NodeType);
160 160 }
161 161
162 162 void DumpJsonParse(string json) {
163 163 Console.WriteLine($"JSON: {json}");
164 164 Console.WriteLine("XML");
165 165 using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings {
166 166 Indent = true,
167 167 CloseOutput = false,
168 168 ConformanceLevel = ConformanceLevel.Document
169 169 }))
170 170 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "Data", NodesPrefix = "json", CaseTransform = JsonXmlCaseTransform.UcFirst, ArrayItemName = "Item" })) {
171 171 xmlWriter.WriteNode(xmlReader, false);
172 172 }
173 173 Console.WriteLine();
174 174 }
175 175
176 176 void DumpJsonFlatParse(string json) {
177 177 Console.WriteLine($"JSON: {json}");
178 178 Console.WriteLine("XML");
179 179 using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings {
180 180 Indent = true,
181 181 CloseOutput = false,
182 182 ConformanceLevel = ConformanceLevel.Document
183 183 }))
184 184 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
185 185 xmlWriter.WriteNode(xmlReader, false);
186 186 }
187 187 Console.WriteLine();
188 188 }
189 189 }
190 190 }
191 191
@@ -1,161 +1,160
1 1 // enable System.Diagnostics trace methods
2 2 #define TRACE
3 3
4 4 using System;
5 5 using System.Collections.Generic;
6 6 using System.Diagnostics;
7 7 using System.Linq;
8 8 using System.Text;
9 9 using System.Threading;
10 10 using System.Threading.Tasks;
11 11
12 12 namespace Implab.Diagnostics {
13 13 public static class Trace<T> {
14 14
15 15 static Lazy<TraceSource> _traceSource = new Lazy<TraceSource>(CreateChannel, LazyThreadSafetyMode.ExecutionAndPublication);
16 16
17 17 static int _nextId;
18 18
19 19 static TraceSource CreateChannel() {
20 20 var id = Interlocked.Increment(ref _nextId);
21 return new TraceSource(typeof(T).Name);
21 var trs = new TraceSource(typeof(T).Name);
22
23 TraceChannelRegistry.AllCannels.NotifyChannelCreated(new ChannelInfo(typeof(T), trs));
24
25 return trs;
22 26 }
23 27
24 28 public static TraceSource TraceSource { get { return _traceSource.Value; } }
25 29
26 public static IDisposable Subscribe() {
27
28 throw new NotImplementedException();
29 }
30
31 30 #if NETFX_TRACE_BUG
32 31 readonly static AsyncLocal<object> m_currentOperation = new AsyncLocal<object>();
33 32 #endif
34 33
35 34 /// <summary>
36 35 /// Starts the logical operation nested to the current operation nested to the current one.
37 36 /// </summary>
38 37 public static void StartLogicalOperation() {
39 38 Trace.CorrelationManager.StartLogicalOperation();
40 39
41 40 }
42 41
43 42 /// <summary>
44 43 /// Starts the logical operation with the specified name, this name is usefull in logs.
45 44 /// </summary>
46 45 /// <param name="name">Name.</param>
47 46 #if NETFX_TRACE_BUG
48 47 public static void StartLogicalOperation(object name) {
49 48 m_currentOperation.Value = name;
50 49 Trace.CorrelationManager.StartLogicalOperation(name);
51 50 }
52 51 #else
53 52 public static void StartLogicalOperation(object name) {
54 53 Trace.CorrelationManager.StartLogicalOperation(name);
55 54 }
56 55 #endif
57 56
58 57 /// <summary>
59 58 /// Ends the logical operation and restores the previous one.
60 59 /// </summary>
61 60 public static void StopLogicalOperation() {
62 61 Trace.CorrelationManager.StopLogicalOperation();
63 62 }
64 63
65 64 /// <summary>
66 65 /// Writes a debug message.
67 66 /// </summary>
68 67 /// <param name="format">Format.</param>
69 68 /// <param name="arguments">Arguments.</param>
70 69 [Conditional("DEBUG")]
71 70 public static void Debug(string format, params object[] arguments) {
72 71
73 72 }
74 73
75 74 /// <summary>
76 75 /// Writes an informational message.
77 76 /// </summary>
78 77 /// <param name="format">Format.</param>
79 78 /// <param name="arguments">Arguments.</param>
80 79 [Conditional("TRACE")]
81 80 public static void Log(string format, params object[] arguments) {
82 81 TraceSource.TraceEvent(TraceEventType.Information, 0, format, arguments);
83 82 }
84 83
85 84 /// <summary>
86 85 /// Writes a warning message.
87 86 /// </summary>
88 87 /// <param name="format">Format.</param>
89 88 /// <param name="arguments">Arguments.</param>
90 89 public static void Warn(string format, params object[] arguments) {
91 90 TraceSource.TraceEvent(TraceEventType.Warning, 0, format, arguments);
92 91 }
93 92
94 93 /// <summary>
95 94 /// Writes a error message.
96 95 /// </summary>
97 96 /// <param name="format">Format.</param>
98 97 /// <param name="arguments">Arguments.</param>
99 98 public static void Error(string format, params object[] arguments) {
100 99 TraceSource.TraceEvent(TraceEventType.Error, 0, format, arguments);
101 100 }
102 101
103 102 public static void Error(Exception err) {
104 103 TraceSource.TraceData(TraceEventType.Error, 0, err);
105 104 }
106 105
107 106 /// <summary>
108 107 /// This method save the current activity, and transfers to the specified activity,
109 108 /// emits <see cref="TraceEventType.Start"/> and returns a scope of the new
110 109 /// activity.
111 110 /// </summary>
112 111 /// <param name="activityName">The name of the new activity/</param>
113 112 /// <param name="activityId">The identifier of the activity to which
114 113 /// the control will be transferred</param>
115 114 /// <returns>A scope of the new activity, dispose it to transfer
116 115 /// the control back to the original activity.</returns>
117 116 public static ActivityScope TransferActivity(string activityName, Guid activityId) {
118 117 var prev = Trace.CorrelationManager.ActivityId;
119 118
120 119 TraceSource.TraceTransfer(0, "Transfer", activityId);
121 120 Trace.CorrelationManager.ActivityId = activityId;
122 121 TraceSource.TraceEvent(TraceEventType.Start, 0, activityName);
123 122
124 123 return new ActivityScope(TraceSource, prev, 0, activityName);
125 124 }
126 125
127 126 /// <summary>
128 127 /// Emits <see cref="TraceEventType.Start"/> and returns a scope of the
129 128 /// activity.
130 129 /// </summary>
131 130 /// <param name="activityName">The name of the activity to start</param>
132 131 /// <returns>A scope of the new activity, dispose it to emit
133 132 /// <see cref="TraceEventType.Stop"/> for the current activity.</returns>
134 133 public static ActivityScope StartActivity(string activityName) {
135 134 if (Trace.CorrelationManager.ActivityId == Guid.Empty)
136 135 Trace.CorrelationManager.ActivityId = Guid.NewGuid();
137 136
138 137 var prev = Trace.CorrelationManager.ActivityId;
139 138
140 139 TraceSource.TraceEvent(TraceEventType.Start, 0, activityName);
141 140 return new ActivityScope(TraceSource, prev, 0, activityName);
142 141 }
143 142
144 143 /// <summary>
145 144 /// Creates new <see cref="LogicalOperation(string)"/> and calls
146 145 /// to <see cref="CorrelationManager.StartLogicalOperation(object)"/>
147 146 /// passing the created operation as identity. Calls
148 147 /// <see cref="TraceSource.TraceData(TraceEventType, int, object)"/>
149 148 /// to notify listeners on operation start.
150 149 /// </summary>
151 150 /// <param name="name">The name of the logical operation.</param>
152 151 /// <returns>Logical operation scope, disposing it will stop
153 152 /// logical operation and notify trace listeners.</returns>
154 153 public static LogicalOperationScope LogicalOperation(string name) {
155 154 var operation = new LogicalOperation(name);
156 155 TraceSource.TraceData(TraceEventType.Information, TraceEventCodes.StartLogicalOperation, operation);
157 156 StartLogicalOperation(operation);
158 157 return new LogicalOperationScope(TraceSource, operation);
159 158 }
160 159 }
161 160 }
@@ -1,127 +1,128
1 1 using Implab.Automaton;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Diagnostics;
5 5 using System.Linq;
6 6 using System.Runtime.CompilerServices;
7 7 using System.Text;
8 8 using System.Threading.Tasks;
9 9
10 10 namespace Implab.Formats {
11 11
12 12 /// <summary>
13 /// Fast input scanner for max 255 states and first 255 input chacters.
13 /// Fast input scanner for max 255 states and 255 input chacters.
14 14 /// </summary>
15 15 /// <typeparam name="TTag"></typeparam>
16 16 /// <remarks>
17 /// Creates a one rank array to store automa transition table, each entry in this table is byte, to make this table fit into L1 cache.
18 ///
19 /// Each entry is addressed as <c>(state << 8) | input</c> which make this quite fast to get the next state.
20 ///
21 /// Each input symbol below 255 is treated as 255.
17 /// <para>
18 /// Creates a one rank array to store automa transition table, each entry in this table is byte, to make this table small enough to fit L1 cache.
19 /// </para>
20 /// <para>
21 /// Each entry is addressed as <c>(state << 8) | input</c> which make this quite fast to get the next state. Each input symbol below 255 is treated as 255.
22 /// </para>
22 23 /// </remarks>
23 24 public class FastInputScanner<TTag> {
24 25 const int StateShift = 8;
25 26 const int StateMask = ~((1 << StateShift) - 1);
26 27 const int AlphabetSize = 1 << StateShift;
27 28 const int UnclassifiedInput = (1 << StateShift) - 1;
28 29 const byte UnreachableState = byte.MaxValue;
29 30
30 31 readonly TTag[] m_tags;
31 32 readonly bool[] m_final;
32 33
33 34 readonly byte m_initialState;
34 35 readonly byte[] m_dfa;
35 36
36 37 int m_position;
37 38 byte m_state;
38 39
39 40 protected FastInputScanner(byte[] table, bool[] final, TTag[] tags, byte initial) {
40 41 m_dfa = table;
41 42 m_final = final;
42 43 m_tags = tags;
43 44 m_initialState = initial;
44 45 }
45 46
46 47 public FastInputScanner(int[,] dfaTable, bool[] finalStates, TTag[] tags, int initialState, int[] inputMap) {
47 48 var states = dfaTable.GetLength(0);
48 49 Debug.Assert(states < byte.MaxValue);
49 50
50 51 m_dfa = new byte[states << StateShift];
51 52 m_initialState = (byte)initialState;
52 53
53 54 m_tags = tags;
54 55 m_final = finalStates;
55 56
56 57 // iterate over states
57 58 for(int si = 0; si < states; si++) {
58 59 // state offset for the new table
59 60 var offset = si << StateShift;
60 61
61 62 // iterate over alphabet
62 63 for(int a = 0; a < AlphabetSize; a++) {
63 64 var next = dfaTable[si, a < inputMap.Length ? inputMap[a] : AutomatonConst.UnclassifiedInput];
64 65 if (next == AutomatonConst.UnreachableState)
65 66 next = UnreachableState;
66 67
67 68 m_dfa[offset | a] = (byte)next;
68 69 }
69 70 }
70 71 }
71 72
72 73 public TTag Tag {
73 74 [MethodImpl(MethodImplOptions.AggressiveInlining)]
74 75 get {
75 76 return m_tags[m_state];
76 77 }
77 78 }
78 79
79 80 public int Position {
80 81 [MethodImpl(MethodImplOptions.AggressiveInlining)]
81 82 get {
82 83 return m_position;
83 84 }
84 85 }
85 86
86 87 public bool IsFinal {
87 88 [MethodImpl(MethodImplOptions.AggressiveInlining)]
88 89 get {
89 90 return m_final[m_state];
90 91 }
91 92 }
92 93
93 94 [MethodImpl(MethodImplOptions.AggressiveInlining)]
94 95 public void ResetState() {
95 96 m_state = m_initialState;
96 97 }
97 98
98 99 public FastInputScanner<TTag> Clone() {
99 100 var clone = new FastInputScanner<TTag>(m_dfa, m_final, m_tags, m_initialState);
100 101 clone.m_state = m_state;
101 102 clone.m_position = m_position;
102 103 return clone;
103 104 }
104 105
105 106 public bool Scan(char[] data, int offset, int max) {
106 107 var next = m_state;
107 108
108 109 m_position = offset;
109 110 while (m_position < max) {
110 111 var ch = data[m_position];
111 112
112 113 next = m_dfa[(ch >= AlphabetSize ? (next << StateShift) | UnclassifiedInput : (next << StateShift) | ch)];
113 114
114 115 if (next == UnreachableState)
115 116 // scanner stops at the next position after the last recognized symbol
116 117 return false;
117 118
118 119 m_state = next;
119 120 m_position++;
120 121 }
121 122
122 123 return true;
123 124 }
124 125
125 126
126 127 }
127 128 }
@@ -1,140 +1,115
1 1 using System.Threading;
2 2 using System.Collections.Generic;
3 3 using System;
4 4 using System.Collections;
5 5
6 6 namespace Implab.Parallels {
7
8 /// <summary>
9 /// Very simple thred-safe FIFO queue based on the sinle linked list.
10 /// </summary>
11 /// <typeparam name="T"></typeparam>
12 /// <remarks>
13 /// This queue uses interlocked operations to add and remove nodes,
14 /// each node stores a single value. The queue provides mean performance,
15 /// moderate overhead and situable for a small amount of elements.
16 /// </remarks>
7 17 public class SimpleAsyncQueue<T> : IEnumerable<T> {
8 18 class Node {
9 19 public Node(T value) {
10 20 this.value = value;
11 21 }
12 22 public readonly T value;
13 23 public volatile Node next;
14 24 }
15 25
16 // the reader and the writer are mainteined completely independent,
26 // the reader and the writer are maintained completely independent,
17 27 // the reader can read next item when m_first.next is not null
18 28 // the writer creates a new node, moves m_last to this node and
19 29 // only after that restores the reference from the previous node
20 // making the reader be able to read the new node.
30 // making the reader able to read the new node.
21 31
22 32 volatile Node m_first; // position on the node which is already read
23 33 volatile Node m_last; // position on the node which is already written
24 34
25 35 public SimpleAsyncQueue() {
26 36 m_first = m_last = new Node(default(T));
27 37 }
28 38
29 39 public void Enqueue(T value) {
30 40 var next = new Node(value);
31 41
32 42 // Interlocaked.CompareExchange implies Thread.MemoryBarrier();
33 43 // to ensure that the next node is completely constructed
34 44 var last = Interlocked.Exchange(ref m_last, next);
35 45
36 46 // release-fence
37 47 last.next = next;
38 48
39 49 }
40 50
41 51 public bool TryDequeue(out T value) {
42 Node first = m_first; ;
43 Node next = first.next; ;
52 Node first = m_first;
53 Node next = first.next;
44 54
45 55 if (next == null) {
46 56 value = default(T);
47 57 return false;
48 58 }
49 59
50 60 var first2 = Interlocked.CompareExchange(ref m_first, next, first);
51 61
52 62 if (first != first2) {
53 63 // head is updated by someone else
54 64
55 65 SpinWait spin = new SpinWait();
56 66 do {
57 67 first = first2;
58 68 next = first.next;
59 69 if (next == null) {
60 70 value = default(T);
61 71 return false;
62 72 }
63 73
64 74 first2 = Interlocked.CompareExchange(ref m_first, next, first);
65 75 if (first == first2)
66 76 break;
67 77 spin.SpinOnce();
68 78 } while (true);
69 79 }
70 80
71 81 value = next.value;
72 82 return true;
73 83 }
74 84
75 #region IEnumerable implementation
76
77 class Enumerator : IEnumerator<T> {
78 Node m_current;
79 Node m_first;
80
81 public Enumerator(Node first) {
82 m_first = first;
83 }
84
85 #region IEnumerator implementation
86
87 public bool MoveNext() {
88 m_current = m_current == null ? m_first : m_current.next;
89 return m_current != null;
90 }
91
92 public void Reset() {
93 m_current = null;
94 }
85 /// <summary>
86 /// Creates a thin copy of the current linked list.
87 /// </summary>
88 /// <remarks>Iterating over the snapshot is thread safe and
89 /// will produce repeatble results. Each snapshot stores only
90 /// two references one for the first and one for last elements
91 /// from list.
92 /// <returns>Enumerable collection.</returns>
93 public IEnumerable<T> Snapshot() {
94 var first = m_first;
95 var last = m_last;
95 96
96 object IEnumerator.Current {
97 get {
98 if (m_current == null)
99 throw new InvalidOperationException();
100 return m_current.value;
101 }
102 }
103
104 #endregion
105
106 #region IDisposable implementation
107
108 public void Dispose() {
97 var current = m_first;
98 while(current != m_last) {
99 current = current.next;
100 yield return current.value;
109 101 }
110
111 #endregion
112
113 #region IEnumerator implementation
114
115 public T Current {
116 get {
117 if (m_current == null)
118 throw new InvalidOperationException();
119 return m_current.value;
120 }
121 }
122
123 #endregion
124 102 }
125 103
126 104 public IEnumerator<T> GetEnumerator() {
127 return new Enumerator(m_first);
105 for (var current = m_first.next; current != null; current = current.next) {
106 yield return current.value;
107 }
128 108 }
129 109
130 #endregion
131
132 #region IEnumerable implementation
133
134 110 IEnumerator IEnumerable.GetEnumerator() {
135 111 return GetEnumerator();
136 112 }
137 113
138 #endregion
139 114 }
140 115 }
1 NO CONTENT: file was removed
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now