PageRenderTime 54ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://github.com/schani/mono
C# | 497 lines | 349 code | 108 blank | 40 comment | 31 complexity | 06c03d4d4fa5dfa49d07bf2b381aab8e MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, Unlicense
  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. namespace MonoTests.System.Runtime.CompilerServices {
  38. [TestFixture]
  39. public class ConditionalWeakTableTest {
  40. public class Link {
  41. public object obj;
  42. public Link(object obj) {
  43. this.obj = obj;
  44. }
  45. }
  46. class Key {
  47. public int Foo;
  48. public override string ToString () {
  49. return "key-" + Foo;
  50. }
  51. }
  52. class Val {
  53. public int Foo;
  54. public override string ToString () {
  55. return "value-" + Foo;
  56. }
  57. }
  58. [Test]
  59. public void GetValue () {
  60. var cwt = new ConditionalWeakTable <object,object> ();
  61. try {
  62. cwt.GetValue (null, k => null);
  63. Assert.Fail ("#0");
  64. } catch (ArgumentNullException) {}
  65. try {
  66. cwt.GetValue (20, null);
  67. Assert.Fail ("#1");
  68. } catch (ArgumentNullException) {}
  69. object key = "foo";
  70. object val = cwt.GetValue (key, k => new Link (k));
  71. Assert.IsTrue (val != null, "#2");
  72. Assert.AreEqual (typeof (Link), val.GetType () , "#3");
  73. Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
  74. }
  75. [Test]
  76. public void GetOrCreateValue () {
  77. var cwt = new ConditionalWeakTable <object,object> ();
  78. try {
  79. cwt.GetOrCreateValue (null);
  80. Assert.Fail ("#0");
  81. } catch (ArgumentNullException) {}
  82. object key = "foo";
  83. object val = cwt.GetOrCreateValue (key);
  84. Assert.IsTrue (val != null, "#2");
  85. Assert.AreEqual (typeof (object), val.GetType () , "#3");
  86. Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
  87. var cwt2 = new ConditionalWeakTable <object, string> ();
  88. try {
  89. cwt2.GetOrCreateValue (key);
  90. Assert.Fail ("#5");
  91. } catch (MissingMethodException) {}
  92. }
  93. [Test]
  94. public void Remove () {
  95. var cwt = new ConditionalWeakTable <object,object> ();
  96. object c = new Key ();
  97. cwt.Add (c, "x");
  98. try {
  99. cwt.Remove (null);
  100. Assert.Fail ("#0");
  101. } catch (ArgumentNullException) {}
  102. Assert.IsFalse (cwt.Remove ("x"), "#1");
  103. Assert.IsTrue (cwt.Remove (c), "#2");
  104. Assert.IsFalse (cwt.Remove (c), "#3");
  105. }
  106. [Test]
  107. public void Add () {
  108. var cwt = new ConditionalWeakTable <object,object> ();
  109. object c = new Key ();
  110. cwt.Add (c, new Link (c));
  111. try {
  112. cwt.Add (c, new Link (c));
  113. Assert.Fail ("#0");
  114. } catch (ArgumentException) {}
  115. cwt.Add ("zzz", null);//ok
  116. try {
  117. cwt.Add (null, new Link (c));
  118. Assert.Fail ("#1");
  119. } catch (ArgumentNullException) {}
  120. }
  121. [Test]
  122. public void TryGetValue () {
  123. var cwt = new ConditionalWeakTable <object,object> ();
  124. object res;
  125. object c = new Key ();
  126. cwt.Add (c, "foo");
  127. try {
  128. cwt.TryGetValue (null, out res);
  129. Assert.Fail ("#0");
  130. } catch (ArgumentNullException) {}
  131. Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
  132. Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
  133. Assert.AreEqual ("foo", res, "#3");
  134. }
  135. static void FillStuff (ConditionalWeakTable <object,object> cwt,
  136. out List<object> keepAlive, out List<WeakReference> keys) {
  137. keepAlive = new List<object> ();
  138. keys = new List<WeakReference> ();
  139. object a = new Key ();
  140. object b = new Key ();
  141. object c = new Key ();
  142. cwt.Add (a, new string ("str0".ToCharArray ()));
  143. cwt.Add (b, "str1");
  144. cwt.Add (c, new Link (a));
  145. keepAlive.Add (c);
  146. keys.Add (new WeakReference(a));
  147. keys.Add (new WeakReference(b));
  148. keys.Add (new WeakReference(c));
  149. }
  150. [Test]
  151. public void Reachability () {
  152. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  153. Assert.Ignore ("Not working on Boehm.");
  154. var cwt = new ConditionalWeakTable <object,object> ();
  155. List<object> keepAlive = null;
  156. List<WeakReference> keys = null;
  157. Thread t = new Thread (delegate () {
  158. FillStuff (cwt, out keepAlive, out keys);
  159. });
  160. t.Start ();
  161. t.Join ();
  162. GC.Collect ();
  163. Assert.IsTrue (keys [0].IsAlive, "r0");
  164. Assert.IsFalse (keys [1].IsAlive, "r1");
  165. Assert.IsTrue (keys [2].IsAlive, "r2");
  166. object res;
  167. Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
  168. Assert.IsTrue (res is Link, "ka1");
  169. Link link = res as Link;
  170. Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
  171. Assert.AreEqual ("str0", res, "ka3");
  172. }
  173. static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
  174. {
  175. const int K = 500;
  176. object[] keys = new object [K];
  177. for (int i = 0; i < K; ++i)
  178. keys[i] = new object ();
  179. Random rand = new Random ();
  180. /*produce a complex enough network of links*/
  181. for (int i = 0; i < K; ++i)
  182. cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
  183. var res = new List<WeakReference> ();
  184. for (int i = 0; i < 10; ++i)
  185. res.Add (new WeakReference (keys [rand.Next (K)]));
  186. Array.Clear (keys, 0, keys.Length);
  187. return res;
  188. }
  189. [Test]
  190. public void InsertStress () {
  191. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  192. Assert.Ignore ("Not working on Boehm.");
  193. var cwt = new ConditionalWeakTable <object,object> ();
  194. var a = new object ();
  195. var b = new object ();
  196. cwt.Add (a, new object ());
  197. cwt.Add (b, new object ());
  198. List<WeakReference> res = null;
  199. ThreadStart dele = () => { res = FillWithNetwork (cwt); };
  200. var th = new Thread(dele);
  201. th.Start ();
  202. th.Join ();
  203. GC.Collect ();
  204. GC.Collect ();
  205. for (int i = 0; i < res.Count; ++i)
  206. Assert.IsFalse (res [i].IsAlive, "#r" + i);
  207. }
  208. static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
  209. if (cwt [0] == null)
  210. cwt[0] = new ConditionalWeakTable <object,object> ();
  211. var res = FillWithNetwork (cwt[0]);
  212. return res;
  213. }
  214. static void ForcePromotion () {
  215. var o = new object[64000];
  216. for (int i = 0; i < 64000; ++i)
  217. o[i] = new int[10];
  218. }
  219. static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
  220. {
  221. var res = new List<object> ();
  222. for (int i = 0; i < 10; ++i) {
  223. res.Add (new object ());
  224. cwt[0].Add (res [i], i);
  225. }
  226. return res;
  227. }
  228. [Test]
  229. public void OldGenStress () {
  230. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  231. Assert.Ignore ("Not working on Boehm.");
  232. var cwt = new ConditionalWeakTable <object,object>[1];
  233. List<object> k = null;
  234. List<WeakReference> res, res2;
  235. res = res2 = null;
  236. ThreadStart dele = () => {
  237. res = FillWithNetwork2 (cwt);
  238. ForcePromotion ();
  239. k = FillReachable (cwt);
  240. res2 = FillWithNetwork2 (cwt);
  241. };
  242. var th = new Thread(dele);
  243. th.Start ();
  244. th.Join ();
  245. GC.Collect ();
  246. for (int i = 0; i < res.Count; ++i)
  247. Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
  248. for (int i = 0; i < res2.Count; ++i)
  249. Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
  250. for (int i = 0; i < k.Count; ++i) {
  251. object val;
  252. Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
  253. Assert.AreEqual (i, val, "k1-" + i);
  254. }
  255. }
  256. static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
  257. var handles = new List<GCHandle> ();
  258. var a = (object)10;
  259. var b = (object)20;
  260. var k1 = (object)30;
  261. var k2 = (object)40;
  262. handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
  263. handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
  264. handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
  265. handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
  266. cwt.Add (a, k1);
  267. cwt.Add (b, k2);
  268. return handles;
  269. }
  270. static void MakeObjMovable (List<GCHandle> handles)
  271. {
  272. for (int i = 0; i < handles.Count; ++i) {
  273. object o = handles[i].Target;
  274. handles[i].Free ();
  275. handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
  276. }
  277. }
  278. static void ForceMinor () {
  279. for (int i = 0; i < 64000; ++i)
  280. new object ();
  281. }
  282. public void PromotedCwtPointingToYoungStuff () {
  283. var cwt = new ConditionalWeakTable <object,object> ();
  284. var handles = FillTable3 (cwt);
  285. GC.Collect (0);
  286. /*Be 100% sure it will be on the young gen*/
  287. /*cwt array now will be on old gen*/
  288. ForceMinor ();
  289. ForceMinor ();
  290. ForceMinor ();
  291. //Make them non pinned
  292. MakeObjMovable (handles);
  293. GC.Collect (0);
  294. //Force a minor GC - this should cause
  295. ForceMinor ();
  296. ForceMinor ();
  297. ForceMinor ();
  298. ForceMinor ();
  299. GC.Collect (0);
  300. object r1, r2;
  301. Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
  302. Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
  303. GC.Collect ();
  304. cwt.GetHashCode ();
  305. }
  306. static object _lock1 = new object ();
  307. static object _lock2 = new object ();
  308. static int reachable = 0;
  309. public class FinalizableLink {
  310. // The sole purpose of this object is to keep a reference to another object, so it is fine to not use it.
  311. #pragma warning disable 414
  312. object obj;
  313. int id;
  314. #pragma warning restore 414
  315. ConditionalWeakTable <object,object> cwt;
  316. public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
  317. this.id = id;
  318. this.obj = obj;
  319. this.cwt = cwt;
  320. }
  321. ~FinalizableLink () {
  322. lock (_lock1) { ; }
  323. object obj;
  324. bool res = cwt.TryGetValue (this, out obj);
  325. if (res)
  326. ++reachable;
  327. if (reachable == 20)
  328. lock (_lock2) { Monitor.Pulse (_lock2); }
  329. }
  330. }
  331. static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
  332. {
  333. object a = new object ();
  334. object b = new FinalizableLink (0, a, cwt);
  335. cwt.Add (a, "foo");
  336. cwt.Add (b, "bar");
  337. for (int i = 1; i < 20; ++i) {
  338. b = new FinalizableLink (i, b, cwt);
  339. cwt.Add (b, i);
  340. }
  341. }
  342. [Test]
  343. public void FinalizableObjectsThatRetainDeadKeys ()
  344. {
  345. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  346. Assert.Ignore ("Not working on Boehm.");
  347. lock (_lock1) {
  348. var cwt = new ConditionalWeakTable <object,object> ();
  349. ThreadStart dele = () => { FillWithFinalizable (cwt); };
  350. var th = new Thread(dele);
  351. th.Start ();
  352. th.Join ();
  353. GC.Collect ();
  354. GC.Collect ();
  355. Assert.AreEqual (0, reachable, "#1");
  356. }
  357. GC.Collect ();
  358. GC.Collect ();
  359. lock (_lock2) { Monitor.Wait (_lock2, 1000); }
  360. Assert.AreEqual (20, reachable, "#1");
  361. }
  362. [Test]
  363. public void OldGenKeysMakeNewGenObjectsReachable ()
  364. {
  365. if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
  366. Assert.Ignore ("Not working on Boehm.");
  367. ConditionalWeakTable<object, Val> table = new ConditionalWeakTable<object, Val>();
  368. List<Key> keys = new List<Key>();
  369. //
  370. // This list references all keys for the duration of the program, so none
  371. // should be collected ever.
  372. //
  373. for (int x = 0; x < 1000; x++)
  374. keys.Add (new Key () { Foo = x });
  375. for (int i = 0; i < 1000; ++i) {
  376. // Insert all keys into the ConditionalWeakTable
  377. foreach (var key in keys)
  378. table.Add (key, new Val () { Foo = key.Foo });
  379. // Look up all keys to verify that they are still there
  380. Val val;
  381. foreach (var key in keys)
  382. Assert.IsTrue (table.TryGetValue (key, out val), "#1-" + i + "-k-" + key);
  383. // Remove all keys from the ConditionalWeakTable
  384. foreach (var key in keys)
  385. Assert.IsTrue (table.Remove (key), "#2-" + i + "-k-" + key);
  386. }
  387. }
  388. }
  389. }