xronos /Microsoft.Scripting.Core/Utils/WeakUniqueSet.cs

Language C# Lines 190
MD5 Hash 4834dbe07bbff968050743d8784c8eae Estimated Cost $3,095 (why?)
Repository https://bitbucket.org/stefanrusek/xronos View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/
using System; using Microsoft;


using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

#if CODEPLEX_40
namespace System.Dynamic.Utils {
#else
namespace Microsoft.Scripting.Utils {
#endif
    internal sealed class WeakUniqueSet<T> where T : class {
        private readonly Dictionary<int, WeakLinkedList> hashTable = new Dictionary<int, WeakLinkedList>();

        internal T GetUniqueFor(T obj) {
            CheckCleanup();

            int hash = obj.GetHashCode();
            WeakLinkedList list;

            // LOCK table to get/set the list
            lock (hashTable) {
                if (!hashTable.TryGetValue(hash, out list)) {
                    hashTable[hash] = list = new WeakLinkedList();
                    list.Add(obj);
                    return obj;
                }
            }

            var objType = obj.GetType();

            // LOCK list to iterate
            lock (list) {
                foreach (var current in list) {
                    Debug.Assert(current != null);
                    if (current.GetType() == objType && current.Equals(obj)) {
                        return current as T;
                    }
                }
                list.Add(obj);
            }
            return obj;
        }



        #region TableCleanup

        int sinceLastChange;

#if SILVERLIGHT // GC
        WeakReference cleanupGC = new WeakReference(new object());
#else
        int cleanupGC = 0;
#endif

        /// <summary>
        /// Check if any of the keys have gotten collected
        /// </summary>
        private void CheckCleanup() {
            sinceLastChange++;

            // Cleanup the table if it is a while since we have done it last time.
            // Take the size of the table into account.
            if (sinceLastChange > 1234 + hashTable.Count / 2) {
                if (Interlocked.Exchange(ref sinceLastChange, 0) < 1234) {
                    return; // someone is already cleaning
                };

                bool HasCollectionSinceLastCleanup;
#if SILVERLIGHT // GC.CollectionCount
                HasCollectionSinceLastCleanup = !cleanupGC.IsAlive;
                if (HasCollectionSinceLastCleanup) cleanupGC = new WeakReference(new object());
#else
                int currentGC = GC.CollectionCount(2);
                HasCollectionSinceLastCleanup = currentGC != cleanupGC;
                if (HasCollectionSinceLastCleanup) cleanupGC = currentGC;
#endif
                if (HasCollectionSinceLastCleanup) {
                    CleanTable();
                }
            }
        }

        // remove empty lists.
        internal void CleanTable() {
            var keys = hashTable.Keys.ToReadOnly();
            // LOCK table to get lists.
            lock (hashTable) {
                foreach (int hash in keys) {
                    WeakLinkedList list = hashTable[hash];
                    int cnt;

                    // LOCK list to iterate.
                    lock (list) {
                        cnt = list.Count();
                    }

                    if (cnt == 0) {
                        hashTable.Remove(hash);
                    }
                }
            }
        }

        #endregion
    }


    // self-compacting list of weak values
    internal class WeakLinkedList : IEnumerable {
        private class Node {
            internal Node next;
            internal readonly WeakReference value;

            internal Node(object item) {
                value = new WeakReference(item);
            }
        }

        private Node _head;

        internal void Add(object item) {
            Node newNode = new Node(item);
            newNode.next = _head;
            _head = newNode;
        }

        // O(n) operation. will drop dead nodes and report number of live ones.
        internal int Count() {
            int i = 0;
            foreach (var cur in this) {
                i++;
            }
            return i;
        }

        // enumerator for the list. Skips and drops dead nodes.
        IEnumerator IEnumerable.GetEnumerator() {
            Node cur1 = _head;
            object value = null;

            while (cur1 != null && (value = cur1.value.Target) == null) {
                cur1 = cur1.next;
            }
            // Invariant: cur1 ponts to null or the first live node and value has its Target.

            if (cur1 == null) {
                _head = cur1;
                yield break;
            }

            yield return value;

            Node cur2;
            do {
                // Invariant: cur1 ponts to last known live node, 
                cur2 = cur1.next;
                while (cur2 != null && (value = cur2.value.Target) == null) {
                    cur2 = cur2.next;
                }
                // Invariant: cur2 ponts to null or the next live node and value has its Target.
                if (cur2 == null) {
                    cur1.next = cur2;
                    yield break;
                }

                yield return value;
                cur1 = cur2;
            } while (cur1 != null);
        }
    }
}
Back to Top