diff --git a/Implab.Test/JsonTests.cs b/Implab.Test/JsonTests.cs --- a/Implab.Test/JsonTests.cs +++ b/Implab.Test/JsonTests.cs @@ -91,7 +91,7 @@ namespace Implab.Test { [Fact] public void JsonXmlReaderSimpleTest() { - var json = "\"some text\""; + //var json = "\"some text\""; //Console.WriteLine($"JSON: {json}"); //Console.WriteLine("XML"); /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) { diff --git a/Implab/Diagnostics/ChannelAdvertisementEventArgs.cs b/Implab/Diagnostics/ChannelAdvertisementEventArgs.cs deleted file mode 100644 --- a/Implab/Diagnostics/ChannelAdvertisementEventArgs.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Diagnostics; - -namespace Implab.Diagnostics -{ - public class ChannelAdvertisementEventArgs : EventArgs { - internal ChannelAdvertisementEventArgs(object channelId, TraceSource source) { - ChannelId = channelId; - Source = source; - } - - public object ChannelId { get; private set; } - - public TraceSource Source { get; private set; } - - } -} \ No newline at end of file diff --git a/Implab/Diagnostics/ChannelInfo.cs b/Implab/Diagnostics/ChannelInfo.cs new file mode 100644 --- /dev/null +++ b/Implab/Diagnostics/ChannelInfo.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; + +namespace Implab.Diagnostics +{ + public class ChannelInfo + { + internal ChannelInfo(object id, TraceSource source) { + Debug.Assert(id != null); + Debug.Assert(source != null); + + Id = id; + Source = source; + + } + + public object Id { get; private set; } + + public TraceSource Source { get; private set; } + + + } +} \ No newline at end of file diff --git a/Implab/Diagnostics/Trace.cs b/Implab/Diagnostics/Trace.cs --- a/Implab/Diagnostics/Trace.cs +++ b/Implab/Diagnostics/Trace.cs @@ -18,16 +18,15 @@ namespace Implab.Diagnostics { static TraceSource CreateChannel() { var id = Interlocked.Increment(ref _nextId); - return new TraceSource(typeof(T).Name); + var trs = new TraceSource(typeof(T).Name); + + TraceChannelRegistry.AllCannels.NotifyChannelCreated(new ChannelInfo(typeof(T), trs)); + + return trs; } public static TraceSource TraceSource { get { return _traceSource.Value; } } - public static IDisposable Subscribe() { - - throw new NotImplementedException(); - } - #if NETFX_TRACE_BUG readonly static AsyncLocal m_currentOperation = new AsyncLocal(); #endif diff --git a/Implab/Diagnostics/TraceChannelRegistry.cs b/Implab/Diagnostics/TraceChannelRegistry.cs new file mode 100644 --- /dev/null +++ b/Implab/Diagnostics/TraceChannelRegistry.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Implab.Parallels; + +namespace Implab.Diagnostics { + public class TraceChannelRegistry { + + class Subscription : IDisposable { + readonly Action m_unsubscribe; + + public Subscription(Action unsubscribe) { + m_unsubscribe = unsubscribe; + } + + public void Dispose() { + m_unsubscribe(this); + } + } + + public static TraceChannelRegistry AllCannels { get; } = new TraceChannelRegistry(); + + readonly object m_lock = new object(); + + readonly Dictionary> m_subscriptions = new Dictionary>(); + readonly SimpleAsyncQueue m_channels = new SimpleAsyncQueue(); + + internal void NotifyChannelCreated(ChannelInfo channel) { + // notifications can run in parallel + Action[] handlers = null; + + lock(m_lock) { + m_channels.Enqueue(channel); + if (m_subscriptions.Count > 0) { + handlers = new Action[m_subscriptions.Count]; + m_subscriptions.Values.CopyTo(handlers, 0); + } + } + + if (handlers != null) + foreach(var h in handlers) + h(channel); + } + + /// + /// Subscribes the specified handler to notifications about new trace + /// channels + /// + /// + /// + public IDisposable Subscribe(Action handler, bool existing) { + Safe.ArgumentNotNull(handler, nameof(handler)); + + var cookie = new Subscription(RemoveSubscription); + + IEnumerable snap; + + lock(m_lock) { + m_subscriptions.Add(cookie, handler); + snap = m_channels.Snapshot(); + } + + if (existing) { + foreach(var c in snap) + handler(c); + } + + return cookie; + } + + void RemoveSubscription(object cookie) { + lock(m_lock) + m_subscriptions.Remove(cookie); + } + } +} \ No newline at end of file diff --git a/Implab/Formats/FastInpurScanner.cs b/Implab/Formats/FastInpurScanner.cs --- a/Implab/Formats/FastInpurScanner.cs +++ b/Implab/Formats/FastInpurScanner.cs @@ -10,15 +10,16 @@ using System.Threading.Tasks; namespace Implab.Formats { /// - /// Fast input scanner for max 255 states and first 255 input chacters. + /// Fast input scanner for max 255 states and 255 input chacters. /// /// /// - /// 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. - /// - /// Each entry is addressed as (state << 8) | input which make this quite fast to get the next state. - /// - /// Each input symbol below 255 is treated as 255. + /// + /// 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. + /// + /// + /// Each entry is addressed as (state << 8) | input which make this quite fast to get the next state. Each input symbol below 255 is treated as 255. + /// /// public class FastInputScanner { const int StateShift = 8; diff --git a/Implab/Parallels/SimpleAsyncQueue.cs b/Implab/Parallels/SimpleAsyncQueue.cs --- a/Implab/Parallels/SimpleAsyncQueue.cs +++ b/Implab/Parallels/SimpleAsyncQueue.cs @@ -4,6 +4,16 @@ using System; using System.Collections; namespace Implab.Parallels { + + /// + /// Very simple thred-safe FIFO queue based on the sinle linked list. + /// + /// + /// + /// This queue uses interlocked operations to add and remove nodes, + /// each node stores a single value. The queue provides mean performance, + /// moderate overhead and situable for a small amount of elements. + /// public class SimpleAsyncQueue : IEnumerable { class Node { public Node(T value) { @@ -13,11 +23,11 @@ namespace Implab.Parallels { public volatile Node next; } - // the reader and the writer are mainteined completely independent, + // the reader and the writer are maintained completely independent, // the reader can read next item when m_first.next is not null // the writer creates a new node, moves m_last to this node and // only after that restores the reference from the previous node - // making the reader be able to read the new node. + // making the reader able to read the new node. volatile Node m_first; // position on the node which is already read volatile Node m_last; // position on the node which is already written @@ -39,8 +49,8 @@ namespace Implab.Parallels { } public bool TryDequeue(out T value) { - Node first = m_first; ; - Node next = first.next; ; + Node first = m_first; + Node next = first.next; if (next == null) { value = default(T); @@ -72,69 +82,34 @@ namespace Implab.Parallels { return true; } - #region IEnumerable implementation - - class Enumerator : IEnumerator { - 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; - } + /// + /// Creates a thin copy of the current linked list. + /// + /// Iterating over the snapshot is thread safe and + /// will produce repeatble results. Each snapshot stores only + /// two references one for the first and one for last elements + /// from list. + /// Enumerable collection. + public IEnumerable Snapshot() { + var first = m_first; + var last = m_last; - object IEnumerator.Current { - get { - if (m_current == null) - throw new InvalidOperationException(); - return m_current.value; - } - } - - #endregion - - #region IDisposable implementation - - public void Dispose() { + var current = m_first; + while(current != m_last) { + current = current.next; + yield return current.value; } - - #endregion - - #region IEnumerator implementation - - public T Current { - get { - if (m_current == null) - throw new InvalidOperationException(); - return m_current.value; - } - } - - #endregion } public IEnumerator GetEnumerator() { - return new Enumerator(m_first); + for (var current = m_first.next; current != null; current = current.next) { + yield return current.value; + } } - #endregion - - #region IEnumerable implementation - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - #endregion } }