CancellationTokenSourceTest.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. //
  2. // CancellationTokenSourceTest.cs
  3. //
  4. // Authors:
  5. // Marek Safar ([email protected])
  6. // Jeremie Laval ([email protected])
  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. using System;
  30. using System.Threading;
  31. using NUnit.Framework;
  32. using System.Threading.Tasks;
  33. namespace MonoTests.System.Threading
  34. {
  35. [TestFixture]
  36. public class CancellationTokenSourceTest
  37. {
  38. #if NET_4_5
  39. [Test]
  40. public void Ctor_Invalid ()
  41. {
  42. try {
  43. new CancellationTokenSource (-4);
  44. Assert.Fail ("#1");
  45. } catch (ArgumentException) {
  46. }
  47. }
  48. [Test]
  49. public void Ctor_Timeout ()
  50. {
  51. int called = 0;
  52. var cts = new CancellationTokenSource (TimeSpan.FromMilliseconds (20));
  53. var mre = new ManualResetEvent (false);
  54. cts.Token.Register (() => { called++; mre.Set (); });
  55. Assert.IsTrue (mre.WaitOne (1000), "Not called in 1000ms");
  56. Assert.AreEqual (1, called, "#1");
  57. }
  58. [Test]
  59. public void CancelAfter ()
  60. {
  61. int called = 0;
  62. var cts = new CancellationTokenSource ();
  63. var mre = new ManualResetEvent(false);
  64. cts.Token.Register (() => { called++; mre.Set (); });
  65. cts.CancelAfter (20);
  66. Assert.IsTrue(mre.WaitOne (1000), "Should be cancelled in ~20ms");
  67. Assert.AreEqual (1, called, "#1");
  68. }
  69. [Test]
  70. public void CancelAfter_Invalid ()
  71. {
  72. var cts = new CancellationTokenSource ();
  73. try {
  74. cts.CancelAfter (-9);
  75. Assert.Fail ("#1");
  76. } catch (ArgumentException) {
  77. }
  78. }
  79. [Test]
  80. public void CancelAfter_Disposed ()
  81. {
  82. int called = 0;
  83. var cts = new CancellationTokenSource ();
  84. var mre = new ManualResetEvent (false);
  85. cts.Token.Register (() => { called++; mre.Set (); });
  86. cts.CancelAfter (50);
  87. cts.Dispose ();
  88. Assert.IsFalse (mre.WaitOne (100), "Shouldn't have been called");
  89. Assert.AreEqual (0, called, "#1");
  90. }
  91. #endif
  92. [Test]
  93. public void Token ()
  94. {
  95. CancellationTokenSource cts = new CancellationTokenSource ();
  96. Assert.IsTrue (cts.Token.CanBeCanceled, "#1");
  97. Assert.IsFalse (cts.Token.IsCancellationRequested, "#2");
  98. Assert.IsNotNull (cts.Token.WaitHandle, "#3");
  99. }
  100. [Test]
  101. public void Cancel_NoRegistration ()
  102. {
  103. CancellationTokenSource cts = new CancellationTokenSource ();
  104. cts.Cancel ();
  105. }
  106. [Test]
  107. public void Cancel ()
  108. {
  109. var cts = new CancellationTokenSource ();
  110. int called = 0;
  111. cts.Token.Register (l => { Assert.AreEqual ("v", l); ++called; }, "v");
  112. cts.Cancel ();
  113. Assert.AreEqual (1, called, "#1");
  114. called = 0;
  115. cts.Token.Register (() => { called += 12; });
  116. cts.Cancel ();
  117. Assert.AreEqual (12, called, "#2");
  118. }
  119. [Test]
  120. public void Cancel_Order ()
  121. {
  122. var cts = new CancellationTokenSource ();
  123. var current = 0;
  124. Action<object> a = x => { Assert.AreEqual(current, x); current++; };
  125. cts.Token.Register (a, 2);
  126. cts.Token.Register (a, 1);
  127. cts.Token.Register (a, 0);
  128. cts.Cancel ();
  129. }
  130. [Test]
  131. public void CancelWithDispose ()
  132. {
  133. CancellationTokenSource cts = new CancellationTokenSource ();
  134. CancellationToken c = cts.Token;
  135. c.Register (() => {
  136. cts.Dispose ();
  137. });
  138. int called = 0;
  139. c.Register (() => {
  140. called++;
  141. });
  142. cts.Cancel ();
  143. Assert.AreEqual (1, called, "#1");
  144. }
  145. [Test]
  146. public void Cancel_SingleException ()
  147. {
  148. var cts = new CancellationTokenSource ();
  149. cts.Token.Register (() => { throw new ApplicationException (); });
  150. try {
  151. cts.Cancel ();
  152. Assert.Fail ("#1");
  153. } catch (AggregateException e) {
  154. Assert.AreEqual (1, e.InnerExceptions.Count, "#2");
  155. }
  156. cts.Cancel ();
  157. }
  158. [Test]
  159. public void Cancel_MultipleExceptions ()
  160. {
  161. var cts = new CancellationTokenSource ();
  162. cts.Token.Register (() => { throw new ApplicationException ("1"); });
  163. cts.Token.Register (() => { throw new ApplicationException ("2"); });
  164. cts.Token.Register (() => { throw new ApplicationException ("3"); });
  165. try {
  166. cts.Cancel ();
  167. Assert.Fail ("#1");
  168. } catch (AggregateException e) {
  169. Assert.AreEqual (3, e.InnerExceptions.Count, "#2");
  170. }
  171. cts.Cancel ();
  172. try {
  173. cts.Token.Register (() => { throw new ApplicationException ("1"); });
  174. Assert.Fail ("#11");
  175. } catch (ApplicationException) {
  176. }
  177. cts.Cancel ();
  178. }
  179. [Test]
  180. public void Cancel_ExceptionOrder ()
  181. {
  182. var cts = new CancellationTokenSource ();
  183. cts.Token.Register (() => { throw new ApplicationException ("1"); });
  184. cts.Token.Register (() => { throw new ApplicationException ("2"); });
  185. cts.Token.Register (() => { throw new ApplicationException ("3"); });
  186. try {
  187. cts.Cancel ();
  188. } catch (AggregateException e) {
  189. Assert.AreEqual (3, e.InnerExceptions.Count, "#2");
  190. Assert.AreEqual ("3", e.InnerExceptions[0].Message, "#3");
  191. Assert.AreEqual ("2", e.InnerExceptions[1].Message, "#4");
  192. Assert.AreEqual ("1", e.InnerExceptions[2].Message, "#5");
  193. }
  194. }
  195. [Test]
  196. public void Cancel_MultipleException_Recursive ()
  197. {
  198. CancellationTokenSource cts = new CancellationTokenSource ();
  199. CancellationToken c = cts.Token;
  200. c.Register (() => {
  201. cts.Cancel ();
  202. });
  203. c.Register (() => {
  204. throw new ApplicationException ();
  205. });
  206. c.Register (() => {
  207. throw new NotSupportedException ();
  208. });
  209. try {
  210. cts.Cancel (false);
  211. Assert.Fail ("#1");
  212. } catch (AggregateException e) {
  213. Assert.AreEqual (2, e.InnerExceptions.Count, "#2");
  214. }
  215. }
  216. [Test]
  217. public void Cancel_MultipleExceptionsFirstThrows ()
  218. {
  219. var cts = new CancellationTokenSource ();
  220. cts.Token.Register (() => { throw new ApplicationException ("1"); });
  221. cts.Token.Register (() => { throw new ApplicationException ("2"); });
  222. cts.Token.Register (() => { throw new ApplicationException ("3"); });
  223. try {
  224. cts.Cancel (true);
  225. Assert.Fail ("#1");
  226. } catch (ApplicationException) {
  227. }
  228. cts.Cancel ();
  229. }
  230. [Test]
  231. public void CreateLinkedTokenSource_InvalidArguments ()
  232. {
  233. var cts = new CancellationTokenSource ();
  234. var token = cts.Token;
  235. try {
  236. CancellationTokenSource.CreateLinkedTokenSource (null);
  237. Assert.Fail ("#1");
  238. } catch (ArgumentNullException) {
  239. }
  240. try {
  241. CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken[0]);
  242. Assert.Fail ("#2");
  243. } catch (ArgumentException) {
  244. }
  245. }
  246. [Test]
  247. public void CreateLinkedTokenSource ()
  248. {
  249. var cts = new CancellationTokenSource ();
  250. cts.Cancel ();
  251. var linked = CancellationTokenSource.CreateLinkedTokenSource (cts.Token);
  252. Assert.IsTrue (linked.IsCancellationRequested, "#1");
  253. linked = CancellationTokenSource.CreateLinkedTokenSource (new CancellationToken ());
  254. Assert.IsFalse (linked.IsCancellationRequested, "#2");
  255. }
  256. [Test]
  257. public void Dispose ()
  258. {
  259. var cts = new CancellationTokenSource ();
  260. var token = cts.Token;
  261. cts.Dispose ();
  262. cts.Dispose ();
  263. var b = cts.IsCancellationRequested;
  264. token.ThrowIfCancellationRequested ();
  265. try {
  266. cts.Cancel ();
  267. Assert.Fail ("#1");
  268. } catch (ObjectDisposedException) {
  269. }
  270. try {
  271. var t = cts.Token;
  272. Assert.Fail ("#2");
  273. } catch (ObjectDisposedException) {
  274. }
  275. try {
  276. token.Register (() => { });
  277. Assert.Fail ("#3");
  278. } catch (ObjectDisposedException) {
  279. }
  280. try {
  281. var wh = token.WaitHandle;
  282. Assert.Fail ("#4");
  283. } catch (ObjectDisposedException) {
  284. }
  285. try {
  286. CancellationTokenSource.CreateLinkedTokenSource (token);
  287. Assert.Fail ("#5");
  288. } catch (ObjectDisposedException) {
  289. }
  290. #if NET_4_5
  291. try {
  292. cts.CancelAfter (1);
  293. Assert.Fail ("#6");
  294. } catch (ObjectDisposedException) {
  295. }
  296. #endif
  297. }
  298. [Test]
  299. public void RegisterThenDispose ()
  300. {
  301. var cts1 = new CancellationTokenSource ();
  302. var reg1 = cts1.Token.Register (() => { throw new ApplicationException (); });
  303. var cts2 = new CancellationTokenSource ();
  304. var reg2 = cts2.Token.Register (() => { throw new ApplicationException (); });
  305. Assert.AreNotEqual (cts1, cts2, "#1");
  306. Assert.AreNotSame (cts1, cts2, "#2");
  307. reg1.Dispose ();
  308. cts1.Cancel ();
  309. try {
  310. cts2.Cancel ();
  311. Assert.Fail ("#3");
  312. } catch (AggregateException) {
  313. }
  314. }
  315. [Test]
  316. public void RegisterWhileCancelling ()
  317. {
  318. var cts = new CancellationTokenSource ();
  319. var mre = new ManualResetEvent (false);
  320. var mre2 = new ManualResetEvent (false);
  321. int called = 0;
  322. cts.Token.Register (() => {
  323. Assert.IsTrue (cts.IsCancellationRequested, "#10");
  324. Assert.IsTrue (cts.Token.WaitHandle.WaitOne (0), "#11");
  325. mre2.Set ();
  326. mre.WaitOne (3000);
  327. called += 11;
  328. });
  329. var t = Task.Factory.StartNew (() => { cts.Cancel (); });
  330. Assert.IsTrue (mre2.WaitOne (1000), "#0");
  331. cts.Token.Register (() => { called++; });
  332. Assert.AreEqual (1, called, "#1");
  333. Assert.IsFalse (t.IsCompleted, "#2");
  334. mre.Set ();
  335. Assert.IsTrue (t.Wait (1000), "#3");
  336. Assert.AreEqual (12, called, "#4");
  337. }
  338. [Test]
  339. public void ReEntrantRegistrationTest ()
  340. {
  341. bool unregister = false;
  342. bool register = false;
  343. var source = new CancellationTokenSource ();
  344. var token = source.Token;
  345. Console.WriteLine ("Test1");
  346. var reg = token.Register (() => unregister = true);
  347. token.Register (() => reg.Dispose ());
  348. token.Register (() => { Console.WriteLine ("Gnyah"); token.Register (() => register = true); });
  349. source.Cancel ();
  350. Assert.IsFalse (unregister);
  351. Assert.IsTrue (register);
  352. }
  353. [Test]
  354. public void DisposeAfterRegistrationTest ()
  355. {
  356. var source = new CancellationTokenSource ();
  357. bool ran = false;
  358. var req = source.Token.Register (() => ran = true);
  359. source.Dispose ();
  360. req.Dispose ();
  361. Assert.IsFalse (ran);
  362. }
  363. [Test]
  364. public void CancelLinkedTokenSource ()
  365. {
  366. var cts = new CancellationTokenSource ();
  367. bool canceled = false;
  368. cts.Token.Register (() => canceled = true);
  369. using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cts.Token))
  370. ;
  371. Assert.IsFalse (canceled, "#1");
  372. Assert.IsFalse (cts.IsCancellationRequested, "#2");
  373. cts.Cancel ();
  374. Assert.IsTrue (canceled, "#3");
  375. }
  376. [Category ("NotWorking")] // why would linked token be imune to ObjectDisposedException on Cancel?
  377. [Test]
  378. public void ConcurrentCancelLinkedTokenSourceWhileDisposing ()
  379. {
  380. for (int i = 0, total = 500; i < total; ++i) {
  381. var src = new CancellationTokenSource ();
  382. var linked = CancellationTokenSource.CreateLinkedTokenSource (src.Token);
  383. var cntd = new CountdownEvent (2);
  384. var t1 = new Thread (() => {
  385. if (!cntd.Signal ())
  386. cntd.Wait (200);
  387. src.Cancel ();
  388. });
  389. var t2 = new Thread (() => {
  390. if (!cntd.Signal ())
  391. cntd.Wait (200);
  392. linked.Dispose ();
  393. });
  394. t1.Start ();
  395. t2.Start ();
  396. t1.Join (500);
  397. t2.Join (500);
  398. }
  399. }
  400. #if NET_4_5
  401. [Test]
  402. public void DisposeRace ()
  403. {
  404. for (int i = 0, total = 1000; i < total; ++i) {
  405. var c1 = new CancellationTokenSource ();
  406. var wh = c1.Token.WaitHandle;
  407. c1.CancelAfter (1);
  408. Thread.Sleep (1);
  409. c1.Dispose ();
  410. }
  411. }
  412. #endif
  413. }
  414. }