2 // ConditionalWeakTableTest.cs
5 // Rodrigo Kumpera <rkumpera@novell.com>
7 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using NUnit.Framework;
33 using System.Reflection;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Runtime.Serialization;
37 using System.Security.Permissions;
38 using System.Collections.Generic;
39 using System.Threading;
42 namespace MonoTests.System.Runtime.CompilerServices {
45 public class ConditionalWeakTableTest {
50 public Link(object obj) {
55 class Key { public int Foo; }
56 class Val { public int Foo; }
59 public void GetValue () {
60 var cwt = new ConditionalWeakTable <object,object> ();
63 cwt.GetValue (null, k => null);
65 } catch (ArgumentNullException) {}
68 cwt.GetValue (20, null);
70 } catch (ArgumentNullException) {}
74 object val = cwt.GetValue (key, k => new Link (k));
75 Assert.IsTrue (val != null, "#2");
76 Assert.AreEqual (typeof (Link), val.GetType () , "#3");
78 Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
82 public void GetOrCreateValue () {
83 var cwt = new ConditionalWeakTable <object,object> ();
86 cwt.GetOrCreateValue (null);
88 } catch (ArgumentNullException) {}
92 object val = cwt.GetOrCreateValue (key);
93 Assert.IsTrue (val != null, "#2");
94 Assert.AreEqual (typeof (object), val.GetType () , "#3");
96 Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
98 var cwt2 = new ConditionalWeakTable <object, string> ();
100 cwt2.GetOrCreateValue (key);
102 } catch (MissingMethodException) {}
106 public void Remove () {
107 var cwt = new ConditionalWeakTable <object,object> ();
108 object c = new Key ();
115 } catch (ArgumentNullException) {}
118 Assert.IsFalse (cwt.Remove ("x"), "#1");
119 Assert.IsTrue (cwt.Remove (c), "#2");
120 Assert.IsFalse (cwt.Remove (c), "#3");
125 var cwt = new ConditionalWeakTable <object,object> ();
126 object c = new Key ();
128 cwt.Add (c, new Link (c));
131 cwt.Add (c, new Link (c));
133 } catch (ArgumentException) {}
135 cwt.Add ("zzz", null);//ok
138 cwt.Add (null, new Link (c));
140 } catch (ArgumentNullException) {}
144 public void TryGetValue () {
145 var cwt = new ConditionalWeakTable <object,object> ();
147 object c = new Key ();
152 cwt.TryGetValue (null, out res);
154 } catch (ArgumentNullException) {}
156 Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
157 Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
158 Assert.AreEqual ("foo", res, "#3");
162 static void FillStuff (ConditionalWeakTable <object,object> cwt,
163 out List<object> keepAlive, out List<WeakReference> keys) {
165 keepAlive = new List<object> ();
166 keys = new List<WeakReference> ();
168 object a = new Key ();
169 object b = new Key ();
170 object c = new Key ();
172 cwt.Add (a, new string ("str0".ToCharArray ()));
174 cwt.Add (c, new Link (a));
177 keys.Add (new WeakReference(a));
178 keys.Add (new WeakReference(b));
179 keys.Add (new WeakReference(c));
183 public void Reachability () {
184 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
186 var cwt = new ConditionalWeakTable <object,object> ();
187 List<object> keepAlive;
188 List<WeakReference> keys;
189 FillStuff (cwt, out keepAlive, out keys);
193 Assert.IsTrue (keys [0].IsAlive, "r0");
194 Assert.IsFalse (keys [1].IsAlive, "r1");
195 Assert.IsTrue (keys [2].IsAlive, "r2");
198 Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
199 Assert.IsTrue (res is Link, "ka1");
201 Link link = res as Link;
202 Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
203 Assert.AreEqual ("str0", res, "ka3");
207 static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
210 object[] keys = new object [K];
211 for (int i = 0; i < K; ++i)
212 keys[i] = new object ();
214 Random rand = new Random ();
216 /*produce a complex enough network of links*/
217 for (int i = 0; i < K; ++i)
218 cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
220 var res = new List<WeakReference> ();
222 for (int i = 0; i < 10; ++i)
223 res.Add (new WeakReference (keys [rand.Next (K)]));
224 Array.Clear (keys, 0, keys.Length);
230 public void InsertStress () {
231 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
233 var cwt = new ConditionalWeakTable <object,object> ();
235 var a = new object ();
236 var b = new object ();
238 cwt.Add (a, new object ());
239 cwt.Add (b, new object ());
241 List<WeakReference> res = null;
242 ThreadStart dele = () => { res = FillWithNetwork (cwt); };
243 var th = new Thread(dele);
250 for (int i = 0; i < res.Count; ++i)
251 Assert.IsFalse (res [i].IsAlive, "#r" + i);
254 static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
256 cwt[0] = new ConditionalWeakTable <object,object> ();
257 var res = FillWithNetwork (cwt[0]);
262 static void ForcePromotion () {
263 var o = new object[64000];
265 for (int i = 0; i < 64000; ++i)
269 static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
271 var res = new List<object> ();
272 for (int i = 0; i < 10; ++i) {
273 res.Add (new object ());
274 cwt[0].Add (res [i], i);
281 public void OldGenStress () {
282 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
284 var cwt = new ConditionalWeakTable <object,object>[1];
285 List<object> k = null;
286 List<WeakReference> res, res2;
289 ThreadStart dele = () => {
290 res = FillWithNetwork2 (cwt);
292 k = FillReachable (cwt);
293 res2 = FillWithNetwork2 (cwt);
296 var th = new Thread(dele);
302 for (int i = 0; i < res.Count; ++i)
303 Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
304 for (int i = 0; i < res2.Count; ++i)
305 Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
307 for (int i = 0; i < k.Count; ++i) {
309 Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
310 Assert.AreEqual (i, val, "k1-" + i);
315 static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
316 var handles = new List<GCHandle> ();
323 handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
324 handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
325 handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
326 handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
335 static void MakeObjMovable (List<GCHandle> handles)
337 for (int i = 0; i < handles.Count; ++i) {
338 object o = handles[i].Target;
340 handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
344 static void ForceMinor () {
345 for (int i = 0; i < 64000; ++i)
349 public void PromotedCwtPointingToYoungStuff () {
350 var cwt = new ConditionalWeakTable <object,object> ();
352 var handles = FillTable3 (cwt);
356 /*Be 100% sure it will be on the young gen*/
358 /*cwt array now will be on old gen*/
363 //Make them non pinned
364 MakeObjMovable (handles);
368 //Force a minor GC - this should cause
377 Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
378 Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
384 static object _lock1 = new object ();
385 static object _lock2 = new object ();
386 static int reachable = 0;
388 public class FinalizableLink {
390 ConditionalWeakTable <object,object> cwt;
393 public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
399 ~FinalizableLink () {
402 bool res = cwt.TryGetValue (this, out obj);
406 lock (_lock2) { Monitor.Pulse (_lock2); }
410 static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
412 object a = new object ();
413 object b = new FinalizableLink (0, a, cwt);
417 for (int i = 1; i < 20; ++i) {
418 b = new FinalizableLink (i, b, cwt);
424 public void FinalizableObjectsThatRetainDeadKeys ()
426 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
429 var cwt = new ConditionalWeakTable <object,object> ();
430 ThreadStart dele = () => { FillWithFinalizable (cwt); };
431 var th = new Thread(dele);
437 Assert.AreEqual (0, reachable, "#1");
442 lock (_lock2) { Monitor.Wait (_lock2, 1000); }
444 Assert.AreEqual (20, reachable, "#1");
448 public void OldGenKeysMakeNewGenObjectsReachable ()
450 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
452 ConditionalWeakTable<object, Val> table = new ConditionalWeakTable<object, Val>();
453 List<Key> keys = new List<Key>();
456 // This list references all keys for the duration of the program, so none
457 // should be collected ever.
459 for (int x = 0; x < 1000; x++)
460 keys.Add(new Key() { Foo = x });
462 for (int i = 0; i < 10000; ++i) {
463 // Insert all keys into the ConditionalWeakTable
464 foreach (var key in keys)
465 table.Add(key, new Val() { Foo = key.Foo });
467 // Look up all keys to verify that they are still there
469 foreach (var key in keys)
470 Assert.IsTrue (table.TryGetValue(key, out val), "#1-" + i);
472 // Remove all keys from the ConditionalWeakTable
473 foreach (var key in keys)
474 Assert.IsTrue (!table.Remove(key), "#2-" + i);