Merge pull request #1949 from lewurm/fixtype
[mono.git] / mcs / class / corlib / Test / System.Threading / CancellationTokenSourceTest.cs
1 //
2 // CancellationTokenSourceTest.cs
3 //
4 // Authors:
5 //       Marek Safar (marek.safar@gmail.com)
6 //       Jeremie Laval (jeremie.laval@gmail.com)
7 //
8 // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30
31 using System;
32 using System.Threading;
33 using NUnit.Framework;
34 using System.Threading.Tasks;
35
36 namespace MonoTests.System.Threading
37 {
38         [TestFixture]
39         public class CancellationTokenSourceTest
40         {
41 #if NET_4_5
42
43                 [Test]
44                 public void Ctor_Invalid ()
45                 {
46                         try {
47                                 new CancellationTokenSource (-4);
48                                 Assert.Fail ("#1");
49                         } catch (ArgumentException) {
50                         }
51                 }
52
53                 [Test]
54                 public void Ctor_Timeout ()
55                 {
56                         int called = 0;
57                         var cts = new CancellationTokenSource (TimeSpan.FromMilliseconds (20));
58                         cts.Token.Register (() => called++);
59                         Thread.Sleep (50);
60                         Assert.AreEqual (1, called, "#1");
61                 }
62
63                 [Test]
64                 public void CancelAfter ()
65                 {
66                         int called = 0;
67                         var cts = new CancellationTokenSource ();
68                         cts.Token.Register (() => called++);
69                         cts.CancelAfter (20);
70                         Thread.Sleep (50);
71                         Assert.AreEqual (1, called, "#1");
72                 }
73
74                 [Test]
75                 public void CancelAfter_Invalid ()
76                 {
77                         var cts = new CancellationTokenSource ();
78                         try {
79                                 cts.CancelAfter (-9);
80                                 Assert.Fail ("#1");
81                         } catch (ArgumentException) {
82                         }
83                 }
84
85                 [Test]
86                 public void CancelAfter_Disposed ()
87                 {
88                         int called = 0;
89                         var cts = new CancellationTokenSource ();
90                         cts.Token.Register (() => called++);
91                         cts.CancelAfter (50);
92                         cts.Dispose ();
93                         Thread.Sleep (100);
94                         Assert.AreEqual (0, called, "#1");
95                 }
96
97 #endif
98
99                 [Test]
100                 public void Token ()
101                 {
102                         CancellationTokenSource cts = new CancellationTokenSource ();
103                         Assert.IsTrue (cts.Token.CanBeCanceled, "#1");
104                         Assert.IsFalse (cts.Token.IsCancellationRequested, "#2");
105                         Assert.IsNotNull (cts.Token.WaitHandle, "#3");
106                 }
107
108                 [Test]
109                 public void Cancel_NoRegistration ()
110                 {
111                         CancellationTokenSource cts = new CancellationTokenSource ();
112                         cts.Cancel ();
113                 }
114
115                 [Test]
116                 public void Cancel ()
117                 {
118                         var cts = new CancellationTokenSource ();
119
120                         int called = 0;
121                         cts.Token.Register (l => { Assert.AreEqual ("v", l); ++called; }, "v");
122                         cts.Cancel ();
123                         Assert.AreEqual (1, called, "#1");
124
125                         called = 0;
126                         cts.Token.Register (() => { called += 12; });
127                         cts.Cancel ();
128                         Assert.AreEqual (12, called, "#2");
129                 }
130
131
132                 [Test]
133                 public void Cancel_Order ()
134                 {
135                         var cts = new CancellationTokenSource ();
136                         var current = 0;
137                         Action<object> a = x => { Assert.AreEqual(current, x); current++; };
138
139                         cts.Token.Register (a, 2);
140                         cts.Token.Register (a, 1);
141                         cts.Token.Register (a, 0);
142                         cts.Cancel ();
143                 }
144
145
146                 [Test]
147                 public void CancelWithDispose ()
148                 {
149                         CancellationTokenSource cts = new CancellationTokenSource ();
150                         CancellationToken c = cts.Token;
151                         c.Register (() => {
152                                 cts.Dispose ();
153                         });
154
155                         int called = 0;
156                         c.Register (() => {
157                                 called++;
158                         });
159
160                         cts.Cancel ();
161                         Assert.AreEqual (1, called, "#1");
162                 }
163
164                 [Test]
165                 public void Cancel_SingleException ()
166                 {
167                         var cts = new CancellationTokenSource ();
168
169                         cts.Token.Register (() => { throw new ApplicationException (); });
170                         try {
171                                 cts.Cancel ();
172                                 Assert.Fail ("#1");
173                         } catch (AggregateException e) {
174                                 Assert.AreEqual (1, e.InnerExceptions.Count, "#2");
175                         }
176
177                         cts.Cancel ();
178                 }
179
180                 [Test]
181                 public void Cancel_MultipleExceptions ()
182                 {
183                         var cts = new CancellationTokenSource ();
184
185                         cts.Token.Register (() => { throw new ApplicationException ("1"); });
186                         cts.Token.Register (() => { throw new ApplicationException ("2"); });
187                         cts.Token.Register (() => { throw new ApplicationException ("3"); });
188
189                         try {
190                                 cts.Cancel ();
191                                 Assert.Fail ("#1");
192                         } catch (AggregateException e) {
193                                 Assert.AreEqual (3, e.InnerExceptions.Count, "#2");
194                         }
195
196                         cts.Cancel ();
197
198                         try {
199                                 cts.Token.Register (() => { throw new ApplicationException ("1"); });
200                                 Assert.Fail ("#11");
201                         } catch (ApplicationException) {
202                         }
203
204                         cts.Cancel ();
205                 }
206
207                 [Test]
208                 public void Cancel_ExceptionOrder ()
209                 {
210                         var cts = new CancellationTokenSource ();
211
212                         cts.Token.Register (() => { throw new ApplicationException ("1"); });
213                         cts.Token.Register (() => { throw new ApplicationException ("2"); });
214                         cts.Token.Register (() => { throw new ApplicationException ("3"); });
215
216                         try {
217                                 cts.Cancel ();
218                         } catch (AggregateException e) {
219                                 Assert.AreEqual (3, e.InnerExceptions.Count, "#2");
220                                 Assert.AreEqual ("3", e.InnerExceptions[0].Message, "#3");
221                                 Assert.AreEqual ("2", e.InnerExceptions[1].Message, "#4");
222                                 Assert.AreEqual ("1", e.InnerExceptions[2].Message, "#5");
223                         }
224                 }
225
226                 [Test]
227                 public void Cancel_MultipleException_Recursive ()
228                 {
229                         CancellationTokenSource cts = new CancellationTokenSource ();
230                         CancellationToken c = cts.Token;
231                         c.Register (() => {
232                                 cts.Cancel ();
233                         });
234
235                         c.Register (() => {
236                                 throw new ApplicationException ();
237                         });
238
239                         c.Register (() => {
240                                 throw new NotSupportedException ();
241                         });
242
243                         try {
244                                 cts.Cancel (false);
245                                 Assert.Fail ("#1");
246                         } catch (AggregateException e) {
247                                 Assert.AreEqual (2, e.InnerExceptions.Count, "#2");
248                         }
249                 }
250
251                 [Test]
252                 public void Cancel_MultipleExceptionsFirstThrows ()
253                 {
254                         var cts = new CancellationTokenSource ();
255
256                         cts.Token.Register (() => { throw new ApplicationException ("1"); });
257                         cts.Token.Register (() => { throw new ApplicationException ("2"); });
258                         cts.Token.Register (() => { throw new ApplicationException ("3"); });
259
260                         try {
261                                 cts.Cancel (true);
262                                 Assert.Fail ("#1");
263                         } catch (ApplicationException) {
264                         }
265
266                         cts.Cancel ();
267                 }
268
269                 [Test]
270                 public void CreateLinkedTokenSource_InvalidArguments ()
271                 {
272                         var cts = new CancellationTokenSource ();
273                         var token = cts.Token;
274
275                         try {
276                                 CancellationTokenSource.CreateLinkedTokenSource (null);
277                                 Assert.Fail ("#1");
278                         } catch (ArgumentNullException) {
279                         }
280
281                         try {
282                                 CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken[0]);
283                                 Assert.Fail ("#2");
284                         } catch (ArgumentException) {
285                         }
286                 }
287
288                 [Test]
289                 public void CreateLinkedTokenSource ()
290                 {
291                         var cts = new CancellationTokenSource ();
292                         cts.Cancel ();
293
294                         var linked = CancellationTokenSource.CreateLinkedTokenSource (cts.Token);
295                         Assert.IsTrue (linked.IsCancellationRequested, "#1");
296
297                         linked = CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken ());
298                         Assert.IsFalse (linked.IsCancellationRequested, "#2");
299                 }
300
301                 [Test]
302                 public void Dispose ()
303                 {
304                         var cts = new CancellationTokenSource ();
305                         var token = cts.Token;
306
307                         cts.Dispose ();
308                         cts.Dispose ();
309                         var b = cts.IsCancellationRequested;
310                         token.ThrowIfCancellationRequested ();
311
312                         try {
313                                 cts.Cancel ();
314                                 Assert.Fail ("#1");
315                         } catch (ObjectDisposedException) {
316                         }
317
318                         try {
319                                 var t = cts.Token;
320                                 Assert.Fail ("#2");
321                         } catch (ObjectDisposedException) {
322                         }
323
324                         try {
325                                 token.Register (() => { });
326                                 Assert.Fail ("#3");
327                         } catch (ObjectDisposedException) {
328                         }
329
330                         try {
331                                 var wh = token.WaitHandle;
332                                 Assert.Fail ("#4");
333                         } catch (ObjectDisposedException) {
334                         }
335
336                         try {
337                                 CancellationTokenSource.CreateLinkedTokenSource (token);
338                                 Assert.Fail ("#5");
339                         } catch (ObjectDisposedException) {
340                         }
341
342 #if NET_4_5
343                         try {
344                                 cts.CancelAfter (1);
345                                 Assert.Fail ("#6");
346                         } catch (ObjectDisposedException) {
347                         }
348 #endif
349                 }
350
351                 [Test]
352                 public void RegisterThenDispose ()
353                 {
354                         var cts1 = new CancellationTokenSource ();
355                         var reg1 = cts1.Token.Register (() => { throw new ApplicationException (); });
356
357                         var cts2 = new CancellationTokenSource ();
358                         var reg2 = cts2.Token.Register (() => { throw new ApplicationException (); });
359
360                         Assert.AreNotEqual (cts1, cts2, "#1");
361                         Assert.AreNotSame (cts1, cts2, "#2");
362
363                         reg1.Dispose ();
364                         cts1.Cancel ();
365
366                         try {
367                                 cts2.Cancel ();
368                                 Assert.Fail ("#3");
369                         } catch (AggregateException) {
370                         }
371                 }
372
373                 [Test]
374                 public void RegisterWhileCancelling ()
375                 {
376                         var cts = new CancellationTokenSource ();
377                         var mre = new ManualResetEvent (false);
378                         var mre2 = new ManualResetEvent (false);
379                         int called = 0;
380
381                         cts.Token.Register (() => {
382                                 Assert.IsTrue (cts.IsCancellationRequested, "#10");
383                                 Assert.IsTrue (cts.Token.WaitHandle.WaitOne (0), "#11");
384                                 mre2.Set ();
385                                 mre.WaitOne (3000);
386                                 called += 11;
387                         });
388
389                         var t = Task.Factory.StartNew (() => { cts.Cancel (); });
390
391                         Assert.IsTrue (mre2.WaitOne (1000), "#0");
392                         cts.Token.Register (() => { called++; });
393                         Assert.AreEqual (1, called, "#1");
394                         Assert.IsFalse (t.IsCompleted, "#2");
395
396                         mre.Set ();
397                         Assert.IsTrue (t.Wait (1000), "#3");
398                         Assert.AreEqual (12, called, "#4");
399                 }
400
401                 [Test]
402                 public void ReEntrantRegistrationTest ()
403                 {
404                         bool unregister = false;
405                         bool register = false;
406                         var source = new CancellationTokenSource ();
407                         var token = source.Token;
408
409                         Console.WriteLine ("Test1");
410                         var reg = token.Register (() => unregister = true);
411                         token.Register (() => reg.Dispose ());
412                         token.Register (() => { Console.WriteLine ("Gnyah"); token.Register (() => register = true); });
413                         source.Cancel ();
414
415                         Assert.IsFalse (unregister);
416                         Assert.IsTrue (register);
417                 }
418
419                 [Test]
420                 public void DisposeAfterRegistrationTest ()
421                 {
422                         var source = new CancellationTokenSource ();
423                         bool ran = false;
424                         var req = source.Token.Register (() => ran = true);
425                         source.Dispose ();
426                         req.Dispose ();
427                         Assert.IsFalse (ran);
428                 }
429
430                 [Test]
431                 public void CancelLinkedTokenSource ()
432                 {
433                         var cts = new CancellationTokenSource ();
434                         bool canceled = false;
435                         cts.Token.Register (() => canceled = true);
436
437                         using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cts.Token))
438                                 ;
439
440                         Assert.IsFalse (canceled, "#1");
441                         Assert.IsFalse (cts.IsCancellationRequested, "#2");
442
443                         cts.Cancel ();
444
445                         Assert.IsTrue (canceled, "#3");
446                 }
447
448                 [Category ("NotWorking")] // why would linked token be imune to ObjectDisposedException on Cancel?
449                 [Test]
450                 public void ConcurrentCancelLinkedTokenSourceWhileDisposing ()
451                 {
452                         for (int i = 0, total = 500; i < total; ++i) {
453                                 var src = new CancellationTokenSource ();
454                                 var linked = CancellationTokenSource.CreateLinkedTokenSource (src.Token);
455                                 var cntd = new CountdownEvent (2);
456
457                                 var t1 = new Thread (() => {
458                                         if (!cntd.Signal ())
459                                                 cntd.Wait (200);
460                                         src.Cancel ();
461                                 });
462                                 var t2 = new Thread (() => {
463                                         if (!cntd.Signal ())
464                                                 cntd.Wait (200);
465                                         linked.Dispose ();
466                                 });
467
468                                 t1.Start ();
469                                 t2.Start ();
470                                 t1.Join (500);
471                                 t2.Join (500);
472                         }
473                 }
474
475 #if NET_4_5
476                 [Test]
477                 public void DisposeRace ()
478                 {
479                         for (int i = 0, total = 1000; i < total; ++i) {
480                                 var c1 = new CancellationTokenSource ();
481                                 var wh = c1.Token.WaitHandle;
482                                 c1.CancelAfter (1);
483                                 Thread.Sleep (1);
484                                 c1.Dispose ();
485                         }
486                 }
487 #endif
488         }
489 }
490
491