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