/// <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);
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        /// <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);
        }
Beispiel #4
0
 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;
                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);
        }