Merge remote branch 'upstream/master'
[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                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
184                         return;
185                 var cwt = new ConditionalWeakTable <object,object> ();
186                 List<object> keepAlive;
187                 List<WeakReference> keys;
188                 FillStuff (cwt, out keepAlive, out keys);
189
190                 GC.Collect ();
191
192                 Assert.IsTrue (keys [0].IsAlive, "r0");
193                 Assert.IsFalse (keys [1].IsAlive, "r1");
194                 Assert.IsTrue (keys [2].IsAlive, "r2");
195
196                 object res;
197                 Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
198                 Assert.IsTrue (res is Link, "ka1");
199
200                 Link link = res as Link;
201                 Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
202                 Assert.AreEqual ("str0", res, "ka3");
203         }
204
205
206         static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
207         {
208                 const int K = 500;
209                 object[] keys = new object [K];
210                 for (int i = 0; i < K; ++i)
211                         keys[i] = new object ();
212
213                 Random rand = new Random ();
214
215                 /*produce a complex enough network of links*/
216                 for (int i = 0; i < K; ++i)
217                         cwt.Add (keys [i], new Link (keys [rand.Next (K)]));
218
219                 var res = new List<WeakReference> ();
220
221                 for (int i = 0; i < 10; ++i)
222                         res.Add (new WeakReference (keys [rand.Next (K)]));
223                 Array.Clear (keys, 0, keys.Length);
224
225                 return res;
226         }
227
228         [Test]
229         public void InsertStress () {
230                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
231                         return;
232                 var cwt = new ConditionalWeakTable <object,object> ();
233
234                 var a = new object ();
235                 var b = new object ();
236
237                 cwt.Add (a, new object ());
238                 cwt.Add (b, new object ());
239
240                 List<WeakReference> res = null;
241                 ThreadStart dele = () => { res = FillWithNetwork (cwt); };
242                 var th = new Thread(dele);
243                 th.Start ();
244                 th.Join ();
245
246                 GC.Collect ();
247                 GC.Collect ();
248
249                 for (int i = 0; i < res.Count; ++i)
250                         Assert.IsFalse (res [i].IsAlive, "#r" + i);
251         }
252
253         static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
254                 if (cwt [0] == null)
255                         cwt[0] = new ConditionalWeakTable <object,object> ();
256                 var res = FillWithNetwork (cwt[0]);
257
258                 return res;
259         }
260
261         static void ForcePromotion () {
262                 var o = new object[64000];
263
264                 for (int i = 0; i < 64000; ++i)
265                         o[i] = new int[10];
266         }
267
268         static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
269         {
270                 var res = new List<object> ();
271                 for (int i = 0; i < 10; ++i) {
272                         res.Add (new object ());
273                         cwt[0].Add (res [i], i);
274                 }
275
276                 return res;
277         }
278
279         [Test]
280         public void OldGenStress () {
281                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
282                         return;
283                 var cwt = new ConditionalWeakTable <object,object>[1];
284                 List<object> k = null;
285                 List<WeakReference> res, res2;
286                 res = res2 = null;
287
288                 ThreadStart dele = () => {
289                         res = FillWithNetwork2 (cwt);
290                         ForcePromotion ();
291                         k = FillReachable (cwt);
292                         res2 = FillWithNetwork2 (cwt);
293                 };
294
295                 var th = new Thread(dele);
296                 th.Start ();
297                 th.Join ();
298
299                 GC.Collect ();
300
301                 for (int i = 0; i < res.Count; ++i)
302                         Assert.IsFalse (res [i].IsAlive, "#r0-" + i);
303                 for (int i = 0; i < res2.Count; ++i)
304                         Assert.IsFalse (res2 [i].IsAlive, "#r1-" + i);
305
306                 for (int i = 0; i < k.Count; ++i) {
307                         object val;
308                         Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
309                         Assert.AreEqual (i, val, "k1-" + i);
310                 }
311         }
312
313
314         static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
315                 var handles = new List<GCHandle> ();
316
317                 var a = (object)10;
318                 var b = (object)20;
319                 var k1 = (object)30;
320                 var k2 = (object)40;
321
322                 handles.Add (GCHandle.Alloc (a, GCHandleType.Pinned));
323                 handles.Add (GCHandle.Alloc (b, GCHandleType.Pinned));
324                 handles.Add (GCHandle.Alloc (k1, GCHandleType.Pinned));
325                 handles.Add (GCHandle.Alloc (k2, GCHandleType.Pinned));
326                 
327                 cwt.Add (a, k1);
328                 cwt.Add (b, k2);
329
330         
331                 return handles;
332         }
333
334         static void MakeObjMovable (List<GCHandle> handles)
335         {
336                 for (int i = 0; i < handles.Count; ++i) {
337                         object o = handles[i].Target;
338                         handles[i].Free ();
339                         handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
340                 }
341         }
342
343         static void ForceMinor () {
344                 for (int i = 0; i < 64000; ++i)
345                         new object ();
346         }
347
348         public void PromotedCwtPointingToYoungStuff () {
349                 var cwt = new ConditionalWeakTable <object,object> ();
350
351                 var handles = FillTable3 (cwt);
352
353                 GC.Collect (0);
354
355                 /*Be 100% sure it will be on the young gen*/
356
357                 /*cwt array now will be on old gen*/
358                 ForceMinor ();
359                 ForceMinor ();
360                 ForceMinor ();
361
362                 //Make them non pinned
363                 MakeObjMovable (handles);
364
365                 GC.Collect (0);
366                 
367                 //Force a minor GC - this should cause
368                 ForceMinor ();
369                 ForceMinor ();
370                 ForceMinor ();
371                 ForceMinor ();
372
373                 GC.Collect (0);
374
375                 object r1, r2;
376                 Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
377                 Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
378
379                 GC.Collect ();
380                 cwt.GetHashCode ();
381         }
382
383         static object _lock1 = new object ();
384         static object _lock2 = new object ();
385         static int reachable = 0;
386  
387         public class FinalizableLink {
388                 object obj;
389                 ConditionalWeakTable <object,object> cwt;
390                 int id;
391
392                 public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
393                         this.id = id;
394                         this.obj = obj;
395                         this.cwt = cwt;
396                 }
397
398                 ~FinalizableLink () {
399                         lock (_lock1) { ; }
400                         object obj;
401                         bool res = cwt.TryGetValue (this, out obj);
402                         if (res)
403                                 ++reachable;
404                         if (reachable == 20)
405                                 lock (_lock2) { Monitor.Pulse (_lock2); }
406                 }
407         }
408
409         static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
410         {
411                 object a = new object ();
412                 object b = new FinalizableLink (0, a, cwt);
413                 cwt.Add (a, "foo");
414                 cwt.Add (b, "bar");
415
416                 for (int i = 1; i < 20; ++i) {
417                         b = new FinalizableLink (i, b, cwt);
418                         cwt.Add (b, i);
419                 }
420         }
421
422         [Test]
423         public void FinalizableObjectsThatRetainDeadKeys ()
424         {
425                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
426                         return;
427                 lock (_lock1) { 
428                         var cwt = new ConditionalWeakTable <object,object> ();
429                         ThreadStart dele = () => { FillWithFinalizable (cwt); };
430                         var th = new Thread(dele);
431                         th.Start ();
432                         th.Join ();
433                         GC.Collect ();
434                         GC.Collect ();
435
436                         Assert.AreEqual (0, reachable, "#1");
437                 }
438
439                 GC.Collect ();
440                 GC.Collect ();
441                 lock (_lock2) { Monitor.Wait (_lock2, 1000); }
442
443                 Assert.AreEqual (20, reachable, "#1");
444         }
445         }
446 }
447
448 #endif