|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Diagnostics;
|
|
|
using Implab.Parallels;
|
|
|
|
|
|
namespace Implab.Diagnostics {
|
|
|
public class TraceRegistry: IObservable<TraceChannel> {
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// The global collection of available diagnostic channels
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
public static TraceRegistry Global { get; } = new TraceRegistry();
|
|
|
|
|
|
readonly object m_lock = new object();
|
|
|
|
|
|
readonly Dictionary<object, IObserver<TraceChannel>> m_subscriptions = new Dictionary<object, IObserver<TraceChannel>>();
|
|
|
readonly SimpleAsyncQueue<TraceChannel> m_channels = new SimpleAsyncQueue<TraceChannel>();
|
|
|
|
|
|
public void Register(TraceChannel channel) {
|
|
|
// notifications can run in parallel
|
|
|
IObserver<TraceChannel>[] handlers = null;
|
|
|
|
|
|
lock(m_lock) {
|
|
|
m_channels.Enqueue(channel);
|
|
|
if (m_subscriptions.Count > 0) {
|
|
|
handlers = new IObserver<TraceChannel>[m_subscriptions.Count];
|
|
|
m_subscriptions.Values.CopyTo(handlers, 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (handlers != null)
|
|
|
foreach(var h in handlers)
|
|
|
h.OnNext(channel);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Subscribes the specified handler to notifications about new trace
|
|
|
/// channels
|
|
|
/// </summary>
|
|
|
/// <param name="handler"></param>
|
|
|
/// <returns></returns>
|
|
|
public IDisposable Subscribe(IObserver<TraceChannel> handler) {
|
|
|
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
|
|
|
if (snap != null) {
|
|
|
foreach(var c in snap)
|
|
|
handler.OnNext(c);
|
|
|
}
|
|
|
|
|
|
// return the subscription
|
|
|
return cookie;
|
|
|
}
|
|
|
|
|
|
void RemoveSubscription(object cookie) {
|
|
|
lock(m_lock)
|
|
|
m_subscriptions.Remove(cookie);
|
|
|
}
|
|
|
}
|
|
|
}
|