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.
30 using NUnit.Framework;
32 using System.Reflection;
33 using System.Runtime.CompilerServices;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Serialization;
36 using System.Security.Permissions;
37 using System.Collections.Generic;
38 using System.Threading;
41 namespace MonoTests.System.Runtime.CompilerServices {
44 public class ConditionalWeakTableTest {
49 public Link(object obj) {
56 public override string ToString () {
63 public override string ToString () {
64 return "value-" + Foo;
70 public void GetValue () {
71 var cwt = new ConditionalWeakTable <object,object> ();
74 cwt.GetValue (null, k => null);
76 } catch (ArgumentNullException) {}
79 cwt.GetValue (20, null);
81 } catch (ArgumentNullException) {}
85 object val = cwt.GetValue (key, k => new Link (k));
86 Assert.IsTrue (val != null, "#2");
87 Assert.AreEqual (typeof (Link), val.GetType () , "#3");
89 Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
93 public void GetOrCreateValue () {
94 var cwt = new ConditionalWeakTable <object,object> ();
97 cwt.GetOrCreateValue (null);
99 } catch (ArgumentNullException) {}
103 object val = cwt.GetOrCreateValue (key);
104 Assert.IsTrue (val != null, "#2");
105 Assert.AreEqual (typeof (object), val.GetType () , "#3");
107 Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
109 var cwt2 = new ConditionalWeakTable <object, string> ();
111 cwt2.GetOrCreateValue (key);
113 } catch (MissingMethodException) {}
117 public void Remove () {
118 var cwt = new ConditionalWeakTable <object,object> ();
119 object c = new Key ();
126 } catch (ArgumentNullException) {}
129 Assert.IsFalse (cwt.Remove ("x"), "#1");
130 Assert.IsTrue (cwt.Remove (c), "#2");
131 Assert.IsFalse (cwt.Remove (c), "#3");
136 var cwt = new ConditionalWeakTable <object,object> ();
137 object c = new Key ();
139 cwt.Add (c, new Link (c));
142 cwt.Add (c, new Link (c));
144 } catch (ArgumentException) {}
146 cwt.Add ("zzz", null);//ok
149 cwt.Add (null, new Link (c));
151 } catch (ArgumentNullException) {}
155 public void TryGetValue () {
156 var cwt = new ConditionalWeakTable <object,object> ();
158 object c = new Key ();
163 cwt.TryGetValue (null, out res);
165 } catch (ArgumentNullException) {}
167 Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
168 Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
169 Assert.AreEqual ("foo", res, "#3");
173 static void FillStuff (ConditionalWeakTable <object,object> cwt,
174 out List<object> keepAlive, out List<WeakReference> keys) {
176 keepAlive = new List<object> ();
177 keys = new List<WeakReference> ();
179 object a = new Key ();
180 object b = new Key ();
181 object c = new Key ();
183 cwt.Add (a, new string ("str0".ToCharArray ()));
185 cwt.Add (c, new Link (a));
188 keys.Add (new WeakReference(a));
189 keys.Add (new WeakReference(b));
190 keys.Add (new WeakReference(c));
194 public void Reachability () {
195 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
196 Assert.Ignore ("Not working on Boehm.");
197 var cwt = new ConditionalWeakTable <object,object> ();
198 List<object> keepAlive = null;
199 List<WeakReference> keys = null;
200 Thread t = new Thread (delegate () {
201 FillStuff (cwt, out keepAlive, out keys);
208 Assert.IsTrue (keys [0].IsAlive, "r0");
209 Assert.IsFalse (keys [1].IsAlive, "r1");
210 Assert.IsTrue (keys [2].IsAlive, "r2");
213 Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
214 Assert.IsTrue (res is Link, "ka1");
216 Link link = res as Link;
217 Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
218 Assert.AreEqual ("str0", res, "ka3");
222 static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
225 object[] keys = new object [K];
226 for (int i = 0; i < K; ++i)
227 keys[i] = new object ();
229 Random rand = new Random ();
231 /*produce a complex enough network of links*/
232 for (int i = 0; i < K; ++i)
233 cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
235 var res = new List<WeakReference> ();
237 for (int i = 0; i < 10; ++i)
238 res.Add (new WeakReference (keys [rand.Next (K)]));
239 Array.Clear (keys, 0, keys.Length);
245 public void InsertStress () {
246 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
247 Assert.Ignore ("Not working on Boehm.");
248 var cwt = new ConditionalWeakTable <object,object> ();
250 var a = new object ();
251 var b = new object ();
253 cwt.Add (a, new object ());
254 cwt.Add (b, new object ());
256 List<WeakReference> res = null;
257 ThreadStart dele = () => { res = FillWithNetwork (cwt); };
258 var th = new Thread(dele);
265 for (int i = 0; i < res.Count; ++i)
266 Assert.IsFalse (res [i].IsAlive, "#r" + i);
269 static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
271 cwt[0] = new ConditionalWeakTable <object,object> ();
272 var res = FillWithNetwork (cwt[0]);
277 static void ForcePromotion () {
278 var o = new object[64000];
280 for (int i = 0; i < 64000; ++i)
284 static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
286 var res = new List<object> ();
287 for (int i = 0; i < 10; ++i) {
288 res.Add (new object ());
289 cwt[0].Add (res [i], i);
296 public void OldGenStress () {
297 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
298 Assert.Ignore ("Not working on Boehm.");
299 var cwt = new ConditionalWeakTable <object,object>[1];
300 List<object> k = null;
301 List<WeakReference> res, res2;
304 ThreadStart dele = () => {
305 res = FillWithNetwork2 (cwt);
307 k = FillReachable (cwt);
308 res2 = FillWithNetwork2 (cwt);
311 var th = new Thread(dele);
317 for (int i = 0; i < res.Count; ++i)
318 Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
319 for (int i = 0; i < res2.Count; ++i)
320 Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
322 for (int i = 0; i < k.Count; ++i) {
324 Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
325 Assert.AreEqual (i, val, "k1-" + i);
330 static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
331 var handles = new List<GCHandle> ();
338 handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
339 handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
340 handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
341 handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
350 static void MakeObjMovable (List<GCHandle> handles)
352 for (int i = 0; i < handles.Count; ++i) {
353 object o = handles[i].Target;
355 handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
359 static void ForceMinor () {
360 for (int i = 0; i < 64000; ++i)
364 public void PromotedCwtPointingToYoungStuff () {
365 var cwt = new ConditionalWeakTable <object,object> ();
367 var handles = FillTable3 (cwt);
371 /*Be 100% sure it will be on the young gen*/
373 /*cwt array now will be on old gen*/
378 //Make them non pinned
379 MakeObjMovable (handles);
383 //Force a minor GC - this should cause
392 Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
393 Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
399 static object _lock1 = new object ();
400 static object _lock2 = new object ();
401 static int reachable = 0;
403 public class FinalizableLink {
404 // The sole purpose of this object is to keep a reference to another object, so it is fine to not use it.
405 #pragma warning disable 414
408 #pragma warning restore 414
409 ConditionalWeakTable <object,object> cwt;
411 public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
417 ~FinalizableLink () {
420 bool res = cwt.TryGetValue (this, out obj);
424 lock (_lock2) { Monitor.Pulse (_lock2); }
428 static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
430 object a = new object ();
431 object b = new FinalizableLink (0, a, cwt);
435 for (int i = 1; i < 20; ++i) {
436 b = new FinalizableLink (i, b, cwt);
442 public void FinalizableObjectsThatRetainDeadKeys ()
444 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
445 Assert.Ignore ("Not working on Boehm.");
447 var cwt = new ConditionalWeakTable <object,object> ();
448 ThreadStart dele = () => { FillWithFinalizable (cwt); };
449 var th = new Thread(dele);
455 Assert.AreEqual (0, reachable, "#1");
460 lock (_lock2) { Monitor.Wait (_lock2, 1000); }
462 Assert.AreEqual (20, reachable, "#1");
466 public void OldGenKeysMakeNewGenObjectsReachable ()
468 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
469 Assert.Ignore ("Not working on Boehm.");
470 ConditionalWeakTable<object, Val> table = new ConditionalWeakTable<object, Val>();
471 List<Key> keys = new List<Key>();
474 // This list references all keys for the duration of the program, so none
475 // should be collected ever.
477 for (int x = 0; x < 1000; x++)
478 keys.Add (new Key () { Foo = x });
480 for (int i = 0; i < 1000; ++i) {
481 // Insert all keys into the ConditionalWeakTable
482 foreach (var key in keys)
483 table.Add (key, new Val () { Foo = key.Foo });
485 // Look up all keys to verify that they are still there
487 foreach (var key in keys)
488 Assert.IsTrue (table.TryGetValue (key, out val), "#1-" + i + "-k-" + key);
490 // Remove all keys from the ConditionalWeakTable
491 foreach (var key in keys)
492 Assert.IsTrue (table.Remove (key), "#2-" + i + "-k-" + key);