/// <summary> /// Add a listener.</summary> /// <param name="listener">The listener to add</param> public void Add(IWeakEventListener listener) { // make sure list is ready for writing ListenerList.PrepareForWriting(ref m_listeners); m_listeners.Add(listener); }
// remove a listener public void Remove(IWeakEventListener listener, EventHandler <ValueChangedEventArgs> handler) { // make sure list is ready for writing ListenerList list = _listeners; if (ListenerList.PrepareForWriting(ref list)) { _listeners = (ListenerList <ValueChangedEventArgs>)list; } if (handler != null) { _listeners.RemoveHandler(handler); } else { _listeners.Remove(listener); } // when the last listener goes away, remove the callback if (_listeners.IsEmpty) { StopListening(); } }
public bool Purge() { ListenerList list = _list; if (ListenerList.PrepareForWriting(ref list)) { _list = (ListenerList <PropertyChangedEventArgs>)list; } return(_list.Purge()); }
// purge dead entries public bool Purge() { ListenerList list = _listeners; if (ListenerList.PrepareForWriting(ref list)) { _listeners = (ListenerList <ValueChangedEventArgs>)list; } return(_listeners.Purge()); }
public void RemoveHandler(EventHandler <PropertyChangedEventArgs> handler) { // make sure list is ready for writing ListenerList list = _list; if (ListenerList.PrepareForWriting(ref list)) { _list = (ListenerList <PropertyChangedEventArgs>)list; } // remove a listener from the list _list.RemoveHandler(handler); }
/// <summary> /// Remove a listener.</summary> /// <param name="listener">The listener to remove</param> public void Remove(IWeakEventListener listener) { // make sure list is ready for writing ListenerList.PrepareForWriting(ref m_listeners); m_listeners.Remove(listener); // when the last listener goes away, remove the callback if (m_listeners.IsEmpty) { StopListening(); } }
private void AddListener(INotifyPropertyChanged source, string propertyName, IWeakEventListener listener, EventHandler <PropertyChangedEventArgs> handler) { using (WriteLock) { HybridDictionary dict = (HybridDictionary)this[source]; if (dict == null) { // no entry in the hashtable - add a new one dict = new HybridDictionary(true /* case insensitive */); this[source] = dict; // listen for the desired events StartListening(source); } ListenerList list = (ListenerList)dict[propertyName]; if (list == null) { // no entry in the dictionary - add a new one list = new ListenerList <PropertyChangedEventArgs>(); dict[propertyName] = list; } // make sure list is ready for writing if (ListenerList.PrepareForWriting(ref list)) { dict[propertyName] = list; } // add a listener to the list if (handler != null) { ListenerList <PropertyChangedEventArgs> hlist = (ListenerList <PropertyChangedEventArgs>)list; hlist.AddHandler(handler); } else { list.Add(listener); } dict.Remove(AllListenersKey); // invalidate list of all listeners _proposedAllListenersList = null; // schedule a cleanup pass ScheduleCleanup(); } }
private void RemoveListener(INotifyPropertyChanged source, string propertyName, IWeakEventListener listener, EventHandler <PropertyChangedEventArgs> handler) { using (WriteLock) { HybridDictionary dict = (HybridDictionary)this[source]; if (dict != null) { ListenerList list = (ListenerList)dict[propertyName]; if (list != null) { // make sure list is ready for writing if (ListenerList.PrepareForWriting(ref list)) { dict[propertyName] = list; } // remove a listener from the list if (handler != null) { ListenerList <PropertyChangedEventArgs> hlist = (ListenerList <PropertyChangedEventArgs>)list; hlist.RemoveHandler(handler); } else { list.Remove(listener); } // when the last listener goes away, remove the list if (list.IsEmpty) { dict.Remove(propertyName); } } if (dict.Count == 0) { StopListening(source); Remove(source); } dict.Remove(AllListenersKey); // invalidate list of all listeners _proposedAllListenersList = null; } } }
internal void Remove(IWeakEventListener listener) { if (listener == null) { return; } ListenerList.PrepareForWriting(ref _listeners); _listeners.Remove(listener); if (_listeners.IsEmpty) { StopListening(); } }
// add a listener public void Add(IWeakEventListener listener, EventHandler <ValueChangedEventArgs> handler) { // make sure list is ready for writing ListenerList list = _listeners; if (ListenerList.PrepareForWriting(ref list)) { _listeners = (ListenerList <ValueChangedEventArgs>)list; } if (handler != null) { _listeners.AddHandler(handler); } else { _listeners.Add(listener); } }
/// <summary> /// Remove dead entries from the data for the given source. Returns true if /// some entries were actually removed. /// </summary> protected virtual bool Purge(object source, object data, bool purgeAll) { bool foundDirt = false; bool removeList = purgeAll || source == null; // remove dead entries from the list if (!removeList) { ListenerList list = (ListenerList)data; if (ListenerList.PrepareForWriting(ref list) && source != null) { Table[this, source] = list; } if (list.Purge()) { foundDirt = true; } removeList = list.IsEmpty; } // if the list is no longer needed, stop listening to the event if (removeList) { if (source != null) // source may have been GC'd { StopListening(source); // remove the list completely (in the purgeAll case, we'll do it later) if (!purgeAll) { Table.Remove(this, source); foundDirt = true; } } } return(foundDirt); }
private void AddListener(object source, IWeakEventListener listener, Delegate handler) { object sourceKey = (source != null) ? source : StaticSource; using (Table.WriteLock) { ListenerList list = (ListenerList)Table[this, sourceKey]; if (list == null) { // no entry in the table - add a new one list = NewListenerList(); Table[this, sourceKey] = list; // listen for the desired event StartListening(source); } // make sure list is ready for writing if (ListenerList.PrepareForWriting(ref list)) { Table[this, source] = list; } // add a target to the list of listeners if (handler != null) { list.AddHandler(handler); } else { list.Add(listener); } // schedule a cleanup pass (heuristic (b) described above) ScheduleCleanup(); } }
private void RemoveListener(object source, object target, Delegate handler) { object sourceKey = (source != null) ? source : StaticSource; using (Table.WriteLock) { ListenerList list = (ListenerList)Table[this, sourceKey]; if (list != null) { // make sure list is ready for writing if (ListenerList.PrepareForWriting(ref list)) { Table[this, sourceKey] = list; } // remove the target from the list of listeners if (handler != null) { list.RemoveHandler(handler); } else { list.Remove((IWeakEventListener)target); } // after removing the last listener, stop listening if (list.IsEmpty) { Table.Remove(this, sourceKey); StopListening(source); } } } }
/// <summary> /// Remove dead entries from the data for the given source. Returns true if /// some entries were actually removed. /// </summary> protected override bool Purge(object source, object data, bool purgeAll) { bool foundDirt = false; if (!purgeAll) { HybridDictionary dict = (HybridDictionary)data; int ignoredKeys = 0; if (!BaseAppContextSwitches.EnableWeakEventMemoryImprovements) { // copy the keys into a separate array, so that later on // we can change the dictionary while iterating over the keys ICollection ic = dict.Keys; String[] keys = new String[ic.Count]; ic.CopyTo(keys, 0); for (int i = keys.Length - 1; i >= 0; --i) { if (keys[i] == AllListenersKey) { ++ignoredKeys; continue; // ignore the special entry for now } // for each key, remove dead entries in its list bool removeList = purgeAll || source == null; if (!removeList) { ListenerList list = (ListenerList)dict[keys[i]]; if (ListenerList.PrepareForWriting(ref list)) { dict[keys[i]] = list; } if (list.Purge()) { foundDirt = true; } removeList = (list.IsEmpty); } // if there are no more entries, remove the key if (removeList) { dict.Remove(keys[i]); } } #if WeakEventTelemetry LogAllocation(ic.GetType(), 1, 12); // dict.Keys - Hashtable+KeyCollection LogAllocation(typeof(String[]), 1, 12 + ic.Count * 4); // keys #endif } else { Debug.Assert(_toRemove.Count == 0, "to-remove list should be empty"); // enumerate the dictionary using IDE explicitly rather than // foreach, to avoid allocating temporary DictionaryEntry objects IDictionaryEnumerator ide = dict.GetEnumerator() as IDictionaryEnumerator; while (ide.MoveNext()) { String key = (String)ide.Key; if (key == AllListenersKey) { ++ignoredKeys; continue; // ignore the special entry for now } // for each key, remove dead entries in its list bool removeList = purgeAll || source == null; if (!removeList) { ListenerList list = (ListenerList)ide.Value; if (ListenerList.PrepareForWriting(ref list)) { dict[key] = list; } if (list.Purge()) { foundDirt = true; } removeList = (list.IsEmpty); } // if there are no more entries, remove the key if (removeList) { _toRemove.Add(key); } } // do the actual removal (outside the dictionary iteration) if (_toRemove.Count > 0) { foreach (String key in _toRemove) { dict.Remove(key); } _toRemove.Clear(); _toRemove.TrimExcess(); } #if WeakEventTelemetry Type enumeratorType = ide.GetType(); if (enumeratorType.Name.IndexOf("NodeEnumerator") >= 0) { LogAllocation(enumeratorType, 1, 24); // ListDictionary+NodeEnumerator } else { LogAllocation(enumeratorType, 1, 36); // Hashtable+HashtableEnumerator } #endif } if (dict.Count == ignoredKeys) { // if there are no more listeners at all, remove the entry from // the main table, and prepare to stop listening purgeAll = true; if (source != null) // source may have been GC'd { this.Remove(source); } } else if (foundDirt) { // if any entries were purged, invalidate the special entry dict.Remove(AllListenersKey); _proposedAllListenersList = null; } } if (purgeAll) { // stop listening. List cleanup is handled by Purge() if (source != null) // source may have been GC'd { StopListening(source); } foundDirt = true; } return(foundDirt); }
internal void Add(IWeakEventListener listener) { ListenerList.PrepareForWriting(ref _listeners); _listeners.Add(listener); }
internal bool Purge() { ListenerList.PrepareForWriting(ref _listeners); return(_listeners.Purge()); }
/// <summary> /// Remove dead entries from the data for the given source. Returns true if /// some entries were actually removed. /// </summary> protected override bool Purge(object source, object data, bool purgeAll) { bool foundDirt = false; if (!purgeAll) { HybridDictionary dict = (HybridDictionary)data; // copy the keys into a separate array, so that later on // we can change the dictionary while iterating over the keys ICollection ic = dict.Keys; String[] keys = new String[ic.Count]; ic.CopyTo(keys, 0); for (int i = keys.Length - 1; i >= 0; --i) { if (keys[i] == AllListenersKey) { continue; // ignore the special entry for now } // for each key, remove dead entries in its list bool removeList = purgeAll || source == null; if (!removeList) { ListenerList list = (ListenerList)dict[keys[i]]; if (ListenerList.PrepareForWriting(ref list)) { dict[keys[i]] = list; } if (list.Purge()) { foundDirt = true; } removeList = (list.IsEmpty); } // if there are no more entries, remove the key if (removeList) { dict.Remove(keys[i]); } } if (dict.Count == 0) { // if there are no more listeners at all, remove the entry from // the main table, and prepare to stop listening purgeAll = true; if (source != null) // source may have been GC'd { this.Remove(source); } } else if (foundDirt) { // if any entries were purged, invalidate the special entry dict.Remove(AllListenersKey); _proposedAllListenersList = null; } } if (purgeAll) { // stop listening. List cleanup is handled by Purge() if (source != null) // source may have been GC'd { StopListening(source); } foundDirt = true; } return(foundDirt); }
/// <summary> /// Remove dead entries from the data for the given source. Returns true if /// some entries were actually removed. /// </summary> protected override bool Purge(object source, object data, bool purgeAll) { bool foundDirt = false; if (!purgeAll) { HybridDictionary dict = (HybridDictionary)data; int ignoredKeys = 0; if (!BaseAppContextSwitches.EnableWeakEventMemoryImprovements) { // copy the keys into a separate array, so that later on // we can change the dictionary while iterating over the keys ICollection ic = dict.Keys; String[] keys = new String[ic.Count]; ic.CopyTo(keys, 0); for (int i = keys.Length - 1; i >= 0; --i) { if (keys[i] == AllListenersKey) { ++ignoredKeys; continue; // ignore the special entry for now } // for each key, remove dead entries in its list bool removeList = purgeAll || source == null; if (!removeList) { ListenerList list = (ListenerList)dict[keys[i]]; if (ListenerList.PrepareForWriting(ref list)) { dict[keys[i]] = list; } if (list.Purge()) { foundDirt = true; } removeList = (list.IsEmpty); } // if there are no more entries, remove the key if (removeList) { dict.Remove(keys[i]); } } #if WeakEventTelemetry LogAllocation(ic.GetType(), 1, 12); // dict.Keys - Hashtable+KeyCollection LogAllocation(typeof(String[]), 1, 12 + ic.Count * 4); // keys #endif } else { Debug.Assert(_toRemove.Count == 0, "to-remove list should be empty"); // If an "in-use" list is changed, we will re-install its clone // back into the dictionary. Doing this inside the loop // causes an exception "collection was modified after the enumerator // was instantiated" (DDVSO 812614), so instead just record // what to do and do the actual work after the loop. // This is a rare case - it only arises if a PropertyChanged event // handler calls (indirectly) into the cleanup code - so allocate // the temporary memory lazily on the stack. // [In the bug, the indirect call comes about because the app's // event handler calls ShowDialog(), which pushes a dispatcher // frame to run a nested message pump. Then a pending cleanup task // reaches the front of the dispatcher queue before the dialog // is dismissed, effectively calling this method while the // PropertyChanged event delivery is in progress.] HybridDictionary toInstall = null; // enumerate the dictionary using IDE explicitly rather than // foreach, to avoid allocating temporary DictionaryEntry objects IDictionaryEnumerator ide = dict.GetEnumerator() as IDictionaryEnumerator; while (ide.MoveNext()) { String key = (String)ide.Key; if (key == AllListenersKey) { ++ignoredKeys; continue; // ignore the special entry for now } // for each key, remove dead entries in its list bool removeList = purgeAll || source == null; if (!removeList) { ListenerList list = (ListenerList)ide.Value; bool inUse = ListenerList.PrepareForWriting(ref list); bool isChanged = false; if (list.Purge()) { isChanged = true; foundDirt = true; } removeList = (list.IsEmpty); // if a cloned list changed, remember the details // so that the clone can be installed back into the // dictionary outside the iteration (DDVSO 812614) if (!removeList && inUse && isChanged) { if (toInstall == null) { // lazy allocation toInstall = new HybridDictionary(); } toInstall[key] = list; } } // if there are no more entries, remove the key if (removeList) { _toRemove.Add(key); } } // do the actual removal (outside the dictionary iteration) if (_toRemove.Count > 0) { foreach (String key in _toRemove) { dict.Remove(key); } _toRemove.Clear(); _toRemove.TrimExcess(); } // do the actual re-install of "in-use" lists that changed if (toInstall != null) { IDictionaryEnumerator installDE = toInstall.GetEnumerator() as IDictionaryEnumerator; while (installDE.MoveNext()) { String key = (String)installDE.Key; ListenerList list = (ListenerList)installDE.Value; dict[key] = list; } } #if WeakEventTelemetry Type enumeratorType = ide.GetType(); if (enumeratorType.Name.IndexOf("NodeEnumerator") >= 0) { LogAllocation(enumeratorType, 1, 24); // ListDictionary+NodeEnumerator } else { LogAllocation(enumeratorType, 1, 36); // Hashtable+HashtableEnumerator } #endif } if (dict.Count == ignoredKeys) { // if there are no more listeners at all, remove the entry from // the main table, and prepare to stop listening purgeAll = true; if (source != null) // source may have been GC'd { this.Remove(source); } } else if (foundDirt) { // if any entries were purged, invalidate the special entry dict.Remove(AllListenersKey); _proposedAllListenersList = null; } } if (purgeAll) { // stop listening. List cleanup is handled by Purge() if (source != null) // source may have been GC'd { StopListening(source); } foundDirt = true; } return(foundDirt); }
/// <summary> /// Purge dead entries.</summary> /// <returns>True if any entries were purged, otherwise false</returns> public bool Purge() { ListenerList.PrepareForWriting(ref m_listeners); return(m_listeners.Purge()); }