public TelemetryListener(EventSubscriptionWrapper espw, TelemetrySubscription ts, IConnect connect, NotificationListener notificationListener) { _connect = connect; _eventSubscriptionWrapper = espw; _telemetrySubscription = ts; _notificationListener = notificationListener; }
/// <summary> /// Override abstract method /// </summary> public override void WriteTelemetry(string telemetryName, object arguments) { for (TelemetrySubscription curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) { curSubscription.Observer.OnNext(new KeyValuePair <string, object>(telemetryName, arguments)); } }
// NotificationSource implementation /// <summary> /// Override abstract method /// </summary> public override bool IsEnabled(string telemetryName) { for (TelemetrySubscription curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) { if (curSubscription.IsEnabled == null || curSubscription.IsEnabled(telemetryName)) { return(true); } } return(false); }
/// <summary> /// Clean up the NotificationListeners. Notification listeners do NOT DIE ON THEIR OWN /// because they are in a global list (for discoverability). You must dispose them explicitly. /// Note that we do not do the Dispose(bool) pattern because we frankly don't want to support /// subclasses that have non-managed state. /// </summary> virtual public void Dispose() { // Remove myself from the list of all listeners. lock (DefaultListener) { if (_disposed) { return; } _disposed = true; if (s_allListeners == this) { s_allListeners = s_allListeners._next; } else { var cur = s_allListeners; while (cur != null) { if (cur._next == this) { cur._next = _next; break; } cur = cur._next; } } _next = null; } // Indicate completion to all subscribers. TelemetrySubscription subscriber = null; Interlocked.Exchange(ref subscriber, _subscriptions); while (subscriber != null) { subscriber.Observer.OnCompleted(); subscriber = subscriber.Next; } // The code above also nulled out all subscriptions. }
// Subscription implementation /// <summary> /// Add a subscriber (Observer). If 'IsEnabled' == null (or not present), then the Source's IsEnabled /// will always return true. /// </summary> virtual public IDisposable Subscribe(IObserver <KeyValuePair <string, object> > observer, Predicate <string> isEnabled) { // If we have been disposed, we silently ignore any subscriptions. if (_disposed) { return(new TelemetrySubscription() { Owner = this }); } TelemetrySubscription newSubscription = new TelemetrySubscription() { Observer = observer, IsEnabled = isEnabled, Owner = this, Next = _subscriptions }; while (Interlocked.CompareExchange(ref _subscriptions, newSubscription, newSubscription.Next) != newSubscription.Next) { newSubscription.Next = _subscriptions; } return(newSubscription); }
internal TelemetrySubscription Next; // Linked list of subscribers public void Dispose() { // TO keep this lock free and easy to analyze, the linked list is READ ONLY. Thus we copy for (;;) { TelemetrySubscription subscriptions = Owner._subscriptions; TelemetrySubscription newSubscriptions = Remove(subscriptions, this); // Make a new list, with myself removed. // try to update, but if someone beat us to it, then retry. if (Interlocked.CompareExchange(ref Owner._subscriptions, newSubscriptions, subscriptions) == subscriptions) { #if DEBUG var cur = newSubscriptions; while (cur != null) { Debug.Assert(!(cur.Observer == Observer && cur.IsEnabled == IsEnabled), "Did not remove subscription!"); cur = cur.Next; } #endif break; } } }
// Create a new linked list where 'subscription has been removed from the linked list of 'subscriptions'. private static TelemetrySubscription Remove(TelemetrySubscription subscriptions, TelemetrySubscription subscription) { if (subscriptions == null) { // May happen if the IDisposable returned from Subscribe is Dispose'd again return(null); } if (subscriptions.Observer == subscription.Observer && subscriptions.IsEnabled == subscription.IsEnabled) { return(subscriptions.Next); } #if DEBUG // Delay a bit. This makes it more likely that races will happen. for (int i = 0; i < 100; i++) { GC.KeepAlive(""); } #endif return(new TelemetrySubscription() { Observer = subscriptions.Observer, Owner = subscriptions.Owner, IsEnabled = subscriptions.IsEnabled, Next = Remove(subscriptions.Next, subscription) }); }
// Create a new linked list where 'subscription has been removed from the linked list of 'subscriptions'. private static TelemetrySubscription Remove(TelemetrySubscription subscriptions, TelemetrySubscription subscription) { if (subscriptions == null) { Debug.Assert(false, "Could not find subscription"); return(null); } if (subscriptions.Observer == subscription.Observer && subscriptions.IsEnabled == subscription.IsEnabled) { return(subscriptions.Next); } #if DEBUG // Delay a bit. This makes it more likely that races will happen. for (int i = 0; i < 100; i++) { GC.KeepAlive(""); } #endif return(new TelemetrySubscription() { Observer = subscriptions.Observer, Owner = subscriptions.Owner, IsEnabled = subscriptions.IsEnabled, Next = Remove(subscriptions.Next, subscription) }); }