using System; using System.Collections.Generic; using System.Diagnostics; using Implab.Parallels; namespace Implab.Diagnostics { public class TraceRegistry { class Subscription : IDisposable { readonly WeakReference m_registry; readonly Action m_unsubscribe; public Subscription(TraceRegistry registry) { m_registry = new WeakReference(registry); } public void Dispose() { TraceRegistry t; if (m_registry.TryGetTarget(out t)) t.RemoveSubscription(this); } } public static TraceRegistry Global { get; } = new TraceRegistry(); readonly object m_lock = new object(); readonly Dictionary> m_subscriptions = new Dictionary>(); readonly SimpleAsyncQueue m_channels = new SimpleAsyncQueue(); internal void Register(TraceChannel 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(this); IEnumerable snap; // lock to ensure that no new channels will be added // while the subscription is added lock(m_lock) { m_subscriptions.Add(cookie, handler); snap = m_channels.Snapshot(); } // announce previously declared channels if required if (existing) { foreach(var c in snap) handler(c); } // return the subscription return cookie; } void RemoveSubscription(object cookie) { lock(m_lock) m_subscriptions.Remove(cookie); } } }