PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_2_0/Src/IronPython/Runtime/CommonDictionaryStorage.cs

#
C# | 703 lines | 462 code | 115 blank | 126 comment | 135 complexity | c793dd41569d909df638108ecf722d0c 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 System.Runtime.Serialization;
  19. using Microsoft.Scripting.Actions;
  20. using Microsoft.Scripting;
  21. using Microsoft.Scripting.Generation;
  22. using IronPython.Runtime.Operations;
  23. using IronPython.Runtime.Types;
  24. namespace IronPython.Runtime {
  25. /// <summary>
  26. /// General purpose storage used for most PythonDictionarys.
  27. ///
  28. /// This dictionary storage is thread safe for multiple readers or writers.
  29. ///
  30. /// Mutations to the dictionary involves a simple locking strategy of
  31. /// locking on the DictionaryStorage object to ensure that only one
  32. /// mutation happens at a time.
  33. ///
  34. /// Reads against the dictionary happen lock free. When the dictionary is mutated
  35. /// it is either adding or removing buckets in a thread-safe manner so that the readers
  36. /// will either see a consistent picture as if the read occured before or after the mutation.
  37. ///
  38. /// When resizing the dictionary the buckets are replaced atomically so that the reader
  39. /// sees the new buckets or the old buckets. When reading the reader first reads
  40. /// the buckets and then calls a static helper function to do the read from the bucket
  41. /// array to ensure that readers are not seeing multiple bucket arrays.
  42. /// </summary>
  43. [Serializable]
  44. internal sealed class CommonDictionaryStorage : DictionaryStorage
  45. #if !SILVERLIGHT
  46. , ISerializable, IDeserializationCallback
  47. #endif
  48. {
  49. private Bucket[] _buckets;
  50. private int _count;
  51. private Func<object, int> _hashFunc;
  52. private Func<object, object, bool> _eqFunc;
  53. private Type _keyType;
  54. private const int InitialBucketSize = 7;
  55. private const int ResizeMultiplier = 3;
  56. // pre-created delegate instances shared by all homogeneous dictionaries for primitive types.
  57. private static readonly Func<object, int> _primitiveHash = PrimitiveHash, _doubleHash = DoubleHash, _intHash = IntHash, _tupleHash = TupleHash, _genericHash = GenericHash;
  58. private static readonly Func<object, object, bool> _intEquals = IntEquals, _doubleEquals = DoubleEquals, _stringEquals = StringEquals, _tupleEquals = TupleEquals, _genericEquals = GenericEquals, _objectEq = System.Object.ReferenceEquals;
  59. private static readonly Type _polymorphicType = typeof(CommonDictionaryStorage);
  60. /// <summary>
  61. /// Creates a new dictionary storage with no buckets
  62. /// </summary>
  63. public CommonDictionaryStorage() {
  64. }
  65. /// <summary>
  66. /// Creates a new dictionary storage with no buckets
  67. /// </summary>
  68. public CommonDictionaryStorage(int count) {
  69. _buckets = new Bucket[count + 1];
  70. }
  71. /// <summary>
  72. /// Creates a new dictionary geting values/keys from the
  73. /// items arary
  74. /// </summary>
  75. public CommonDictionaryStorage(object[] items, bool isHomogeneous)
  76. : this(Math.Max(items.Length / 2, InitialBucketSize)) {
  77. // always called w/ items, and items should be even (key/value pairs)
  78. Debug.Assert(items.Length > 0 && (items.Length & 0x01) == 0);
  79. Type t = CompilerHelpers.GetType(items[1]);
  80. if (!isHomogeneous) {
  81. for (int i = 1; i < items.Length / 2; i++) {
  82. if (CompilerHelpers.GetType(items[i * 2 + 1]) != t) {
  83. SetHeterogeneousSites();
  84. t = null;
  85. break;
  86. }
  87. }
  88. }
  89. if (t != null) {
  90. // homogeneous collection
  91. UpdateHelperFunctions(t, items[1]);
  92. }
  93. for (int i = 0; i < items.Length / 2; i++) {
  94. AddOne(items[i * 2 + 1], items[i * 2]);
  95. }
  96. }
  97. private void AddItems(object[] items) {
  98. for (int i = 0; i < items.Length / 2; i++) {
  99. AddNoLock(items[i * 2 + 1], items[i * 2]);
  100. }
  101. }
  102. /// <summary>
  103. /// Creates a new dictionary storage with the given set of buckets
  104. /// and size. Used when cloning the dictionary storage.
  105. /// </summary>
  106. private CommonDictionaryStorage(Bucket[] buckets, int count, Type keyType, Func<object, int> hashFunc, Func<object, object, bool> eqFunc) {
  107. _buckets = buckets;
  108. _count = count;
  109. _keyType = keyType;
  110. _hashFunc = hashFunc;
  111. _eqFunc = eqFunc;
  112. }
  113. #if !SILVERLIGHT
  114. private CommonDictionaryStorage(SerializationInfo info, StreamingContext context) {
  115. // remember the serialization info, we'll deserialize when we get the callback. This
  116. // enables special types like DBNull.Value to successfully be deserialized inside of us. We
  117. // store the serialization info in a special bucket so we don't have an extra field just for
  118. // serialization
  119. _buckets = new Bucket[] { new DeserializationBucket(info) };
  120. }
  121. #endif
  122. /// <summary>
  123. /// Adds a new item to the dictionary, replacing an existing one if it already exists.
  124. /// </summary>
  125. public override void Add(object key, object value) {
  126. lock (this) {
  127. AddNoLock(key, value);
  128. }
  129. }
  130. public override void AddNoLock(object key, object value) {
  131. if (_buckets == null) {
  132. Initialize();
  133. }
  134. Type t = CompilerHelpers.GetType(key);
  135. if (t != _keyType && _keyType != typeof(CommonDictionaryStorage)) {
  136. UpdateHelperFunctions(t, key);
  137. }
  138. AddOne(key, value);
  139. }
  140. private void AddOne(object key, object value) {
  141. if (Add(_buckets, key, value)) {
  142. _count++;
  143. if (_count >= _buckets.Length) {
  144. // grow the hash table
  145. EnsureSize(_buckets.Length * ResizeMultiplier);
  146. }
  147. }
  148. }
  149. private void UpdateHelperFunctions(Type t, object key) {
  150. if (_keyType == null) {
  151. // first time through, get the sites for this specific type...
  152. if (t == typeof(int)) {
  153. _hashFunc = _intHash;
  154. _eqFunc = _intEquals;
  155. } else if (t == typeof(string)) {
  156. _hashFunc = _primitiveHash;
  157. _eqFunc = _stringEquals;
  158. } else if (t == typeof(double)) {
  159. _hashFunc = _doubleHash;
  160. _eqFunc = _doubleEquals;
  161. } else if (t == typeof(PythonTuple)) {
  162. _hashFunc = _tupleHash;
  163. _eqFunc = _tupleEquals;
  164. } else if (t == typeof(Type).GetType()) { // this odd check checks for RuntimeType.
  165. _hashFunc = _primitiveHash;
  166. _eqFunc = _objectEq;
  167. } else {
  168. PythonType pt = DynamicHelpers.GetPythonType(key);
  169. // random type, but still homogeneous... get a shared site for this type.
  170. var hashSite = DefaultContext.DefaultPythonContext.GetHashSite(pt);
  171. var equalSite = DefaultContext.DefaultPythonContext.GetEqualSite(pt);
  172. AssignSiteDelegates(hashSite, equalSite);
  173. }
  174. _keyType = t;
  175. } else if (_keyType != typeof(CommonDictionaryStorage)) {
  176. // 2nd time through, we're adding a new type so we have mutliple types now,
  177. // make a new site for this storage
  178. SetHeterogeneousSites();
  179. // we need to clone the buckets so any lock-free readers will only see
  180. // the old buckets which are homogeneous
  181. _buckets = (Bucket[])_buckets.Clone();
  182. }
  183. // else we have already created a new site this dictionary
  184. }
  185. private void SetHeterogeneousSites() {
  186. var hashSite = DefaultContext.DefaultPythonContext.MakeHashSite();
  187. var equalSite = DefaultContext.DefaultPythonContext.MakeEqualSite();
  188. AssignSiteDelegates(hashSite, equalSite);
  189. _keyType = typeof(CommonDictionaryStorage);
  190. }
  191. private void AssignSiteDelegates(CallSite<Func<CallSite, object, int>> hashSite, CallSite<Func<CallSite, object, object, bool>> equalSite) {
  192. _hashFunc = (o) => hashSite.Target(hashSite, o);
  193. _eqFunc = (o1, o2) => equalSite.Target(equalSite, o1, o2);
  194. }
  195. private void EnsureSize(int newSize) {
  196. if (_buckets.Length >= newSize) {
  197. return;
  198. }
  199. Bucket[] newBuckets = new Bucket[newSize];
  200. for (int i = 0; i < _buckets.Length; i++) {
  201. Bucket curBucket = _buckets[i];
  202. while (curBucket != null) {
  203. Bucket next = curBucket.Next;
  204. AddWorker(newBuckets, curBucket.Key, curBucket.Value, curBucket.HashCode);
  205. curBucket = next;
  206. }
  207. }
  208. _buckets = newBuckets;
  209. }
  210. /// <summary>
  211. /// Initializes the buckets to their initial capacity, the caller
  212. /// must check if the buckets are empty first.
  213. /// </summary>
  214. private void Initialize() {
  215. _buckets = new Bucket[InitialBucketSize];
  216. }
  217. /// <summary>
  218. /// Static add helper that works over a single set of buckets. Used for
  219. /// both the normal add case as well as the resize case.
  220. /// </summary>
  221. private bool Add(Bucket[] buckets, object key, object value) {
  222. int hc = Hash(key);
  223. return AddWorker(buckets, key, value, hc);
  224. }
  225. private bool AddWorker(Bucket[] buckets, object key, object value, int hc) {
  226. int index = hc % buckets.Length;
  227. Bucket prev = buckets[index];
  228. Bucket cur = prev;
  229. while (cur != null) {
  230. if (cur.HashCode == hc && _eqFunc(key, cur.Key)) {
  231. cur.Value = value;
  232. return false;
  233. }
  234. prev = cur;
  235. cur = cur.Next;
  236. }
  237. if (prev != null) {
  238. Debug.Assert(prev.Next == null);
  239. prev.Next = new Bucket(hc, key, value, null);
  240. } else {
  241. buckets[index] = new Bucket(hc, key, value, null);
  242. }
  243. return true;
  244. }
  245. /// <summary>
  246. /// Removes an entry from the dictionary and returns true if the
  247. /// entry was removed or false.
  248. /// </summary>
  249. public override bool Remove(object key) {
  250. object dummy;
  251. return TryRemoveValue(key, out dummy);
  252. }
  253. /// <summary>
  254. /// Removes an entry from the dictionary and returns true if the
  255. /// entry was removed or false. The key will always be hashed
  256. /// so if it is unhashable an exception will be thrown - even
  257. /// if the dictionary has no buckets.
  258. /// </summary>
  259. internal bool RemoveAlwaysHash(object key) {
  260. lock (this) {
  261. object dummy;
  262. return TryRemoveNoLock(key, out dummy);
  263. }
  264. }
  265. public override bool TryRemoveValue(object key, out object value) {
  266. lock (this) {
  267. if (!HasAnyValues(_buckets)) {
  268. value = null;
  269. return false;
  270. }
  271. return TryRemoveNoLock(key, out value);
  272. }
  273. }
  274. private bool TryRemoveNoLock(object key, out object value) {
  275. Func<object, int> hashFunc;
  276. Func<object, object, bool> eqFunc;
  277. if (CompilerHelpers.GetType(key) == _keyType || _keyType == _polymorphicType) {
  278. hashFunc = _hashFunc;
  279. eqFunc = _eqFunc;
  280. } else {
  281. hashFunc = _genericHash;
  282. eqFunc = _genericEquals;
  283. }
  284. int hc = hashFunc(key) & Int32.MaxValue;
  285. if (_buckets == null) {
  286. value = null;
  287. return false;
  288. }
  289. int index = hc % _buckets.Length;
  290. Bucket bucket = _buckets[index];
  291. Bucket prev = bucket;
  292. while (bucket != null) {
  293. if (bucket.HashCode == hc && eqFunc(key, bucket.Key)) {
  294. value = bucket.Value;
  295. if (prev == bucket) {
  296. _buckets[index] = bucket.Next;
  297. } else {
  298. prev.Next = bucket.Next;
  299. }
  300. _count--;
  301. return true;
  302. }
  303. prev = bucket;
  304. bucket = bucket.Next;
  305. }
  306. value = null;
  307. return false;
  308. }
  309. /// <summary>
  310. /// Checks to see if the key exists in the dictionary.
  311. /// </summary>
  312. public override bool Contains(object key) {
  313. return Contains(_buckets, key);
  314. }
  315. /// <summary>
  316. /// Static helper to see if the key exists in the provided bucket array.
  317. ///
  318. /// Used so the contains check can run against a buckets while a writer
  319. /// replaces the buckets.
  320. /// </summary>
  321. private bool Contains(Bucket[] buckets, object key) {
  322. object res;
  323. return TryGetValue(buckets, key, out res);
  324. }
  325. private bool HasAnyValues(Bucket[] buckets) {
  326. return buckets != null && _hashFunc != null;
  327. }
  328. /// <summary>
  329. /// Trys to get the value associated with the given key and returns true
  330. /// if it's found or false if it's not present.
  331. /// </summary>
  332. public override bool TryGetValue(object key, out object value) {
  333. return TryGetValue(_buckets, key, out value);
  334. }
  335. /// <summary>
  336. /// Static helper to try and get the value from the dictionary.
  337. ///
  338. /// Used so the value lookup can run against a buckets while a writer
  339. /// replaces the buckets.
  340. /// </summary>
  341. private bool TryGetValue(Bucket[] buckets, object key, out object value) {
  342. if (HasAnyValues(buckets)) {
  343. int hc;
  344. Func<object, object, bool> eqFunc;
  345. if (CompilerHelpers.GetType(key) == _keyType || _keyType == typeof(CommonDictionaryStorage)) {
  346. hc = _hashFunc(key) & Int32.MaxValue;
  347. eqFunc = _eqFunc;
  348. } else {
  349. hc = _genericHash(key) & Int32.MaxValue;
  350. eqFunc = _genericEquals;
  351. }
  352. Bucket bucket = buckets[hc % buckets.Length];
  353. while (bucket != null) {
  354. if (bucket.HashCode == hc && eqFunc(key, bucket.Key)) {
  355. value = bucket.Value;
  356. return true;
  357. }
  358. bucket = bucket.Next;
  359. }
  360. }
  361. value = null;
  362. return false;
  363. }
  364. /// <summary>
  365. /// Returns the number of key/value pairs currently in the dictionary.
  366. /// </summary>
  367. public override int Count {
  368. get { return _count; }
  369. }
  370. /// <summary>
  371. /// Clears the contents of the dictionary.
  372. /// </summary>
  373. public override void Clear() {
  374. lock (this) {
  375. if (_buckets != null) {
  376. _buckets = new Bucket[8];
  377. _count = 0;
  378. }
  379. }
  380. }
  381. public override List<KeyValuePair<object, object>> GetItems() {
  382. lock (this) {
  383. List<KeyValuePair<object, object>> res = new List<KeyValuePair<object, object>>(Count);
  384. if (_buckets != null) {
  385. for (int i = 0; i < _buckets.Length; i++) {
  386. Bucket curBucket = _buckets[i];
  387. while (curBucket != null) {
  388. res.Add(new KeyValuePair<object, object>(curBucket.Key, curBucket.Value));
  389. curBucket = curBucket.Next;
  390. }
  391. }
  392. }
  393. return res;
  394. }
  395. }
  396. public override bool HasNonStringAttributes() {
  397. lock (this) {
  398. if (_keyType != typeof(string) && _buckets != null) {
  399. for (int i = 0; i < _buckets.Length; i++) {
  400. Bucket curBucket = _buckets[i];
  401. while (curBucket != null) {
  402. if (!(curBucket.Key is string)) {
  403. return true;
  404. }
  405. curBucket = curBucket.Next;
  406. }
  407. }
  408. }
  409. }
  410. return false;
  411. }
  412. /// <summary>
  413. /// Clones the storage returning a new DictionaryStorage object.
  414. /// </summary>
  415. public override DictionaryStorage Clone() {
  416. lock (this) {
  417. if (_buckets == null) {
  418. return new CommonDictionaryStorage();
  419. }
  420. Bucket[] resBuckets = new Bucket[_buckets.Length];
  421. for (int i = 0; i < _buckets.Length; i++) {
  422. if (_buckets[i] != null) {
  423. resBuckets[i] = _buckets[i].Clone();
  424. }
  425. }
  426. return new CommonDictionaryStorage(resBuckets, Count, _keyType, _hashFunc, _eqFunc);
  427. }
  428. }
  429. public override void CopyTo(DictionaryStorage/*!*/ into) {
  430. Debug.Assert(into != null);
  431. if (_buckets != null) {
  432. using (new OrderedLocker(this, into)) {
  433. CommonDictionaryStorage commonInto = into as CommonDictionaryStorage;
  434. if (commonInto != null) {
  435. CommonCopyTo(commonInto);
  436. } else {
  437. UncommonCopyTo(into);
  438. }
  439. }
  440. }
  441. }
  442. private void CommonCopyTo(CommonDictionaryStorage into) {
  443. if (into._buckets == null) {
  444. into._buckets = new Bucket[_buckets.Length];
  445. } else {
  446. int curSize = into._buckets.Length;
  447. while (curSize < _count + into._count) {
  448. curSize *= ResizeMultiplier;
  449. }
  450. into.EnsureSize(curSize);
  451. }
  452. if (into._keyType == null) {
  453. into._keyType = _keyType;
  454. into._hashFunc = _hashFunc;
  455. into._eqFunc = _eqFunc;
  456. } else if (into._keyType != _keyType) {
  457. SetHeterogeneousSites();
  458. }
  459. for (int i = 0; i < _buckets.Length; i++) {
  460. Bucket curBucket = _buckets[i];
  461. while (curBucket != null) {
  462. if (AddWorker(into._buckets, curBucket.Key, curBucket.Value, curBucket.HashCode)) {
  463. into._count++;
  464. }
  465. curBucket = curBucket.Next;
  466. }
  467. }
  468. }
  469. private void UncommonCopyTo(DictionaryStorage into) {
  470. for (int i = 0; i < _buckets.Length; i++) {
  471. Bucket curBucket = _buckets[i];
  472. while (curBucket != null) {
  473. into.AddNoLock(curBucket.Key, curBucket.Value);
  474. curBucket = curBucket.Next;
  475. }
  476. }
  477. }
  478. /// <summary>
  479. /// Helper to hash the given key w/ support for null.
  480. /// </summary>
  481. private int Hash(object key) {
  482. if (key is string) return key.GetHashCode() & Int32.MaxValue;
  483. return _hashFunc(key) & Int32.MaxValue;
  484. }
  485. /// <summary>
  486. /// Used to store a single hashed key/value and a linked list of
  487. /// collisions.
  488. ///
  489. /// Bucket is not serializable because it stores the computed hash
  490. /// code which could change between serialization and deserialization.
  491. /// </summary>
  492. private class Bucket {
  493. public object Key; // the key to be hashed
  494. public object Value; // the value associated with the key
  495. public Bucket Next; // the next chained bucket when there's a collision
  496. public int HashCode; // the hash code of the contained key.
  497. public Bucket() {
  498. }
  499. public Bucket(int hashCode, object key, object value, Bucket next) {
  500. HashCode = hashCode;
  501. Key = key;
  502. Value = value;
  503. Next = next;
  504. }
  505. public Bucket Clone() {
  506. return new Bucket(HashCode, Key, Value, CloneNext());
  507. }
  508. private Bucket CloneNext() {
  509. if (Next == null) return null;
  510. return Next.Clone();
  511. }
  512. }
  513. #region Hash/Equality Delegates
  514. private static int PrimitiveHash(object o) {
  515. return o.GetHashCode();
  516. }
  517. private static int IntHash(object o) {
  518. return (int)o;
  519. }
  520. private static int DoubleHash(object o) {
  521. return DoubleOps.__hash__((double)o);
  522. }
  523. private static int GenericHash(object o) {
  524. return PythonOps.Hash(DefaultContext.Default, o);
  525. }
  526. private static int TupleHash(object o) {
  527. return ((IValueEquality)o).GetValueHashCode();
  528. }
  529. private static bool StringEquals(object o1, object o2) {
  530. return (string)o1 == (string)o2;
  531. }
  532. private static bool IntEquals(object o1, object o2) {
  533. return (int)o1 == (int)o2;
  534. }
  535. private static bool DoubleEquals(object o1, object o2) {
  536. return (double)o1 == (double)o2;
  537. }
  538. private static bool TupleEquals(object o1, object o2) {
  539. return ((IValueEquality)o1).ValueEquals(o2);
  540. }
  541. private static bool GenericEquals(object o1, object o2) {
  542. return PythonOps.EqualRetBool(o1, o2);
  543. }
  544. #endregion
  545. #if !SILVERLIGHT
  546. /// <summary>
  547. /// Special marker bucket used during deserialization to not add
  548. /// an extra field to the dictionary storage type.
  549. /// </summary>
  550. private class DeserializationBucket : Bucket {
  551. public readonly SerializationInfo/*!*/ SerializationInfo;
  552. public DeserializationBucket(SerializationInfo info) {
  553. SerializationInfo = info;
  554. }
  555. }
  556. private DeserializationBucket GetDeserializationBucket() {
  557. if (_buckets == null) {
  558. return null;
  559. }
  560. if (_buckets.Length != 1) {
  561. return null;
  562. }
  563. return _buckets[0] as DeserializationBucket;
  564. }
  565. #region ISerializable Members
  566. public void GetObjectData(SerializationInfo info, StreamingContext context) {
  567. info.AddValue("buckets", GetItems());
  568. }
  569. #endregion
  570. #region IDeserializationCallback Members
  571. void IDeserializationCallback.OnDeserialization(object sender) {
  572. DeserializationBucket bucket = GetDeserializationBucket();
  573. if (bucket == null) {
  574. // we've received multiple OnDeserialization callbacks, only
  575. // deserialize after the 1st one
  576. return;
  577. }
  578. SerializationInfo info = bucket.SerializationInfo;
  579. _buckets = null;
  580. var buckets = (List<KeyValuePair<object, object>>)info.GetValue("buckets", typeof(List<KeyValuePair<object, object>>));
  581. foreach (KeyValuePair<object, object> kvp in buckets) {
  582. Add(kvp.Key, kvp.Value);
  583. }
  584. }
  585. #endregion
  586. #endif
  587. }
  588. }