PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/corlib/Test/System.Runtime.CompilerServices/ConditionalWeakTableTest.cs

https://bitbucket.org/danipen/mono
C# | 492 lines | 345 code | 108 blank | 39 comment | 31 complexity | 33938dccc0f34e6812b9bc5996cdd0d3 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // ConditionalWeakTableTest.cs
  3. //
  4. // Author:
  5. // Rodrigo Kumpera <rkumpera@novell.com>
  6. //
  7. // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. #if NET_4_0
  29. using NUnit.Framework;
  30. using System;
  31. using System.Reflection;
  32. using System.Runtime.CompilerServices;
  33. using System.Runtime.InteropServices;
  34. using System.Runtime.Serialization;
  35. using System.Security.Permissions;
  36. using System.Collections.Generic;
  37. using System.Threading;
  38. namespace MonoTests.System.Runtime.CompilerServices {
  39. [TestFixture]
  40. public class ConditionalWeakTableTest {
  41. public class Link {
  42. public object obj;
  43. public Link(object obj) {
  44. this.obj = obj;
  45. }
  46. }
  47. class Key {
  48. public int Foo;
  49. public override string ToString () {
  50. return "key-" + Foo;
  51. }
  52. }
  53. class Val {
  54. public int Foo;
  55. public override string ToString () {
  56. return "value-" + Foo;
  57. }
  58. }
  59. [Test]
  60. public void GetValue () {
  61. var cwt = new ConditionalWeakTable <object,object> ();
  62. try {
  63. cwt.GetValue (null, k => null);
  64. Assert.Fail ("#0");
  65. } catch (ArgumentNullException) {}
  66. try {
  67. cwt.GetValue (20, null);
  68. Assert.Fail ("#1");
  69. } catch (ArgumentNullException) {}
  70. object key = "foo";
  71. object val = cwt.GetValue (key, k => new Link (k));
  72. Assert.IsTrue (val != null, "#2");
  73. Assert.AreEqual (typeof (Link), val.GetType () , "#3");
  74. Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
  75. }
  76. [Test]
  77. public void GetOrCreateValue () {
  78. var cwt = new ConditionalWeakTable <object,object> ();
  79. try {
  80. cwt.GetOrCreateValue (null);
  81. Assert.Fail ("#0");
  82. } catch (ArgumentNullException) {}
  83. object key = "foo";
  84. object val = cwt.GetOrCreateValue (key);
  85. Assert.IsTrue (val != null, "#2");
  86. Assert.AreEqual (typeof (object), val.GetType () , "#3");
  87. Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
  88. var cwt2 = new ConditionalWeakTable <object, string> ();
  89. try {
  90. cwt2.GetOrCreateValue (key);
  91. Assert.Fail ("#5");
  92. } catch (MissingMethodException) {}
  93. }
  94. [Test]
  95. public void Remove () {
  96. var cwt = new ConditionalWeakTable <object,object> ();
  97. object c = new Key ();
  98. cwt.Add (c, "x");
  99. try {
  100. cwt.Remove (null);
  101. Assert.Fail ("#0");
  102. } catch (ArgumentNullException) {}
  103. Assert.IsFalse (cwt.Remove ("x"), "#1");
  104. Assert.IsTrue (cwt.Remove (c), "#2");
  105. Assert.IsFalse (cwt.Remove (c), "#3");
  106. }
  107. [Test]
  108. public void Add () {
  109. var cwt = new ConditionalWeakTable <object,object> ();
  110. object c = new Key ();
  111. cwt.Add (c, new Link (c));
  112. try {
  113. cwt.Add (c, new Link (c));
  114. Assert.Fail ("#0");
  115. } catch (ArgumentException) {}
  116. cwt.Add ("zzz", null);//ok
  117. try {
  118. cwt.Add (null, new Link (c));
  119. Assert.Fail ("#1");
  120. } catch (ArgumentNullException) {}
  121. }
  122. [Test]
  123. public void TryGetValue () {
  124. var cwt = new ConditionalWeakTable <object,object> ();
  125. object res;
  126. object c = new Key ();
  127. cwt.Add (c, "foo");
  128. try {
  129. cwt.TryGetValue (null, out res);
  130. Assert.Fail ("#0");
  131. } catch (ArgumentNullException) {}
  132. Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
  133. Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
  134. Assert.AreEqual ("foo", res, "#3");
  135. }
  136. static void FillStuff (ConditionalWeakTable <object,object> cwt,
  137. out List<object> keepAlive, out List<WeakReference> keys) {
  138. keepAlive = new List<object> ();
  139. keys = new List<WeakReference> ();
  140. object a = new Key ();
  141. object b = new Key ();
  142. object c = new Key ();
  143. cwt.Add (a, new string ("str0".ToCharArray ()));
  144. cwt.Add (b, "str1");
  145. cwt.Add (c, new Link (a));
  146. keepAlive.Add (c);
  147. keys.Add (new WeakReference(a));
  148. keys.Add (new WeakReference(b));
  149. keys.Add (new WeakReference(c));
  150. }
  151. [Test]
  152. public void Reachability () {
  153. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  154. return;
  155. var cwt = new ConditionalWeakTable <object,object> ();
  156. List<object> keepAlive;
  157. List<WeakReference> keys;
  158. FillStuff (cwt, out keepAlive, out keys);
  159. GC.Collect ();
  160. Assert.IsTrue (keys [0].IsAlive, "r0");
  161. Assert.IsFalse (keys [1].IsAlive, "r1");
  162. Assert.IsTrue (keys [2].IsAlive, "r2");
  163. object res;
  164. Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
  165. Assert.IsTrue (res is Link, "ka1");
  166. Link link = res as Link;
  167. Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
  168. Assert.AreEqual ("str0", res, "ka3");
  169. }
  170. static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
  171. {
  172. const int K = 500;
  173. object[] keys = new object [K];
  174. for (int i = 0; i < K; ++i)
  175. keys[i] = new object ();
  176. Random rand = new Random ();
  177. /*produce a complex enough network of links*/
  178. for (int i = 0; i < K; ++i)
  179. cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
  180. var res = new List<WeakReference> ();
  181. for (int i = 0; i < 10; ++i)
  182. res.Add (new WeakReference (keys [rand.Next (K)]));
  183. Array.Clear (keys, 0, keys.Length);
  184. return res;
  185. }
  186. [Test]
  187. public void InsertStress () {
  188. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  189. return;
  190. var cwt = new ConditionalWeakTable <object,object> ();
  191. var a = new object ();
  192. var b = new object ();
  193. cwt.Add (a, new object ());
  194. cwt.Add (b, new object ());
  195. List<WeakReference> res = null;
  196. ThreadStart dele = () => { res = FillWithNetwork (cwt); };
  197. var th = new Thread(dele);
  198. th.Start ();
  199. th.Join ();
  200. GC.Collect ();
  201. GC.Collect ();
  202. for (int i = 0; i < res.Count; ++i)
  203. Assert.IsFalse (res [i].IsAlive, "#r" + i);
  204. }
  205. static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
  206. if (cwt [0] == null)
  207. cwt[0] = new ConditionalWeakTable <object,object> ();
  208. var res = FillWithNetwork (cwt[0]);
  209. return res;
  210. }
  211. static void ForcePromotion () {
  212. var o = new object[64000];
  213. for (int i = 0; i < 64000; ++i)
  214. o[i] = new int[10];
  215. }
  216. static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
  217. {
  218. var res = new List<object> ();
  219. for (int i = 0; i < 10; ++i) {
  220. res.Add (new object ());
  221. cwt[0].Add (res [i], i);
  222. }
  223. return res;
  224. }
  225. [Test]
  226. public void OldGenStress () {
  227. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  228. return;
  229. var cwt = new ConditionalWeakTable <object,object>[1];
  230. List<object> k = null;
  231. List<WeakReference> res, res2;
  232. res = res2 = null;
  233. ThreadStart dele = () => {
  234. res = FillWithNetwork2 (cwt);
  235. ForcePromotion ();
  236. k = FillReachable (cwt);
  237. res2 = FillWithNetwork2 (cwt);
  238. };
  239. var th = new Thread(dele);
  240. th.Start ();
  241. th.Join ();
  242. GC.Collect ();
  243. for (int i = 0; i < res.Count; ++i)
  244. Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
  245. for (int i = 0; i < res2.Count; ++i)
  246. Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
  247. for (int i = 0; i < k.Count; ++i) {
  248. object val;
  249. Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
  250. Assert.AreEqual (i, val, "k1-" + i);
  251. }
  252. }
  253. static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
  254. var handles = new List<GCHandle> ();
  255. var a = (object)10;
  256. var b = (object)20;
  257. var k1 = (object)30;
  258. var k2 = (object)40;
  259. handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
  260. handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
  261. handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
  262. handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
  263. cwt.Add (a, k1);
  264. cwt.Add (b, k2);
  265. return handles;
  266. }
  267. static void MakeObjMovable (List<GCHandle> handles)
  268. {
  269. for (int i = 0; i < handles.Count; ++i) {
  270. object o = handles[i].Target;
  271. handles[i].Free ();
  272. handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
  273. }
  274. }
  275. static void ForceMinor () {
  276. for (int i = 0; i < 64000; ++i)
  277. new object ();
  278. }
  279. public void PromotedCwtPointingToYoungStuff () {
  280. var cwt = new ConditionalWeakTable <object,object> ();
  281. var handles = FillTable3 (cwt);
  282. GC.Collect (0);
  283. /*Be 100% sure it will be on the young gen*/
  284. /*cwt array now will be on old gen*/
  285. ForceMinor ();
  286. ForceMinor ();
  287. ForceMinor ();
  288. //Make them non pinned
  289. MakeObjMovable (handles);
  290. GC.Collect (0);
  291. //Force a minor GC - this should cause
  292. ForceMinor ();
  293. ForceMinor ();
  294. ForceMinor ();
  295. ForceMinor ();
  296. GC.Collect (0);
  297. object r1, r2;
  298. Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
  299. Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
  300. GC.Collect ();
  301. cwt.GetHashCode ();
  302. }
  303. static object _lock1 = new object ();
  304. static object _lock2 = new object ();
  305. static int reachable = 0;
  306. public class FinalizableLink {
  307. object obj;
  308. ConditionalWeakTable <object,object> cwt;
  309. int id;
  310. public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
  311. this.id = id;
  312. this.obj = obj;
  313. this.cwt = cwt;
  314. }
  315. ~FinalizableLink () {
  316. lock (_lock1) { ; }
  317. object obj;
  318. bool res = cwt.TryGetValue (this, out obj);
  319. if (res)
  320. ++reachable;
  321. if (reachable == 20)
  322. lock (_lock2) { Monitor.Pulse (_lock2); }
  323. }
  324. }
  325. static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
  326. {
  327. object a = new object ();
  328. object b = new FinalizableLink (0, a, cwt);
  329. cwt.Add (a, "foo");
  330. cwt.Add (b, "bar");
  331. for (int i = 1; i < 20; ++i) {
  332. b = new FinalizableLink (i, b, cwt);
  333. cwt.Add (b, i);
  334. }
  335. }
  336. [Test]
  337. public void FinalizableObjectsThatRetainDeadKeys ()
  338. {
  339. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  340. return;
  341. lock (_lock1) {
  342. var cwt = new ConditionalWeakTable <object,object> ();
  343. ThreadStart dele = () => { FillWithFinalizable (cwt); };
  344. var th = new Thread(dele);
  345. th.Start ();
  346. th.Join ();
  347. GC.Collect ();
  348. GC.Collect ();
  349. Assert.AreEqual (0, reachable, "#1");
  350. }
  351. GC.Collect ();
  352. GC.Collect ();
  353. lock (_lock2) { Monitor.Wait (_lock2, 1000); }
  354. Assert.AreEqual (20, reachable, "#1");
  355. }
  356. [Test]
  357. public void OldGenKeysMakeNewGenObjectsReachable ()
  358. {
  359. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  360. return;
  361. ConditionalWeakTable<object, Val> table = new ConditionalWeakTable<object, Val>();
  362. List<Key> keys = new List<Key>();
  363. //
  364. // This list references all keys for the duration of the program, so none
  365. // should be collected ever.
  366. //
  367. for (int x = 0; x < 1000; x++)
  368. keys.Add (new Key () { Foo = x });
  369. for (int i = 0; i < 1000; ++i) {
  370. // Insert all keys into the ConditionalWeakTable
  371. foreach (var key in keys)
  372. table.Add (key, new Val () { Foo = key.Foo });
  373. // Look up all keys to verify that they are still there
  374. Val val;
  375. foreach (var key in keys)
  376. Assert.IsTrue (table.TryGetValue (key, out val), "#1-" + i + "-k-" + key);
  377. // Remove all keys from the ConditionalWeakTable
  378. foreach (var key in keys)
  379. Assert.IsTrue (table.Remove (key), "#2-" + i + "-k-" + key);
  380. }
  381. }
  382. }
  383. }
  384. #endif