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