TraceRegistry.cs
85 lines
| 2.8 KiB
| text/x-csharp
|
CSharpLexer
|
|
r286 | using System; | |
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| using Implab.Parallels; | |||
| namespace Implab.Diagnostics { | |||
|
|
r288 | public class TraceRegistry: IObservable<TraceChannel> { | |
|
|
r286 | ||
| class Subscription : IDisposable { | |||
| readonly WeakReference<TraceRegistry> m_registry; | |||
| public Subscription(TraceRegistry registry) { | |||
| m_registry = new WeakReference<TraceRegistry>(registry); | |||
| } | |||
| public void Dispose() { | |||
| TraceRegistry t; | |||
| if (m_registry.TryGetTarget(out t)) | |||
| t.RemoveSubscription(this); | |||
| } | |||
| } | |||
|
|
r288 | /// <summary> | |
| /// The global collection of available diagnostic channels | |||
| /// </summary> | |||
| /// <returns></returns> | |||
|
|
r286 | public static TraceRegistry Global { get; } = new TraceRegistry(); | |
| readonly object m_lock = new object(); | |||
|
|
r288 | readonly Dictionary<object, IObserver<TraceChannel>> m_subscriptions = new Dictionary<object, IObserver<TraceChannel>>(); | |
|
|
r286 | readonly SimpleAsyncQueue<TraceChannel> m_channels = new SimpleAsyncQueue<TraceChannel>(); | |
|
|
r288 | public void Register(TraceChannel channel) { | |
|
|
r286 | // notifications can run in parallel | |
|
|
r288 | IObserver<TraceChannel>[] handlers = null; | |
|
|
r286 | ||
| lock(m_lock) { | |||
| m_channels.Enqueue(channel); | |||
| if (m_subscriptions.Count > 0) { | |||
|
|
r288 | handlers = new IObserver<TraceChannel>[m_subscriptions.Count]; | |
|
|
r286 | m_subscriptions.Values.CopyTo(handlers, 0); | |
| } | |||
| } | |||
| if (handlers != null) | |||
| foreach(var h in handlers) | |||
|
|
r288 | h.OnNext(channel); | |
|
|
r286 | } | |
| /// <summary> | |||
| /// Subscribes the specified handler to notifications about new trace | |||
| /// channels | |||
| /// </summary> | |||
| /// <param name="handler"></param> | |||
| /// <returns></returns> | |||
|
|
r288 | public IDisposable Subscribe(IObserver<TraceChannel> handler) { | |
|
|
r286 | Safe.ArgumentNotNull(handler, nameof(handler)); | |
| var cookie = new Subscription(this); | |||
| IEnumerable<TraceChannel> 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 | |||
|
|
r288 | if (snap != null) { | |
|
|
r286 | foreach(var c in snap) | |
|
|
r288 | handler.OnNext(c); | |
|
|
r286 | } | |
| // return the subscription | |||
| return cookie; | |||
| } | |||
| void RemoveSubscription(object cookie) { | |||
| lock(m_lock) | |||
| m_subscriptions.Remove(cookie); | |||
| } | |||
| } | |||
| } |
