|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Presentation.Model
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
internal class WeakKeyDictionary<K, V> : IDictionary<K, V>
{
private Dictionary<WeakKey, V> _internalDictionary;
private object _sync = new object();
private bool _finalized;
internal WeakKeyDictionary()
{
_internalDictionary = new Dictionary<WeakKey, V>(new WeakComparer());
}
public WeakKeyDictionary(IEqualityComparer<K> comparer)
{
_internalDictionary = new Dictionary<WeakKey, V>(new WeakComparer(comparer));
}
// FXCop: this is not empty; we need to mark this so we know if a key
// still has an active dictionary at its finalization.
[SuppressMessage("Microsoft.Performance", "CA1821:RemoveEmptyFinalizers")]
~WeakKeyDictionary()
{
_finalized = true;
}
public ICollection<K> Keys
{
get
{
List<K> list = new List<K>();
lock (_sync)
{
foreach (WeakKey key in _internalDictionary.Keys)
{
object k = key.Target;
if (k != null)
{
list.Add((K)k);
}
}
}
return list;
}
}
public ICollection<V> Values
{
get {
lock (_sync) {
// make a copy of the values, so the during GC, the returned collection does not change.
return new List<V>(_internalDictionary.Values);
}
}
}
public int Count
{
get
{
// Ensure a fairly accurate count.
ScavangeLostKeys();
lock (_sync)
{
return _internalDictionary.Count;
}
}
}
public bool IsReadOnly
{
get {
return false;
}
}
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "LostKeyFinder's purpose is to get garbage collected as soon as posible")]
public V this[K key]
{
get {
lock (_sync) {
return _internalDictionary[new WeakKey(key)];
}
}
set
{
WeakKey k = new WeakKey(key);
lock (_sync)
{
_internalDictionary[k] = value;
}
// This looks a bit weird but the purpose of the lost key finder is to execute
// code in some future garbage collection phase so we immediately create some garbage.
new LostKeyFinder(this, k);
}
}
public bool TryGetValue(K key, out V value)
{
WeakKey k = new WeakKey(key);
lock (_sync)
{
return _internalDictionary.TryGetValue(k, out value);
}
}
[SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "LostKeyFinder's purpose is to get garbage collected as soon as posible")]
public void Add(K key, V value)
{
WeakKey k = new WeakKey(key);
lock (_sync)
{
_internalDictionary.Add(k, value);
}
// This looks a bit weird but the purpose of the lost key finder is to execute
// code in some future garbage collection phase so we immediately create some garbage.
new LostKeyFinder(this, k);
}
public bool ContainsKey(K key)
{
return _internalDictionary.ContainsKey(new WeakKey(key));
}
public bool Remove(K key)
{
lock (_sync)
{
return _internalDictionary.Remove(new WeakKey(key));
}
}
public void Add(KeyValuePair<K, V> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
lock (_sync)
{
_internalDictionary.Clear();
}
}
public bool Contains(KeyValuePair<K, V> item)
{
V value;
bool result;
lock (_sync)
{
result = _internalDictionary.TryGetValue(new WeakKey(item.Key), out value);
}
if (result)
{
return value.Equals(item.Value);
}
else
{
return false;
}
}
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
{
lock (_sync)
{
foreach (KeyValuePair<WeakKey, V> item in _internalDictionary)
{
KeyValuePair<K, V> kv = new KeyValuePair<K, V>((K)item.Key.Target, item.Value);
array[arrayIndex] = kv;
arrayIndex++;
}
}
}
public bool Remove(KeyValuePair<K, V> item)
{
WeakKey key = new WeakKey(item.Key);
lock (_sync)
{
return _internalDictionary.Remove(key);
}
}
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
List<WeakKey> lostKeys = null;
lock (_sync)
{
foreach (KeyValuePair<WeakKey, V> item in _internalDictionary)
{
object k = item.Key.Target;
if (k != null)
{
yield return new KeyValuePair<K, V>((K)k, item.Value);
}
else
{
if (lostKeys == null)
{
lostKeys = new List<WeakKey>();
}
lostKeys.Add(item.Key);
}
}
}
// Recover any lost keys.
if (lostKeys != null)
{
lock (_sync)
{
foreach (WeakKey key in lostKeys)
{
_internalDictionary.Remove(key);
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void ScavangeLostKeys()
{
List<WeakKey> lostKeys = null;
lock (_sync)
{
foreach (WeakKey key in _internalDictionary.Keys)
{
if (!key.IsAlive)
{
if (lostKeys == null)
{
lostKeys = new List<WeakKey>();
}
lostKeys.Add(key);
}
}
}
if (lostKeys != null)
{
lock (_sync)
{
foreach (WeakKey key in lostKeys)
{
_internalDictionary.Remove(key);
}
}
}
}
private class WeakKey : WeakReference
{
private int _hashCode;
// private GCHandle _gcHandle;
public WeakKey(K key)
: base(key, true)
{
_hashCode = key.GetHashCode();
// Keep the key alive until it is explicitly collected
// _gcHandle = GCHandle.Alloc(this);
}
internal void Release()
{
// _gcHandle.Free();
}
public override int GetHashCode()
{
return _hashCode;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetHashCode() != _hashCode)
{
return false;
}
if (obj != this && (!IsAlive || !obj.Equals(Target)))
{
return false;
}
return true;
}
}
private class WeakComparer : IEqualityComparer<WeakKey>
{
private IEqualityComparer<K> _comparer;
public WeakComparer()
{
}
public WeakComparer(IEqualityComparer<K> comparer)
{
_comparer = comparer;
}
public bool Equals(WeakKey x, WeakKey y)
{
if (x.GetHashCode() != y.GetHashCode())
{
return false;
}
if (object.ReferenceEquals(x, y))
{
return true;
}
object ref1 = x.Target;
if (ref1 == null)
{
return false;
}
object ref2 = y.Target;
if (ref2 == null)
{
return false;
}
if (_comparer != null)
{
return _comparer.Equals((K)ref1, (K)ref2);
}
else
{
return ref1.Equals(ref2);
}
}
public int GetHashCode(WeakKey obj)
{
return obj.GetHashCode();
}
}
private class LostKeyFinder
{
WeakKeyDictionary<K, V> _dictionary;
WeakKey _key;
public LostKeyFinder(WeakKeyDictionary<K, V> dictionary, WeakKey key)
{
_dictionary = dictionary;
_key = key;
}
~LostKeyFinder()
{
if (_dictionary._finalized || _key == null)
{
if (_key != null)
{
_key.Release();
_key = null;
}
return;
}
// if (!_key.IsAlive) {
if (_key.Target == null)
{
bool locked = false;
try
{
locked = Monitor.TryEnter(_dictionary._sync);
_dictionary._internalDictionary.Remove(_key);
}
finally
{
if (locked)
{
Monitor.Exit(_dictionary._sync);
}
}
if (locked)
{
_key.Release();
_key = null;
}
else
{
GC.ReRegisterForFinalize(this);
}
}
else if (_dictionary._internalDictionary.ContainsKey(_key))
{
GC.ReRegisterForFinalize(this);
}
}
}
}
}
|