2010-06-22 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / Test / System.Runtime.CompilerServices / ConditionalWeakTableTest.cs
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
29 #if NET_4_0
30
31 using NUnit.Framework;
32 using System;
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;
40
41
42 namespace MonoTests.System.Runtime.CompilerServices {
43
44         [TestFixture]
45         public class ConditionalWeakTableTest {
46
47         public class Link {
48                 public object obj;
49         
50                 public Link(object obj) { 
51                         this.obj = obj;
52                 }
53         }
54
55         public class Key {}
56
57         [Test]
58         public void GetValue () {
59                 var cwt = new ConditionalWeakTable <object,object> ();
60
61                 try {
62                         cwt.GetValue (null, k => null);
63                         Assert.Fail ("#0");
64                 } catch (ArgumentNullException) {}
65
66                 try {
67                         cwt.GetValue (20, null);
68                         Assert.Fail ("#1");
69                 } catch (ArgumentNullException) {}
70
71
72                 object key = "foo";
73                 object val = cwt.GetValue (key, k => new Link (k));
74                 Assert.IsTrue (val != null, "#2");
75                 Assert.AreEqual (typeof (Link), val.GetType () , "#3");
76
77                 Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
78         }
79
80         [Test]
81         public void GetOrCreateValue () {
82                 var cwt = new ConditionalWeakTable <object,object> ();
83
84                 try {
85                         cwt.GetOrCreateValue (null);
86                         Assert.Fail ("#0");
87                 } catch (ArgumentNullException) {}
88
89
90                 object key = "foo";
91                 object val = cwt.GetOrCreateValue (key);
92                 Assert.IsTrue (val != null, "#2");
93                 Assert.AreEqual (typeof (object), val.GetType () , "#3");
94
95                 Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
96
97                 var cwt2 = new ConditionalWeakTable <object, string> ();
98                 try {
99                         cwt2.GetOrCreateValue (key);
100                         Assert.Fail ("#5");
101                 } catch (MissingMethodException) {}
102         }
103
104         [Test]
105         public void Remove () {
106                 var cwt = new ConditionalWeakTable <object,object> ();
107                 object c = new Key ();
108
109                 cwt.Add (c, "x");
110
111                 try {
112                         cwt.Remove (null);
113                         Assert.Fail ("#0");
114                 } catch (ArgumentNullException) {}
115
116
117                 Assert.IsFalse (cwt.Remove ("x"), "#1");
118                 Assert.IsTrue (cwt.Remove (c), "#2");
119                 Assert.IsFalse (cwt.Remove (c), "#3");
120         }
121
122         [Test]
123         public void Add () {
124                 var cwt = new ConditionalWeakTable <object,object> ();
125                 object c = new Key ();
126
127                 cwt.Add (c, new Link (c));
128
129                 try {
130                         cwt.Add (c, new Link (c));
131                         Assert.Fail ("#0");
132                 } catch (ArgumentException) {}
133
134                 cwt.Add ("zzz", null);//ok
135
136                 try {
137                         cwt.Add (null, new Link (c));
138                         Assert.Fail ("#1");
139                 } catch (ArgumentNullException) {}
140         }
141
142         [Test]
143         public void TryGetValue () {
144                 var cwt = new ConditionalWeakTable <object,object> ();
145                 object res;
146                 object c = new Key ();
147
148                 cwt.Add (c, "foo");
149
150                 try {
151                         cwt.TryGetValue (null, out res);
152                         Assert.Fail ("#0");
153                 } catch (ArgumentNullException) {}
154
155                 Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
156                 Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
157                 Assert.AreEqual ("foo", res, "#3");
158         }
159
160
161         static void FillStuff (ConditionalWeakTable <object,object> cwt, 
162                         out List<object> keepAlive, out List<WeakReference> keys) {
163
164                 keepAlive = new List<object> ();
165                 keys = new List<WeakReference> ();
166
167                 object a = new Key ();
168                 object b = new Key ();
169                 object c = new Key ();
170                 
171                 cwt.Add (a, new string ("str0".ToCharArray ()));
172                 cwt.Add (b, "str1");
173                 cwt.Add (c, new Link (a));
174
175                 keepAlive.Add (c);
176                 keys.Add (new WeakReference(a));
177                 keys.Add (new WeakReference(b));
178                 keys.Add (new WeakReference(c));
179         }
180
181         [Test]
182         public void Reachability () {
183                 var cwt = new ConditionalWeakTable <object,object> ();
184
185                 List<object> keepAlive;
186                 List<WeakReference> keys;
187                 FillStuff (cwt, out keepAlive, out keys);
188
189                 GC.Collect ();
190
191                 Assert.IsTrue (keys [0].IsAlive, "r0");
192                 Assert.IsFalse (keys [1].IsAlive, "r1");
193                 Assert.IsTrue (keys [2].IsAlive, "r2");
194
195                 object res;
196                 Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
197                 Assert.IsTrue (res is Link, "ka1");
198
199                 Link link = res as Link;
200                 Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
201                 Assert.AreEqual ("str0", res, "ka3");
202         }
203
204
205         static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
206         {
207                 const int K = 500;
208                 object[] keys = new object [K];
209                 for (int i = 0; i < K; ++i)
210                         keys[i] = new object ();
211
212                 Random rand = new Random ();
213
214                 /*produce a complex enough network of links*/
215                 for (int i = 0; i < K; ++i)
216                         cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
217
218                 var res = new List<WeakReference> ();
219
220                 for (int i = 0; i < 10; ++i)
221                         res.Add (new WeakReference (keys [rand.Next (K)]));
222                 Array.Clear (keys, 0, keys.Length);
223
224                 return res;
225         }
226
227         [Test]
228         public void InsertStress () {
229                 var cwt = new ConditionalWeakTable <object,object> ();
230
231                 var a = new object ();
232                 var b = new object ();
233
234                 cwt.Add (a, new object ());
235                 cwt.Add (b, new object ());
236
237                 List<WeakReference> res = null;
238                 ThreadStart dele = () => { res = FillWithNetwork (cwt); };
239                 var th = new Thread(dele);
240                 th.Start ();
241                 th.Join ();
242
243                 GC.Collect ();
244                 GC.Collect ();
245
246                 for (int i = 0; i < res.Count; ++i)
247                         Assert.IsFalse (res [i].IsAlive, "#r" + i);
248         }
249
250         static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
251                 if (cwt [0] == null)
252                         cwt[0] = new ConditionalWeakTable <object,object> ();
253                 var res = FillWithNetwork (cwt[0]);
254
255                 return res;
256         }
257
258         static void ForcePromotion () {
259                 var o = new object[64000];
260
261                 for (int i = 0; i < 64000; ++i)
262                         o[i] = new int[10];
263         }
264
265         static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
266         {
267                 var res = new List<object> ();
268                 for (int i = 0; i < 10; ++i) {
269                         res.Add (new object ());
270                         cwt[0].Add (res [i], i);
271                 }
272
273                 return res;
274         }
275
276         [Test]
277         public void OldGenStress () {
278                 var cwt = new ConditionalWeakTable <object,object>[1];
279                 List<object> k = null;
280                 List<WeakReference> res, res2;
281                 res = res2 = null;
282
283                 ThreadStart dele = () => {
284                         res = FillWithNetwork2 (cwt);
285                         ForcePromotion ();
286                         k = FillReachable (cwt);
287                         res2 = FillWithNetwork2 (cwt);
288                 };
289
290                 var th = new Thread(dele);
291                 th.Start ();
292                 th.Join ();
293
294                 GC.Collect ();
295
296                 for (int i = 0; i < res.Count; ++i)
297                         Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
298                 for (int i = 0; i < res2.Count; ++i)
299                         Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
300
301                 for (int i = 0; i < k.Count; ++i) {
302                         object val;
303                         Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
304                         Assert.AreEqual (i, val, "k1-" + i);
305                 }
306         }
307
308
309         static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
310                 var handles = new List<GCHandle> ();
311
312                 var a = (object)10;
313                 var b = (object)20;
314                 var k1 = (object)30;
315                 var k2 = (object)40;
316
317                 handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
318                 handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
319                 handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
320                 handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
321                 
322                 cwt.Add (a, k1);
323                 cwt.Add (b, k2);
324
325         
326                 return handles;
327         }
328
329         static void MakeObjMovable (List<GCHandle> handles)
330         {
331                 for (int i = 0; i < handles.Count; ++i) {
332                         object o = handles[i].Target;
333                         handles[i].Free ();
334                         handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
335                 }
336         }
337
338         static void ForceMinor () {
339                 for (int i = 0; i < 64000; ++i)
340                         new object ();
341         }
342
343         public void PromotedCwtPointingToYoungStuff () {
344                 var cwt = new ConditionalWeakTable <object,object> ();
345
346                 var handles = FillTable3 (cwt);
347
348                 GC.Collect (0);
349
350                 /*Be 100% sure it will be on the young gen*/
351
352                 /*cwt array now will be on old gen*/
353                 ForceMinor ();
354                 ForceMinor ();
355                 ForceMinor ();
356
357                 //Make them non pinned
358                 MakeObjMovable (handles);
359
360                 GC.Collect (0);
361                 
362                 //Force a minor GC - this should cause
363                 ForceMinor ();
364                 ForceMinor ();
365                 ForceMinor ();
366                 ForceMinor ();
367
368                 GC.Collect (0);
369
370                 object r1, r2;
371                 Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
372                 Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
373
374                 GC.Collect ();
375                 cwt.GetHashCode ();
376         }
377
378         static object _lock1 = new object ();
379         static object _lock2 = new object ();
380         static int reachable = 0;
381  
382         public class FinalizableLink {
383                 object obj;
384                 ConditionalWeakTable <object,object> cwt;
385                 int id;
386
387                 public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
388                         this.id = id;
389                         this.obj = obj;
390                         this.cwt = cwt;
391                 }
392
393                 ~FinalizableLink () {
394                         lock (_lock1) { ; }
395                         object obj;
396                         bool res = cwt.TryGetValue (this, out obj);
397                         if (res)
398                                 ++reachable;
399                         if (reachable == 20)
400                                 lock (_lock2) { Monitor.Pulse (_lock2); }
401                 }
402         }
403
404         static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
405         {
406                 object a = new object ();
407                 object b = new FinalizableLink (0, a, cwt);
408                 cwt.Add (a, "foo");
409                 cwt.Add (b, "bar");
410
411                 for (int i = 1; i < 20; ++i) {
412                         b = new FinalizableLink (i, b, cwt);
413                         cwt.Add (b, i);
414                 }
415         }
416
417         [Test]
418         public void FinalizableObjectsThatRetainDeadKeys ()
419         {
420                 lock (_lock1) { 
421                         var cwt = new ConditionalWeakTable <object,object> ();
422                         ThreadStart dele = () => { FillWithFinalizable (cwt); };
423                         var th = new Thread(dele);
424                         th.Start ();
425                         th.Join ();
426                         GC.Collect ();
427                         GC.Collect ();
428
429                         Assert.AreEqual (0, reachable, "#1");
430                 }
431
432                 GC.Collect ();
433                 GC.Collect ();
434                 lock (_lock2) { Monitor.Wait (_lock2, 1000); }
435
436                 Assert.AreEqual (20, reachable, "#1");
437         }
438         }
439 }
440
441 #endif