PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/pruiz/mono
C# | 486 lines | 339 code | 107 blank | 40 comment | 31 complexity | 22f5f655ef55f6d1715fb623b0491ecf MD5 | raw file
Possible License(s): 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. using NUnit.Framework;
  29. using System;
  30. using System.Reflection;
  31. using System.Runtime.CompilerServices;
  32. using System.Runtime.InteropServices;
  33. using System.Runtime.Serialization;
  34. using System.Security.Permissions;
  35. using System.Collections.Generic;
  36. using System.Threading;
  37. using MonoTests.Helpers;
  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. Assert.Ignore ("Not working on Boehm.");
  155. var cwt = new ConditionalWeakTable <object,object> ();
  156. List<object> keepAlive = null;
  157. List<WeakReference> keys = null;
  158. FinalizerHelpers.PerformNoPinAction (delegate () {
  159. FillStuff (cwt, out keepAlive, out keys);
  160. });
  161. GC.Collect ();
  162. Assert.IsTrue (keys [0].IsAlive, "r0");
  163. Assert.IsFalse (keys [1].IsAlive, "r1");
  164. Assert.IsTrue (keys [2].IsAlive, "r2");
  165. object res;
  166. Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
  167. Assert.IsTrue (res is Link, "ka1");
  168. Link link = res as Link;
  169. Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
  170. Assert.AreEqual ("str0", res, "ka3");
  171. }
  172. static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
  173. {
  174. const int K = 500;
  175. object[] keys = new object [K];
  176. for (int i = 0; i < K; ++i)
  177. keys[i] = new object ();
  178. Random rand = new Random ();
  179. /*produce a complex enough network of links*/
  180. for (int i = 0; i < K; ++i)
  181. cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
  182. var res = new List<WeakReference> ();
  183. for (int i = 0; i < 10; ++i)
  184. res.Add (new WeakReference (keys [rand.Next (K)]));
  185. Array.Clear (keys, 0, keys.Length);
  186. return res;
  187. }
  188. [Test]
  189. public void InsertStress () {
  190. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  191. Assert.Ignore ("Not working on Boehm.");
  192. var cwt = new ConditionalWeakTable <object,object> ();
  193. var a = new object ();
  194. var b = new object ();
  195. cwt.Add (a, new object ());
  196. cwt.Add (b, new object ());
  197. List<WeakReference> res = null;
  198. FinalizerHelpers.PerformNoPinAction (() => { res = FillWithNetwork (cwt); });
  199. GC.Collect ();
  200. GC.Collect ();
  201. for (int i = 0; i < res.Count; ++i)
  202. Assert.IsFalse (res [i].IsAlive, "#r" + i);
  203. }
  204. static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
  205. if (cwt [0] == null)
  206. cwt[0] = new ConditionalWeakTable <object,object> ();
  207. var res = FillWithNetwork (cwt[0]);
  208. return res;
  209. }
  210. static void ForcePromotion () {
  211. var o = new object[64000];
  212. for (int i = 0; i < 64000; ++i)
  213. o[i] = new int[10];
  214. }
  215. static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
  216. {
  217. var res = new List<object> ();
  218. for (int i = 0; i < 10; ++i) {
  219. res.Add (new object ());
  220. cwt[0].Add (res [i], i);
  221. }
  222. return res;
  223. }
  224. [Test]
  225. public void OldGenStress () {
  226. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  227. Assert.Ignore ("Not working on Boehm.");
  228. var cwt = new ConditionalWeakTable <object,object>[1];
  229. List<object> k = null;
  230. List<WeakReference> res, res2;
  231. res = res2 = null;
  232. FinalizerHelpers.PerformNoPinAction (() => {
  233. res = FillWithNetwork2 (cwt);
  234. ForcePromotion ();
  235. k = FillReachable (cwt);
  236. res2 = FillWithNetwork2 (cwt);
  237. });
  238. GC.Collect ();
  239. for (int i = 0; i < res.Count; ++i)
  240. Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
  241. for (int i = 0; i < res2.Count; ++i)
  242. Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
  243. for (int i = 0; i < k.Count; ++i) {
  244. object val;
  245. Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
  246. Assert.AreEqual (i, val, "k1-" + i);
  247. }
  248. }
  249. static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
  250. var handles = new List<GCHandle> ();
  251. var a = (object)10;
  252. var b = (object)20;
  253. var k1 = (object)30;
  254. var k2 = (object)40;
  255. handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
  256. handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
  257. handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
  258. handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
  259. cwt.Add (a, k1);
  260. cwt.Add (b, k2);
  261. return handles;
  262. }
  263. static void MakeObjMovable (List<GCHandle> handles)
  264. {
  265. for (int i = 0; i < handles.Count; ++i) {
  266. object o = handles[i].Target;
  267. handles[i].Free ();
  268. handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
  269. }
  270. }
  271. static void ForceMinor () {
  272. for (int i = 0; i < 64000; ++i)
  273. new object ();
  274. }
  275. public void PromotedCwtPointingToYoungStuff () {
  276. var cwt = new ConditionalWeakTable <object,object> ();
  277. var handles = FillTable3 (cwt);
  278. GC.Collect (0);
  279. /*Be 100% sure it will be on the young gen*/
  280. /*cwt array now will be on old gen*/
  281. ForceMinor ();
  282. ForceMinor ();
  283. ForceMinor ();
  284. //Make them non pinned
  285. MakeObjMovable (handles);
  286. GC.Collect (0);
  287. //Force a minor GC - this should cause
  288. ForceMinor ();
  289. ForceMinor ();
  290. ForceMinor ();
  291. ForceMinor ();
  292. GC.Collect (0);
  293. object r1, r2;
  294. Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
  295. Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
  296. GC.Collect ();
  297. cwt.GetHashCode ();
  298. }
  299. static object _lock1 = new object ();
  300. static object _lock2 = new object ();
  301. static int reachable = 0;
  302. public class FinalizableLink {
  303. // The sole purpose of this object is to keep a reference to another object, so it is fine to not use it.
  304. #pragma warning disable 414
  305. object obj;
  306. int id;
  307. #pragma warning restore 414
  308. ConditionalWeakTable <object,object> cwt;
  309. public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
  310. this.id = id;
  311. this.obj = obj;
  312. this.cwt = cwt;
  313. }
  314. ~FinalizableLink () {
  315. lock (_lock1) { ; }
  316. object obj;
  317. bool res = cwt.TryGetValue (this, out obj);
  318. if (res)
  319. ++reachable;
  320. if (reachable == 20)
  321. lock (_lock2) { Monitor.Pulse (_lock2); }
  322. }
  323. }
  324. static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
  325. {
  326. object a = new object ();
  327. object b = new FinalizableLink (0, a, cwt);
  328. cwt.Add (a, "foo");
  329. cwt.Add (b, "bar");
  330. for (int i = 1; i < 20; ++i) {
  331. b = new FinalizableLink (i, b, cwt);
  332. cwt.Add (b, i);
  333. }
  334. }
  335. [Test]
  336. public void FinalizableObjectsThatRetainDeadKeys ()
  337. {
  338. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  339. Assert.Ignore ("Not working on Boehm.");
  340. lock (_lock1) {
  341. var cwt = new ConditionalWeakTable <object,object> ();
  342. FinalizerHelpers.PerformNoPinAction (() => { FillWithFinalizable (cwt); });
  343. GC.Collect ();
  344. GC.Collect ();
  345. Assert.AreEqual (0, reachable, "#1");
  346. }
  347. GC.Collect ();
  348. GC.Collect ();
  349. lock (_lock2) { Monitor.Wait (_lock2, 1000); }
  350. Assert.AreEqual (20, reachable, "#1");
  351. }
  352. [Test]
  353. public void OldGenKeysMakeNewGenObjectsReachable ()
  354. {
  355. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  356. Assert.Ignore ("Not working on Boehm.");
  357. ConditionalWeakTable<object, Val> table = new ConditionalWeakTable<object, Val>();
  358. List<Key> keys = new List<Key>();
  359. //
  360. // This list references all keys for the duration of the program, so none
  361. // should be collected ever.
  362. //
  363. for (int x = 0; x < 1000; x++)
  364. keys.Add (new Key () { Foo = x });
  365. for (int i = 0; i < 1000; ++i) {
  366. // Insert all keys into the ConditionalWeakTable
  367. foreach (var key in keys)
  368. table.Add (key, new Val () { Foo = key.Foo });
  369. // Look up all keys to verify that they are still there
  370. Val val;
  371. foreach (var key in keys)
  372. Assert.IsTrue (table.TryGetValue (key, out val), "#1-" + i + "-k-" + key);
  373. // Remove all keys from the ConditionalWeakTable
  374. foreach (var key in keys)
  375. Assert.IsTrue (table.Remove (key), "#2-" + i + "-k-" + key);
  376. }
  377. }
  378. }
  379. }