// Create a new linked list where 'subscription has been removed from the linked list of 'subscriptions'. private static DiagnosticSubscription Remove(DiagnosticSubscription subscriptions, DiagnosticSubscription subscription) { if (subscriptions == null) { // May happen if the IDisposable returned from Subscribe is Dispose'd again return(null); } if (subscriptions.Observer == subscription.Observer && subscriptions.IsEnabled1Arg == subscription.IsEnabled1Arg && subscriptions.IsEnabled3Arg == subscription.IsEnabled3Arg) { 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 DiagnosticSubscription() { Observer = subscriptions.Observer, Owner = subscriptions.Owner, IsEnabled1Arg = subscriptions.IsEnabled1Arg, IsEnabled3Arg = subscriptions.IsEnabled3Arg, Next = Remove(subscriptions.Next, subscription) }); }
private IDisposable SubscribeInternal(IObserver <KeyValuePair <string, object> > observer, Predicate <string> isEnabled1Arg, Func <string, object, object, bool> isEnabled3Arg, Action <Activity, object> onActivityImport, Action <Activity, object> onActivityExport) { // If we have been disposed, we silently ignore any subscriptions. if (_disposed) { return(new DiagnosticSubscription() { Owner = this }); } DiagnosticSubscription newSubscription = new DiagnosticSubscription() { Observer = observer, IsEnabled1Arg = isEnabled1Arg, IsEnabled3Arg = isEnabled3Arg, OnActivityImport = onActivityImport, OnActivityExport = onActivityExport, Owner = this, Next = _subscriptions }; while (Interlocked.CompareExchange(ref _subscriptions, newSubscription, newSubscription.Next) != newSubscription.Next) { newSubscription.Next = _subscriptions; } return(newSubscription); }
/// <summary> /// Override abstract method /// </summary> public override void Write(string name, object value) { for (DiagnosticSubscription curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) { curSubscription.Observer.OnNext(new KeyValuePair <string, object>(name, value)); } }
public override void OnActivityExport(Activity activity, object payload) { for (DiagnosticSubscription curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) { curSubscription.OnActivityExport?.Invoke(activity, payload); } }
// NotificationSource implementation /// <summary> /// Override abstract method /// </summary> public override bool IsEnabled(string name, object arg1, object arg2 = null) { for (DiagnosticSubscription curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) { if (curSubscription.IsEnabled3Arg == null || curSubscription.IsEnabled3Arg(name, arg1, arg2)) { return(true); } } return(false); }
// NotificationSource implementation /// <summary> /// Override abstract method /// </summary> public override bool IsEnabled(string name) { for (DiagnosticSubscription curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) { if (curSubscription.IsEnabled == null || curSubscription.IsEnabled(name)) { 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> public virtual void Dispose() { // Remove myself from the list of all listeners. lock (s_allListenersLock) { 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. DiagnosticSubscription 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 DiagnosticSubscription() { Owner = this }); } DiagnosticSubscription newSubscription = new DiagnosticSubscription() { Observer = observer, IsEnabled = isEnabled, Owner = this, Next = _subscriptions }; while (Interlocked.CompareExchange(ref _subscriptions, newSubscription, newSubscription.Next) != newSubscription.Next) { newSubscription.Next = _subscriptions; } return(newSubscription); }
internal DiagnosticSubscription 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 (; ;) { DiagnosticSubscription subscriptions = Owner._subscriptions; DiagnosticSubscription 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.IsEnabled1Arg == IsEnabled1Arg && cur.IsEnabled3Arg == IsEnabled3Arg), "Did not remove subscription!"); cur = cur.Next; } #endif break; } } }