PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_2_0/Src/Microsoft.Scripting/Utils/WeakDictionary.cs

#
C# | 397 lines | 272 code | 66 blank | 59 comment | 41 complexity | fdf51a50a590f4fabbfc585a5c197e0c MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  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. * ***************************************************************************/
  15. using System; using Microsoft;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using Microsoft.Contracts;
  19. namespace Microsoft.Scripting.Utils {
  20. /// <summary>
  21. /// Similar to Dictionary[TKey,TValue], but it also ensures that the keys will not be kept alive
  22. /// if the only reference is from this collection. The value will be kept alive as long as the key
  23. /// is alive.
  24. ///
  25. /// This currently has a limitation that the caller is responsible for ensuring that an object used as
  26. /// a key is not also used as a value in *any* instance of a WeakHash. Otherwise, it will result in the
  27. /// object being kept alive forever. This effectively means that the owner of the WeakHash should be the
  28. /// only one who has access to the object used as a value.
  29. ///
  30. /// Currently, there is also no guarantee of how long the values will be kept alive even after the keys
  31. /// get collected. This could be fixed by triggerring CheckCleanup() to be called on every garbage-collection
  32. /// by having a dummy watch-dog object with a finalizer which calls CheckCleanup().
  33. /// </summary>
  34. public class WeakDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
  35. // The one and only comparer instance.
  36. static readonly IEqualityComparer<object> comparer = new WeakComparer<object>();
  37. IDictionary<object, TValue> dict = new Dictionary<object, TValue>(comparer);
  38. int version, cleanupVersion;
  39. #if SILVERLIGHT // GC
  40. WeakReference cleanupGC = new WeakReference(new object());
  41. #else
  42. int cleanupGC = 0;
  43. #endif
  44. public WeakDictionary() {
  45. }
  46. #region IDictionary<TKey,TValue> Members
  47. public void Add(TKey key, TValue value) {
  48. CheckCleanup();
  49. // If the WeakHash already holds this value as a key, it will lead to a circular-reference and result
  50. // in the objects being kept alive forever. The caller needs to ensure that this cannot happen.
  51. Debug.Assert(!dict.ContainsKey(value));
  52. dict.Add(new WeakObject<TKey>(key), value);
  53. }
  54. [Confined]
  55. public bool ContainsKey(TKey key) {
  56. // We dont have to worry about creating "new WeakObject<TKey>(key)" since the comparer
  57. // can compare raw objects with WeakObject<T>.
  58. return dict.ContainsKey(key);
  59. }
  60. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix
  61. public ICollection<TKey> Keys {
  62. get {
  63. // TODO:
  64. throw new NotImplementedException();
  65. }
  66. }
  67. public bool Remove(TKey key) {
  68. return dict.Remove(key);
  69. }
  70. public bool TryGetValue(TKey key, out TValue value) {
  71. return dict.TryGetValue(key, out value);
  72. }
  73. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix
  74. public ICollection<TValue> Values {
  75. get {
  76. // TODO:
  77. throw new NotImplementedException();
  78. }
  79. }
  80. public TValue this[TKey key] {
  81. get {
  82. return dict[key];
  83. }
  84. set {
  85. // If the WeakHash already holds this value as a key, it will lead to a circular-reference and result
  86. // in the objects being kept alive forever. The caller needs to ensure that this cannot happen.
  87. Debug.Assert(!dict.ContainsKey(value));
  88. dict[new WeakObject<TKey>(key)] = value;
  89. }
  90. }
  91. /// <summary>
  92. /// Check if any of the keys have gotten collected
  93. ///
  94. /// Currently, there is also no guarantee of how long the values will be kept alive even after the keys
  95. /// get collected. This could be fixed by triggerring CheckCleanup() to be called on every garbage-collection
  96. /// by having a dummy watch-dog object with a finalizer which calls CheckCleanup().
  97. /// </summary>
  98. void CheckCleanup() {
  99. version++;
  100. long change = version - cleanupVersion;
  101. // Cleanup the table if it is a while since we have done it last time.
  102. // Take the size of the table into account.
  103. if (change > 1234 + dict.Count / 2) {
  104. // It makes sense to do the cleanup only if a GC has happened in the meantime.
  105. // WeakReferences can become zero only during the GC.
  106. bool garbage_collected;
  107. #if SILVERLIGHT // GC.CollectionCount
  108. garbage_collected = !cleanupGC.IsAlive;
  109. if (garbage_collected) cleanupGC = new WeakReference(new object());
  110. #else
  111. int currentGC = GC.CollectionCount(0);
  112. garbage_collected = currentGC != cleanupGC;
  113. if (garbage_collected) cleanupGC = currentGC;
  114. #endif
  115. if (garbage_collected) {
  116. Cleanup();
  117. cleanupVersion = version;
  118. } else {
  119. cleanupVersion += 1234;
  120. }
  121. }
  122. }
  123. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
  124. private void Cleanup() {
  125. int liveCount = 0;
  126. int emptyCount = 0;
  127. foreach (WeakObject<TKey> w in dict.Keys) {
  128. if (w.Target != null)
  129. liveCount++;
  130. else
  131. emptyCount++;
  132. }
  133. // Rehash the table if there is a significant number of empty slots
  134. if (emptyCount > liveCount / 4) {
  135. Dictionary<object, TValue> newtable = new Dictionary<object, TValue>(liveCount + liveCount / 4, comparer);
  136. foreach (WeakObject<TKey> w in dict.Keys) {
  137. object target = w.Target;
  138. if (target != null)
  139. newtable[w] = dict[w];
  140. GC.KeepAlive(target);
  141. }
  142. dict = newtable;
  143. }
  144. }
  145. #endregion
  146. #region ICollection<KeyValuePair<TKey,TValue>> Members
  147. public void Add(KeyValuePair<TKey, TValue> item) {
  148. // TODO:
  149. throw new NotImplementedException();
  150. }
  151. public void Clear() {
  152. // TODO:
  153. throw new NotImplementedException();
  154. }
  155. [Confined]
  156. public bool Contains(KeyValuePair<TKey, TValue> item) {
  157. // TODO:
  158. throw new NotImplementedException();
  159. }
  160. public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
  161. // TODO:
  162. throw new NotImplementedException();
  163. }
  164. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix
  165. public int Count {
  166. get {
  167. // TODO:
  168. throw new NotImplementedException();
  169. }
  170. }
  171. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] // TODO: fix
  172. public bool IsReadOnly {
  173. get {
  174. // TODO:
  175. throw new NotImplementedException();
  176. }
  177. }
  178. public bool Remove(KeyValuePair<TKey, TValue> item) {
  179. // TODO:
  180. throw new NotImplementedException();
  181. }
  182. #endregion
  183. #region IEnumerable<KeyValuePair<TKey,TValue>> Members
  184. [Pure]
  185. public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
  186. // TODO:
  187. throw new NotImplementedException();
  188. }
  189. #endregion
  190. #region IEnumerable Members
  191. [Pure]
  192. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
  193. // TODO:
  194. throw new NotImplementedException();
  195. }
  196. #endregion
  197. }
  198. internal class WeakObject<T> {
  199. WeakReference weakReference;
  200. int hashCode;
  201. public WeakObject(T obj) {
  202. weakReference = new WeakReference(obj, true);
  203. hashCode = (obj == null) ? 0 : obj.GetHashCode();
  204. }
  205. public T Target {
  206. get {
  207. return (T)weakReference.Target;
  208. }
  209. }
  210. [Confined]
  211. public override int GetHashCode() {
  212. return hashCode;
  213. }
  214. [Confined]
  215. public override bool Equals(object obj) {
  216. object target = weakReference.Target;
  217. if (target == null) {
  218. return false;
  219. }
  220. return ((T)target).Equals(obj);
  221. }
  222. }
  223. // WeakComparer treats WeakObject as transparent envelope
  224. sealed class WeakComparer<T> : IEqualityComparer<T> {
  225. bool IEqualityComparer<T>.Equals(T x, T y) {
  226. WeakObject<T> wx = x as WeakObject<T>;
  227. if (wx != null)
  228. x = wx.Target;
  229. WeakObject<T> wy = y as WeakObject<T>;
  230. if (wy != null)
  231. y = wy.Target;
  232. return Object.Equals(x, y);
  233. }
  234. int IEqualityComparer<T>.GetHashCode(T obj) {
  235. WeakObject<T> wobj = obj as WeakObject<T>;
  236. if (wobj != null)
  237. return wobj.GetHashCode();
  238. return (obj == null) ? 0 : obj.GetHashCode();
  239. }
  240. }
  241. public sealed class HybridMapping<T> {
  242. private Dictionary<int, object> _dict = new Dictionary<int, object>();
  243. private readonly Object _synchObject = new Object();
  244. private readonly int _offset;
  245. private int _current;
  246. private const int SIZE = 4096;
  247. private const int MIN_RANGE = SIZE / 2;
  248. public HybridMapping() : this(0) {
  249. }
  250. public HybridMapping(int offset) {
  251. if (offset < 0 || (SIZE - offset) < MIN_RANGE) {
  252. throw new InvalidOperationException("HybridMapping is full");
  253. }
  254. _offset = offset;
  255. _current = offset;
  256. }
  257. private void NextKey() {
  258. if (++_current >= SIZE) {
  259. _current = _offset;
  260. }
  261. }
  262. public int WeakAdd(T value) {
  263. lock (_synchObject) {
  264. int saved = _current;
  265. while (_dict.ContainsKey(_current)) {
  266. NextKey();
  267. if (_current == saved)
  268. throw new InvalidOperationException("HybridMapping is full");
  269. }
  270. _dict.Add(_current, new WeakObject<T>(value));
  271. return _current;
  272. }
  273. }
  274. public int StrongAdd(T value) {
  275. lock (_synchObject) {
  276. int saved = _current;
  277. while (_dict.ContainsKey(_current)) {
  278. NextKey();
  279. if (_current == saved)
  280. throw new InvalidOperationException("HybridMapping is full");
  281. }
  282. _dict.Add(_current, value);
  283. return _current;
  284. }
  285. }
  286. public T GetObjectFromId(int id) {
  287. object ret;
  288. if (_dict.TryGetValue(id, out ret)) {
  289. WeakObject<T> weakObj = ret as WeakObject<T>;
  290. if (weakObj != null) {
  291. return weakObj.Target;
  292. }
  293. if (ret is T) {
  294. return (T)ret;
  295. }
  296. throw new InvalidOperationException("Unexpected dictionary content: type " + ret.GetType());
  297. } else
  298. return default(T);
  299. }
  300. public int GetIdFromObject(T value) {
  301. lock (_synchObject) {
  302. foreach (KeyValuePair<int, object> kv in _dict) {
  303. if (kv.Value is WeakObject<T>) {
  304. object target = ((WeakObject<T>)kv.Value).Target;
  305. if (target != null && target.Equals(value))
  306. return kv.Key;
  307. } else if (kv.Value is T) {
  308. object target = (T)(kv.Value);
  309. if (target.Equals(value))
  310. return kv.Key;
  311. }
  312. }
  313. }
  314. return -1;
  315. }
  316. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // TODO: fix (rename?)
  317. public void RemoveOnId(int id) {
  318. lock (_synchObject) {
  319. _dict.Remove(id);
  320. }
  321. }
  322. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] // TODO: fix
  323. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] // TODO: fix (rename?)
  324. public void RemoveOnObject(T value) {
  325. try {
  326. int id = GetIdFromObject(value);
  327. RemoveOnId(id);
  328. } catch { }
  329. }
  330. }
  331. }