2 // CancellationTokenSourceTest.cs
5 // Marek Safar (marek.safar@gmail.com)
6 // Jeremie Laval (jeremie.laval@gmail.com)
8 // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
32 using System.Threading;
33 using NUnit.Framework;
34 using System.Threading.Tasks;
36 namespace MonoTests.System.Threading
39 public class CancellationTokenSourceTest
43 public void Ctor_Invalid ()
46 new CancellationTokenSource (-4);
48 } catch (ArgumentException) {
53 public void Ctor_Timeout ()
56 var cts = new CancellationTokenSource (TimeSpan.FromMilliseconds (20));
57 var mre = new ManualResetEvent (false);
58 cts.Token.Register (() => { called++; mre.Set (); });
60 Assert.IsTrue (mre.WaitOne (1000), "Not called in 1000ms");
61 Assert.AreEqual (1, called, "#1");
65 public void CancelAfter ()
68 var cts = new CancellationTokenSource ();
69 var mre = new ManualResetEvent(false);
70 cts.Token.Register (() => { called++; mre.Set (); });
73 Assert.IsTrue(mre.WaitOne (1000), "Should be cancelled in ~20ms");
74 Assert.AreEqual (1, called, "#1");
78 public void CancelAfter_Invalid ()
80 var cts = new CancellationTokenSource ();
84 } catch (ArgumentException) {
89 public void CancelAfter_Disposed ()
92 var cts = new CancellationTokenSource ();
93 var mre = new ManualResetEvent (false);
94 cts.Token.Register (() => { called++; mre.Set (); });
98 Assert.IsFalse (mre.WaitOne (100), "Shouldn't have been called");
99 Assert.AreEqual (0, called, "#1");
106 CancellationTokenSource cts = new CancellationTokenSource ();
107 Assert.IsTrue (cts.Token.CanBeCanceled, "#1");
108 Assert.IsFalse (cts.Token.IsCancellationRequested, "#2");
109 Assert.IsNotNull (cts.Token.WaitHandle, "#3");
113 public void Cancel_NoRegistration ()
115 CancellationTokenSource cts = new CancellationTokenSource ();
120 public void Cancel ()
122 var cts = new CancellationTokenSource ();
125 cts.Token.Register (l => { Assert.AreEqual ("v", l); ++called; }, "v");
127 Assert.AreEqual (1, called, "#1");
130 cts.Token.Register (() => { called += 12; });
132 Assert.AreEqual (12, called, "#2");
137 public void Cancel_Order ()
139 var cts = new CancellationTokenSource ();
141 Action<object> a = x => { Assert.AreEqual(current, x); current++; };
143 cts.Token.Register (a, 2);
144 cts.Token.Register (a, 1);
145 cts.Token.Register (a, 0);
151 public void CancelWithDispose ()
153 CancellationTokenSource cts = new CancellationTokenSource ();
154 CancellationToken c = cts.Token;
165 Assert.AreEqual (1, called, "#1");
169 public void Cancel_SingleException ()
171 var cts = new CancellationTokenSource ();
173 cts.Token.Register (() => { throw new ApplicationException (); });
177 } catch (AggregateException e) {
178 Assert.AreEqual (1, e.InnerExceptions.Count, "#2");
185 public void Cancel_MultipleExceptions ()
187 var cts = new CancellationTokenSource ();
189 cts.Token.Register (() => { throw new ApplicationException ("1"); });
190 cts.Token.Register (() => { throw new ApplicationException ("2"); });
191 cts.Token.Register (() => { throw new ApplicationException ("3"); });
196 } catch (AggregateException e) {
197 Assert.AreEqual (3, e.InnerExceptions.Count, "#2");
203 cts.Token.Register (() => { throw new ApplicationException ("1"); });
205 } catch (ApplicationException) {
212 public void Cancel_ExceptionOrder ()
214 var cts = new CancellationTokenSource ();
216 cts.Token.Register (() => { throw new ApplicationException ("1"); });
217 cts.Token.Register (() => { throw new ApplicationException ("2"); });
218 cts.Token.Register (() => { throw new ApplicationException ("3"); });
222 } catch (AggregateException e) {
223 Assert.AreEqual (3, e.InnerExceptions.Count, "#2");
224 Assert.AreEqual ("3", e.InnerExceptions[0].Message, "#3");
225 Assert.AreEqual ("2", e.InnerExceptions[1].Message, "#4");
226 Assert.AreEqual ("1", e.InnerExceptions[2].Message, "#5");
231 public void Cancel_MultipleException_Recursive ()
233 CancellationTokenSource cts = new CancellationTokenSource ();
234 CancellationToken c = cts.Token;
240 throw new ApplicationException ();
244 throw new NotSupportedException ();
250 } catch (AggregateException e) {
251 Assert.AreEqual (2, e.InnerExceptions.Count, "#2");
256 public void Cancel_MultipleExceptionsFirstThrows ()
258 var cts = new CancellationTokenSource ();
260 cts.Token.Register (() => { throw new ApplicationException ("1"); });
261 cts.Token.Register (() => { throw new ApplicationException ("2"); });
262 cts.Token.Register (() => { throw new ApplicationException ("3"); });
267 } catch (ApplicationException) {
274 public void CreateLinkedTokenSource_InvalidArguments ()
276 var cts = new CancellationTokenSource ();
277 var token = cts.Token;
280 CancellationTokenSource.CreateLinkedTokenSource (null);
282 } catch (ArgumentNullException) {
286 CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken[0]);
288 } catch (ArgumentException) {
293 public void CreateLinkedTokenSource ()
295 var cts = new CancellationTokenSource ();
298 var linked = CancellationTokenSource.CreateLinkedTokenSource (cts.Token);
299 Assert.IsTrue (linked.IsCancellationRequested, "#1");
301 linked = CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken ());
302 Assert.IsFalse (linked.IsCancellationRequested, "#2");
306 public void Dispose ()
308 var cts = new CancellationTokenSource ();
309 var token = cts.Token;
313 var b = cts.IsCancellationRequested;
314 token.ThrowIfCancellationRequested ();
319 } catch (ObjectDisposedException) {
325 } catch (ObjectDisposedException) {
328 bool throwOnDispose = false;
329 AppContext.TryGetSwitch ("Switch.System.Threading.ThrowExceptionIfDisposedCancellationTokenSource", out throwOnDispose);
330 if (throwOnDispose) {
332 token.Register (() => { });
334 } catch (ObjectDisposedException) {
339 var wh = token.WaitHandle;
341 } catch (ObjectDisposedException) {
344 if (throwOnDispose) {
346 CancellationTokenSource.CreateLinkedTokenSource (token);
348 } catch (ObjectDisposedException) {
355 } catch (ObjectDisposedException) {
360 public void RegisterThenDispose ()
362 var cts1 = new CancellationTokenSource ();
363 var reg1 = cts1.Token.Register (() => { throw new ApplicationException (); });
365 var cts2 = new CancellationTokenSource ();
366 var reg2 = cts2.Token.Register (() => { throw new ApplicationException (); });
368 Assert.AreNotEqual (cts1, cts2, "#1");
369 Assert.AreNotSame (cts1, cts2, "#2");
377 } catch (AggregateException) {
382 public void RegisterWhileCancelling ()
384 var cts = new CancellationTokenSource ();
385 var mre = new ManualResetEvent (false);
386 var mre2 = new ManualResetEvent (false);
389 cts.Token.Register (() => {
390 Assert.IsTrue (cts.IsCancellationRequested, "#10");
391 Assert.IsTrue (cts.Token.WaitHandle.WaitOne (0), "#11");
397 var t = Task.Factory.StartNew (() => { cts.Cancel (); });
399 Assert.IsTrue (mre2.WaitOne (1000), "#0");
400 cts.Token.Register (() => { called++; });
401 Assert.AreEqual (1, called, "#1");
402 Assert.IsFalse (t.IsCompleted, "#2");
405 Assert.IsTrue (t.Wait (1000), "#3");
406 Assert.AreEqual (12, called, "#4");
410 public void ReEntrantRegistrationTest ()
412 bool unregister = false;
413 bool register = false;
414 var source = new CancellationTokenSource ();
415 var token = source.Token;
417 Console.WriteLine ("Test1");
418 var reg = token.Register (() => unregister = true);
419 token.Register (() => reg.Dispose ());
420 token.Register (() => { Console.WriteLine ("Gnyah"); token.Register (() => register = true); });
423 Assert.IsFalse (unregister);
424 Assert.IsTrue (register);
428 public void DisposeAfterRegistrationTest ()
430 var source = new CancellationTokenSource ();
432 var req = source.Token.Register (() => ran = true);
435 Assert.IsFalse (ran);
439 public void CancelLinkedTokenSource ()
441 var cts = new CancellationTokenSource ();
442 bool canceled = false;
443 cts.Token.Register (() => canceled = true);
445 using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cts.Token))
448 Assert.IsFalse (canceled, "#1");
449 Assert.IsFalse (cts.IsCancellationRequested, "#2");
453 Assert.IsTrue (canceled, "#3");
456 [Category ("NotWorking")] // why would linked token be imune to ObjectDisposedException on Cancel?
458 public void ConcurrentCancelLinkedTokenSourceWhileDisposing ()
460 for (int i = 0, total = 500; i < total; ++i) {
461 var src = new CancellationTokenSource ();
462 var linked = CancellationTokenSource.CreateLinkedTokenSource (src.Token);
463 var cntd = new CountdownEvent (2);
465 var t1 = new Thread (() => {
470 var t2 = new Thread (() => {
484 public void DisposeRace ()
486 for (int i = 0, total = 1000; i < total; ++i) {
487 var c1 = new CancellationTokenSource ();
488 var wh = c1.Token.WaitHandle;