System.Drawing: added email to icon and test file headers
[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         class Key { public int Foo; }
56         class Val { public int Foo; }
57
58         [Test]
59         public void GetValue () {
60                 var cwt = new ConditionalWeakTable <object,object> ();
61
62                 try {
63                         cwt.GetValue (null, k => null);
64                         Assert.Fail ("#0");
65                 } catch (ArgumentNullException) {}
66
67                 try {
68                         cwt.GetValue (20, null);
69                         Assert.Fail ("#1");
70                 } catch (ArgumentNullException) {}
71
72
73                 object key = "foo";
74                 object val = cwt.GetValue (key, k => new Link (k));
75                 Assert.IsTrue (val != null, "#2");
76                 Assert.AreEqual (typeof (Link), val.GetType () , "#3");
77
78                 Assert.AreEqual (val, cwt.GetValue (key, k => new object ()), "#4");
79         }
80
81         [Test]
82         public void GetOrCreateValue () {
83                 var cwt = new ConditionalWeakTable <object,object> ();
84
85                 try {
86                         cwt.GetOrCreateValue (null);
87                         Assert.Fail ("#0");
88                 } catch (ArgumentNullException) {}
89
90
91                 object key = "foo";
92                 object val = cwt.GetOrCreateValue (key);
93                 Assert.IsTrue (val != null, "#2");
94                 Assert.AreEqual (typeof (object), val.GetType () , "#3");
95
96                 Assert.AreEqual (val, cwt.GetOrCreateValue (key), "#4");
97
98                 var cwt2 = new ConditionalWeakTable <object, string> ();
99                 try {
100                         cwt2.GetOrCreateValue (key);
101                         Assert.Fail ("#5");
102                 } catch (MissingMethodException) {}
103         }
104
105         [Test]
106         public void Remove () {
107                 var cwt = new ConditionalWeakTable <object,object> ();
108                 object c = new Key ();
109
110                 cwt.Add (c, "x");
111
112                 try {
113                         cwt.Remove (null);
114                         Assert.Fail ("#0");
115                 } catch (ArgumentNullException) {}
116
117
118                 Assert.IsFalse (cwt.Remove ("x"), "#1");
119                 Assert.IsTrue (cwt.Remove (c), "#2");
120                 Assert.IsFalse (cwt.Remove (c), "#3");
121         }
122
123         [Test]
124         public void Add () {
125                 var cwt = new ConditionalWeakTable <object,object> ();
126                 object c = new Key ();
127
128                 cwt.Add (c, new Link (c));
129
130                 try {
131                         cwt.Add (c, new Link (c));
132                         Assert.Fail ("#0");
133                 } catch (ArgumentException) {}
134
135                 cwt.Add ("zzz", null);//ok
136
137                 try {
138                         cwt.Add (null, new Link (c));
139                         Assert.Fail ("#1");
140                 } catch (ArgumentNullException) {}
141         }
142
143         [Test]
144         public void TryGetValue () {
145                 var cwt = new ConditionalWeakTable <object,object> ();
146                 object res;
147                 object c = new Key ();
148
149                 cwt.Add (c, "foo");
150
151                 try {
152                         cwt.TryGetValue (null, out res);
153                         Assert.Fail ("#0");
154                 } catch (ArgumentNullException) {}
155
156                 Assert.IsFalse (cwt.TryGetValue ("foo", out res), "#1");
157                 Assert.IsTrue (cwt.TryGetValue (c, out res), "#2");
158                 Assert.AreEqual ("foo", res, "#3");
159         }
160
161
162         static void FillStuff (ConditionalWeakTable <object,object> cwt, 
163                         out List<object> keepAlive, out List<WeakReference> keys) {
164
165                 keepAlive = new List<object> ();
166                 keys = new List<WeakReference> ();
167
168                 object a = new Key ();
169                 object b = new Key ();
170                 object c = new Key ();
171                 
172                 cwt.Add (a, new string ("str0".ToCharArray ()));
173                 cwt.Add (b, "str1");
174                 cwt.Add (c, new Link (a));
175
176                 keepAlive.Add (c);
177                 keys.Add (new WeakReference(a));
178                 keys.Add (new WeakReference(b));
179                 keys.Add (new WeakReference(c));
180         }
181
182         [Test]
183         public void Reachability () {
184                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
185                         return;
186                 var cwt = new ConditionalWeakTable <object,object> ();
187                 List<object> keepAlive;
188                 List<WeakReference> keys;
189                 FillStuff (cwt, out keepAlive, out keys);
190
191                 GC.Collect ();
192
193                 Assert.IsTrue (keys [0].IsAlive, "r0");
194                 Assert.IsFalse (keys [1].IsAlive, "r1");
195                 Assert.IsTrue (keys [2].IsAlive, "r2");
196
197                 object res;
198                 Assert.IsTrue (cwt.TryGetValue (keepAlive [0], out res), "ka0");
199                 Assert.IsTrue (res is Link, "ka1");
200
201                 Link link = res as Link;
202                 Assert.IsTrue (cwt.TryGetValue (link.obj, out res), "ka2");
203                 Assert.AreEqual ("str0", res, "ka3");
204         }
205
206
207         static List<WeakReference> FillWithNetwork (ConditionalWeakTable <object,object> cwt)
208         {
209                 const int K = 500;
210                 object[] keys = new object [K];
211                 for (int i = 0; i < K; ++i)
212                         keys[i] = new object ();
213
214                 Random rand = new Random ();
215
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)]));
219
220                 var res = new List<WeakReference> ();
221
222                 for (int i = 0; i < 10; ++i)
223                         res.Add (new WeakReference (keys [rand.Next (K)]));
224                 Array.Clear (keys, 0, keys.Length);
225
226                 return res;
227         }
228
229         [Test]
230         public void InsertStress () {
231                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
232                         return;
233                 var cwt = new ConditionalWeakTable <object,object> ();
234
235                 var a = new object ();
236                 var b = new object ();
237
238                 cwt.Add (a, new object ());
239                 cwt.Add (b, new object ());
240
241                 List<WeakReference> res = null;
242                 ThreadStart dele = () => { res = FillWithNetwork (cwt); };
243                 var th = new Thread(dele);
244                 th.Start ();
245                 th.Join ();
246
247                 GC.Collect ();
248                 GC.Collect ();
249
250                 for (int i = 0; i < res.Count; ++i)
251                         Assert.IsFalse (res [i].IsAlive, "#r" + i);
252         }
253
254         static List<WeakReference> FillWithNetwork2 (ConditionalWeakTable <object,object>[] cwt) {
255                 if (cwt [0] == null)
256                         cwt[0] = new ConditionalWeakTable <object,object> ();
257                 var res = FillWithNetwork (cwt[0]);
258
259                 return res;
260         }
261
262         static void ForcePromotion () {
263                 var o = new object[64000];
264
265                 for (int i = 0; i < 64000; ++i)
266                         o[i] = new int[10];
267         }
268
269         static List<object> FillReachable (ConditionalWeakTable <object,object>[] cwt)
270         {
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);
275                 }
276
277                 return res;
278         }
279
280         [Test]
281         public void OldGenStress () {
282                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
283                         return;
284                 var cwt = new ConditionalWeakTable <object,object>[1];
285                 List<object> k = null;
286                 List<WeakReference> res, res2;
287                 res = res2 = null;
288
289                 ThreadStart dele = () => {
290                         res = FillWithNetwork2 (cwt);
291                         ForcePromotion ();
292                         k = FillReachable (cwt);
293                         res2 = FillWithNetwork2 (cwt);
294                 };
295
296                 var th = new Thread(dele);
297                 th.Start ();
298                 th.Join ();
299
300                 GC.Collect ();
301
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);
306
307                 for (int i = 0; i < k.Count; ++i) {
308                         object val;
309                         Assert.IsTrue (cwt[0].TryGetValue (k [i], out val), "k0-" + i);
310                         Assert.AreEqual (i, val, "k1-" + i);
311                 }
312         }
313
314
315         static List<GCHandle> FillTable3 (ConditionalWeakTable <object,object> cwt) {
316                 var handles = new List<GCHandle> ();
317
318                 var a = (object)10;
319                 var b = (object)20;
320                 var k1 = (object)30;
321                 var k2 = (object)40;
322
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));
327                 
328                 cwt.Add (a, k1);
329                 cwt.Add (b, k2);
330
331         
332                 return handles;
333         }
334
335         static void MakeObjMovable (List<GCHandle> handles)
336         {
337                 for (int i = 0; i < handles.Count; ++i) {
338                         object o = handles[i].Target;
339                         handles[i].Free ();
340                         handles[i] = GCHandle.Alloc (o, GCHandleType.Normal);
341                 }
342         }
343
344         static void ForceMinor () {
345                 for (int i = 0; i < 64000; ++i)
346                         new object ();
347         }
348
349         public void PromotedCwtPointingToYoungStuff () {
350                 var cwt = new ConditionalWeakTable <object,object> ();
351
352                 var handles = FillTable3 (cwt);
353
354                 GC.Collect (0);
355
356                 /*Be 100% sure it will be on the young gen*/
357
358                 /*cwt array now will be on old gen*/
359                 ForceMinor ();
360                 ForceMinor ();
361                 ForceMinor ();
362
363                 //Make them non pinned
364                 MakeObjMovable (handles);
365
366                 GC.Collect (0);
367                 
368                 //Force a minor GC - this should cause
369                 ForceMinor ();
370                 ForceMinor ();
371                 ForceMinor ();
372                 ForceMinor ();
373
374                 GC.Collect (0);
375
376                 object r1, r2;
377                 Assert.IsTrue (cwt.TryGetValue (handles[0].Target, out r1), "#1");
378                 Assert.IsTrue (cwt.TryGetValue (handles[1].Target, out r2), "#2");
379
380                 GC.Collect ();
381                 cwt.GetHashCode ();
382         }
383
384         static object _lock1 = new object ();
385         static object _lock2 = new object ();
386         static int reachable = 0;
387  
388         public class FinalizableLink {
389                 object obj;
390                 ConditionalWeakTable <object,object> cwt;
391                 int id;
392
393                 public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
394                         this.id = id;
395                         this.obj = obj;
396                         this.cwt = cwt;
397                 }
398
399                 ~FinalizableLink () {
400                         lock (_lock1) { ; }
401                         object obj;
402                         bool res = cwt.TryGetValue (this, out obj);
403                         if (res)
404                                 ++reachable;
405                         if (reachable == 20)
406                                 lock (_lock2) { Monitor.Pulse (_lock2); }
407                 }
408         }
409
410         static void FillWithFinalizable (ConditionalWeakTable <object,object> cwt)
411         {
412                 object a = new object ();
413                 object b = new FinalizableLink (0, a, cwt);
414                 cwt.Add (a, "foo");
415                 cwt.Add (b, "bar");
416
417                 for (int i = 1; i < 20; ++i) {
418                         b = new FinalizableLink (i, b, cwt);
419                         cwt.Add (b, i);
420                 }
421         }
422
423         [Test]
424         public void FinalizableObjectsThatRetainDeadKeys ()
425         {
426                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
427                         return;
428                 lock (_lock1) { 
429                         var cwt = new ConditionalWeakTable <object,object> ();
430                         ThreadStart dele = () => { FillWithFinalizable (cwt); };
431                         var th = new Thread(dele);
432                         th.Start ();
433                         th.Join ();
434                         GC.Collect ();
435                         GC.Collect ();
436
437                         Assert.AreEqual (0, reachable, "#1");
438                 }
439
440                 GC.Collect ();
441                 GC.Collect ();
442                 lock (_lock2) { Monitor.Wait (_lock2, 1000); }
443
444                 Assert.AreEqual (20, reachable, "#1");
445         }
446
447         [Test]
448         public void OldGenKeysMakeNewGenObjectsReachable ()
449         {
450                 if (GC.MaxGeneration == 0) /*Boehm doesn't handle ephemerons */
451                 return;
452                 ConditionalWeakTable<object, Val> table = new ConditionalWeakTable<object, Val>();
453                 List<Key> keys = new List<Key>();
454
455                 //
456                 // This list references all keys for the duration of the program, so none 
457                 // should be collected ever.
458                 //
459                 for (int x = 0; x < 1000; x++) 
460                 keys.Add(new Key() { Foo = x });
461
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 });
466
467                         // Look up all keys to verify that they are still there
468                         Val val;
469                         foreach (var key in keys)
470                                 Assert.IsTrue (table.TryGetValue(key, out val), "#1-" + i);
471
472                         // Remove all keys from the ConditionalWeakTable
473                         foreach (var key in keys)
474                                 Assert.IsTrue (!table.Remove(key), "#2-" + i);
475                 }
476         }
477         }
478 }
479
480 #endif