PageRenderTime 83ms CodeModel.GetById 40ms app.highlight 9ms RepoModel.GetById 31ms app.codeStats 0ms

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

https://bitbucket.org/stefanrusek/xronos
C# | 189 lines | 127 code | 32 blank | 30 comment | 27 complexity | 4834dbe07bbff968050743d8784c8eae MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15using System; using Microsoft;
 16
 17
 18using System.Collections;
 19using System.Collections.Generic;
 20using System.Diagnostics;
 21using System.Threading;
 22
 23#if CODEPLEX_40
 24namespace System.Dynamic.Utils {
 25#else
 26namespace Microsoft.Scripting.Utils {
 27#endif
 28    internal sealed class WeakUniqueSet<T> where T : class {
 29        private readonly Dictionary<int, WeakLinkedList> hashTable = new Dictionary<int, WeakLinkedList>();
 30
 31        internal T GetUniqueFor(T obj) {
 32            CheckCleanup();
 33
 34            int hash = obj.GetHashCode();
 35            WeakLinkedList list;
 36
 37            // LOCK table to get/set the list
 38            lock (hashTable) {
 39                if (!hashTable.TryGetValue(hash, out list)) {
 40                    hashTable[hash] = list = new WeakLinkedList();
 41                    list.Add(obj);
 42                    return obj;
 43                }
 44            }
 45
 46            var objType = obj.GetType();
 47
 48            // LOCK list to iterate
 49            lock (list) {
 50                foreach (var current in list) {
 51                    Debug.Assert(current != null);
 52                    if (current.GetType() == objType && current.Equals(obj)) {
 53                        return current as T;
 54                    }
 55                }
 56                list.Add(obj);
 57            }
 58            return obj;
 59        }
 60
 61
 62
 63        #region TableCleanup
 64
 65        int sinceLastChange;
 66
 67#if SILVERLIGHT // GC
 68        WeakReference cleanupGC = new WeakReference(new object());
 69#else
 70        int cleanupGC = 0;
 71#endif
 72
 73        /// <summary>
 74        /// Check if any of the keys have gotten collected
 75        /// </summary>
 76        private void CheckCleanup() {
 77            sinceLastChange++;
 78
 79            // Cleanup the table if it is a while since we have done it last time.
 80            // Take the size of the table into account.
 81            if (sinceLastChange > 1234 + hashTable.Count / 2) {
 82                if (Interlocked.Exchange(ref sinceLastChange, 0) < 1234) {
 83                    return; // someone is already cleaning
 84                };
 85
 86                bool HasCollectionSinceLastCleanup;
 87#if SILVERLIGHT // GC.CollectionCount
 88                HasCollectionSinceLastCleanup = !cleanupGC.IsAlive;
 89                if (HasCollectionSinceLastCleanup) cleanupGC = new WeakReference(new object());
 90#else
 91                int currentGC = GC.CollectionCount(2);
 92                HasCollectionSinceLastCleanup = currentGC != cleanupGC;
 93                if (HasCollectionSinceLastCleanup) cleanupGC = currentGC;
 94#endif
 95                if (HasCollectionSinceLastCleanup) {
 96                    CleanTable();
 97                }
 98            }
 99        }
100
101        // remove empty lists.
102        internal void CleanTable() {
103            var keys = hashTable.Keys.ToReadOnly();
104            // LOCK table to get lists.
105            lock (hashTable) {
106                foreach (int hash in keys) {
107                    WeakLinkedList list = hashTable[hash];
108                    int cnt;
109
110                    // LOCK list to iterate.
111                    lock (list) {
112                        cnt = list.Count();
113                    }
114
115                    if (cnt == 0) {
116                        hashTable.Remove(hash);
117                    }
118                }
119            }
120        }
121
122        #endregion
123    }
124
125
126    // self-compacting list of weak values
127    internal class WeakLinkedList : IEnumerable {
128        private class Node {
129            internal Node next;
130            internal readonly WeakReference value;
131
132            internal Node(object item) {
133                value = new WeakReference(item);
134            }
135        }
136
137        private Node _head;
138
139        internal void Add(object item) {
140            Node newNode = new Node(item);
141            newNode.next = _head;
142            _head = newNode;
143        }
144
145        // O(n) operation. will drop dead nodes and report number of live ones.
146        internal int Count() {
147            int i = 0;
148            foreach (var cur in this) {
149                i++;
150            }
151            return i;
152        }
153
154        // enumerator for the list. Skips and drops dead nodes.
155        IEnumerator IEnumerable.GetEnumerator() {
156            Node cur1 = _head;
157            object value = null;
158
159            while (cur1 != null && (value = cur1.value.Target) == null) {
160                cur1 = cur1.next;
161            }
162            // Invariant: cur1 ponts to null or the first live node and value has its Target.
163
164            if (cur1 == null) {
165                _head = cur1;
166                yield break;
167            }
168
169            yield return value;
170
171            Node cur2;
172            do {
173                // Invariant: cur1 ponts to last known live node, 
174                cur2 = cur1.next;
175                while (cur2 != null && (value = cur2.value.Target) == null) {
176                    cur2 = cur2.next;
177                }
178                // Invariant: cur2 ponts to null or the next live node and value has its Target.
179                if (cur2 == null) {
180                    cur1.next = cur2;
181                    yield break;
182                }
183
184                yield return value;
185                cur1 = cur2;
186            } while (cur1 != null);
187        }
188    }
189}