bdaffddb1968d9f0a81952ff7942339c520d2130
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / WeakKeyDictionary.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Model
5 {
6
7     using System;
8     using System.Collections;
9     using System.Collections.Generic;
10     using System.Diagnostics.CodeAnalysis;
11     using System.Text;
12     using System.Threading;
13     using System.Runtime.InteropServices;
14
15     internal class WeakKeyDictionary<K, V> : IDictionary<K, V> 
16     {
17         private Dictionary<WeakKey, V> _internalDictionary;
18         private object _sync = new object();
19         private bool _finalized;
20
21         internal WeakKeyDictionary() 
22         {
23             _internalDictionary = new Dictionary<WeakKey, V>(new WeakComparer());
24         }
25
26         public WeakKeyDictionary(IEqualityComparer<K> comparer)  
27         {
28             _internalDictionary = new Dictionary<WeakKey, V>(new WeakComparer(comparer));
29         }
30
31         // FXCop: this is not empty; we need to mark this so we know if a key
32         // still has an active dictionary at its finalization.
33         [SuppressMessage("Microsoft.Performance", "CA1821:RemoveEmptyFinalizers")]
34         ~WeakKeyDictionary() 
35         {
36             _finalized = true;
37         }
38
39         public ICollection<K> Keys 
40         {
41             get 
42             {
43                 List<K> list = new List<K>();
44                 lock (_sync) 
45                 {
46                     foreach (WeakKey key in _internalDictionary.Keys) 
47                     {
48                         object k = key.Target;
49                         if (k != null)
50                         {
51                             list.Add((K)k);
52                         }
53                     }
54                 }
55                 return list;
56             }
57         }
58
59         public ICollection<V> Values 
60         {
61             get {
62                 lock (_sync) {
63                     // make a copy of the values, so the during GC, the returned collection does not change.
64                     return new List<V>(_internalDictionary.Values);
65                 }
66             }
67         }
68
69         public int Count 
70         {
71             get 
72             {
73                 // Ensure a fairly accurate count.
74                 ScavangeLostKeys();
75                 lock (_sync) 
76                 {
77                     return _internalDictionary.Count;
78                 }
79             }
80         }
81
82         public bool IsReadOnly 
83         {
84             get {
85                 return false;
86             }
87         }
88
89         [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "LostKeyFinder's purpose is to get garbage collected as soon as posible")]
90         public V this[K key] 
91         {
92             get {
93                 lock (_sync) {
94                     return _internalDictionary[new WeakKey(key)];
95                 }
96             }
97             set 
98             {
99                 WeakKey k = new WeakKey(key);
100                 lock (_sync) 
101                 {
102                     _internalDictionary[k] = value;
103                 }
104                 // This looks a bit weird but the purpose of the lost key finder is to execute
105                 // code in some future garbage collection phase so we immediately create some garbage.
106                 new LostKeyFinder(this, k);
107             }
108         }
109
110         public bool TryGetValue(K key, out V value) 
111         {
112             WeakKey k = new WeakKey(key);
113             lock (_sync) 
114             {
115                 return _internalDictionary.TryGetValue(k, out value);
116             }
117         }
118
119         [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "LostKeyFinder's purpose is to get garbage collected as soon as posible")]
120         public void Add(K key, V value) 
121         {
122             WeakKey k = new WeakKey(key);
123             lock (_sync) 
124             {
125                 _internalDictionary.Add(k, value);
126             }
127             // This looks a bit weird but the purpose of the lost key finder is to execute
128             // code in some future garbage collection phase so we immediately create some garbage.
129             new LostKeyFinder(this, k);
130
131         }
132
133         public bool ContainsKey(K key) 
134         {
135             return _internalDictionary.ContainsKey(new WeakKey(key));
136         }
137
138         public bool Remove(K key) 
139         {
140             lock (_sync) 
141             {
142                 return _internalDictionary.Remove(new WeakKey(key));
143             }
144         }
145
146         public void Add(KeyValuePair<K, V> item) 
147         {
148             Add(item.Key, item.Value);
149         }
150
151         public void Clear() 
152         {
153             lock (_sync) 
154             {
155                 _internalDictionary.Clear();
156             }
157         }
158
159         public bool Contains(KeyValuePair<K, V> item) 
160         {
161             V value;
162             bool result;
163             lock (_sync) 
164             {
165                 result = _internalDictionary.TryGetValue(new WeakKey(item.Key), out value);
166             }
167             if (result)
168             {
169                 return value.Equals(item.Value);
170             }
171             else
172             {
173                 return false;
174             }
175         }
176
177         public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) 
178         {
179             lock (_sync) 
180             {
181                 foreach (KeyValuePair<WeakKey, V> item in _internalDictionary) 
182                 {
183                     KeyValuePair<K, V> kv = new KeyValuePair<K, V>((K)item.Key.Target, item.Value);
184                     array[arrayIndex] = kv;
185                     arrayIndex++;
186                 }
187             }
188         }
189
190         public bool Remove(KeyValuePair<K, V> item) 
191         {
192             WeakKey key = new WeakKey(item.Key);
193             lock (_sync) 
194             {
195                 return _internalDictionary.Remove(key);
196             }
197         }
198
199
200
201
202
203         public IEnumerator<KeyValuePair<K, V>> GetEnumerator() 
204         {
205             List<WeakKey> lostKeys = null;
206             lock (_sync) 
207             {
208                 foreach (KeyValuePair<WeakKey, V> item in _internalDictionary) 
209                 {
210                     object k = item.Key.Target;
211                     if (k != null)
212                     {
213                         yield return new KeyValuePair<K, V>((K)k, item.Value);
214                     }
215                     else 
216                     {
217                         if (lostKeys == null)
218                         {
219                             lostKeys = new List<WeakKey>();
220                         }
221                         lostKeys.Add(item.Key);
222                     }
223                 }
224             }
225             // Recover any lost keys.
226             if (lostKeys != null)
227             {
228                 lock (_sync) 
229                 {
230                     foreach (WeakKey key in lostKeys)
231                     {
232                         _internalDictionary.Remove(key);
233                     }
234                 }
235             }
236         }
237
238
239
240
241         IEnumerator IEnumerable.GetEnumerator() 
242         {
243             return GetEnumerator();
244         }
245
246
247
248         private void ScavangeLostKeys() 
249         {
250             List<WeakKey> lostKeys = null;
251             lock (_sync) 
252             {
253                 foreach (WeakKey key in _internalDictionary.Keys)
254                 {
255                     if (!key.IsAlive) 
256                     {
257                         if (lostKeys == null)
258                         {
259                             lostKeys = new List<WeakKey>();
260                         }
261                         lostKeys.Add(key);
262                     }
263                 }
264             }
265             if (lostKeys != null)
266             {
267                 lock (_sync) 
268                 {
269                     foreach (WeakKey key in lostKeys)
270                     {
271                         _internalDictionary.Remove(key);
272                     }
273                 }
274             }
275         }
276
277         private class WeakKey : WeakReference 
278         {
279             private int _hashCode;
280             // private GCHandle _gcHandle;
281
282             public WeakKey(K key)
283                 : base(key, true) 
284             {
285                 _hashCode = key.GetHashCode();
286                 // Keep the key alive until it is explicitly collected
287                 // _gcHandle = GCHandle.Alloc(this);
288             }
289
290             internal void Release() 
291             {
292                 // _gcHandle.Free();
293             }
294
295             public override int GetHashCode() 
296             {
297                 return _hashCode;
298             }
299
300             public override bool Equals(object obj) 
301             {
302                 if (obj == null)
303                 {
304                     return false;
305                 }
306                 if (obj.GetHashCode() != _hashCode)
307                 {
308                     return false;
309                 }
310                 if (obj != this && (!IsAlive || !obj.Equals(Target)))
311                 {
312                     return false;
313                 }
314                 return true;
315             }
316         }
317
318         private class WeakComparer : IEqualityComparer<WeakKey> 
319         {
320
321             private IEqualityComparer<K> _comparer;
322             public WeakComparer() 
323             {
324             }
325
326             public WeakComparer(IEqualityComparer<K> comparer) 
327             {
328                 _comparer = comparer;
329             }
330
331             public bool Equals(WeakKey x, WeakKey y) 
332             {
333                 if (x.GetHashCode() != y.GetHashCode())
334                 {
335                     return false;
336                 }
337                 if (object.ReferenceEquals(x, y))
338                 {
339                     return true;
340                 }
341                 object ref1 = x.Target;
342                 if (ref1 == null)
343                 {
344                     return false;
345                 }
346                 object ref2 = y.Target;
347                 if (ref2 == null)
348                 {
349                     return false;
350                 }
351
352                 if (_comparer != null) 
353                 {
354                     return _comparer.Equals((K)ref1, (K)ref2);
355                 }
356                 else 
357                 {
358                     return ref1.Equals(ref2);
359                 }
360             }
361
362             public int GetHashCode(WeakKey obj) 
363             {
364                 return obj.GetHashCode();
365             }
366         }
367
368         private class LostKeyFinder 
369         {
370             WeakKeyDictionary<K, V> _dictionary;
371             WeakKey _key;
372
373             public LostKeyFinder(WeakKeyDictionary<K, V> dictionary, WeakKey key) 
374             {
375                 _dictionary = dictionary;
376                 _key = key;
377             }
378
379             ~LostKeyFinder() 
380             {
381                 if (_dictionary._finalized || _key == null) 
382                 {
383                     if (_key != null) 
384                     {
385                         _key.Release();
386                         _key = null;
387                     }
388                     return;
389                 }
390                 // if (!_key.IsAlive) {
391                 if (_key.Target == null) 
392                 {
393                     bool locked = false;
394                     try
395                     {
396                         locked = Monitor.TryEnter(_dictionary._sync);
397                         _dictionary._internalDictionary.Remove(_key);
398                     }
399                     finally
400                     {
401                         if (locked)
402                         {
403                             Monitor.Exit(_dictionary._sync);
404                         }
405                     }
406
407                     if (locked)
408                     {
409                         _key.Release();
410                         _key = null;
411                     }
412                     else
413                     {
414                         GC.ReRegisterForFinalize(this);
415                     }
416                 }
417                 else if (_dictionary._internalDictionary.ContainsKey(_key))
418                 {
419                     GC.ReRegisterForFinalize(this);
420                 }
421             }
422         }
423     }
424 }