Task.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  1. //
  2. // Task.cs
  3. //
  4. // Authors:
  5. // Marek Safar <[email protected]>
  6. // Jérémie Laval <jeremie dot laval at xamarin dot com>
  7. //
  8. // Copyright (c) 2008 Jérémie "Garuma" Laval
  9. // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining a copy
  12. // of this software and associated documentation files (the "Software"), to deal
  13. // in the Software without restriction, including without limitation the rights
  14. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. // copies of the Software, and to permit persons to whom the Software is
  16. // furnished to do so, subject to the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be included in
  19. // all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. // THE SOFTWARE.
  28. //
  29. //
  30. #if NET_4_0 || MOBILE
  31. using System;
  32. using System.Threading;
  33. using System.Collections.Concurrent;
  34. using System.Collections.Generic;
  35. using System.Runtime.CompilerServices;
  36. namespace System.Threading.Tasks
  37. {
  38. [System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}")]
  39. [System.Diagnostics.DebuggerTypeProxy (typeof (TaskDebuggerView))]
  40. public class Task : IDisposable, IAsyncResult
  41. {
  42. // With this attribute each thread has its own value so that it's correct for our Schedule code
  43. // and for Parent property.
  44. [System.ThreadStatic]
  45. static Task current;
  46. [System.ThreadStatic]
  47. static Action<Task> childWorkAdder;
  48. // parent is the outer task in which this task is created
  49. readonly Task parent;
  50. // contAncestor is the Task on which this continuation was setup
  51. readonly Task contAncestor;
  52. static int id = -1;
  53. static readonly TaskFactory defaultFactory = new TaskFactory ();
  54. CountdownEvent childTasks;
  55. int taskId;
  56. TaskCreationOptions creationOptions;
  57. internal TaskScheduler scheduler;
  58. TaskExceptionSlot exSlot;
  59. TaskStatus status;
  60. TaskActionInvoker invoker;
  61. object state;
  62. internal AtomicBooleanValue executing;
  63. TaskCompletionQueue<IContinuation> continuations;
  64. CancellationToken token;
  65. CancellationTokenRegistration? cancellationRegistration;
  66. internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
  67. const TaskCreationOptions MaxTaskCreationOptions =
  68. #if NET_4_5
  69. TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
  70. #endif
  71. TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
  72. public Task (Action action)
  73. : this (action, TaskCreationOptions.None)
  74. {
  75. }
  76. public Task (Action action, TaskCreationOptions creationOptions)
  77. : this (action, CancellationToken.None, creationOptions)
  78. {
  79. }
  80. public Task (Action action, CancellationToken cancellationToken)
  81. : this (action, cancellationToken, TaskCreationOptions.None)
  82. {
  83. }
  84. public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
  85. : this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
  86. {
  87. if (action == null)
  88. throw new ArgumentNullException ("action");
  89. if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
  90. throw new ArgumentOutOfRangeException ("creationOptions");
  91. }
  92. public Task (Action<object> action, object state)
  93. : this (action, state, TaskCreationOptions.None)
  94. {
  95. }
  96. public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
  97. : this (action, state, CancellationToken.None, creationOptions)
  98. {
  99. }
  100. public Task (Action<object> action, object state, CancellationToken cancellationToken)
  101. : this (action, state, cancellationToken, TaskCreationOptions.None)
  102. {
  103. }
  104. public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
  105. : this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
  106. {
  107. if (action == null)
  108. throw new ArgumentNullException ("action");
  109. if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
  110. throw new ArgumentOutOfRangeException ("creationOptions");
  111. }
  112. internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
  113. TaskCreationOptions creationOptions, Task parent = null, Task contAncestor = null, bool ignoreCancellation = false)
  114. {
  115. this.invoker = invoker;
  116. this.creationOptions = creationOptions;
  117. this.state = state;
  118. this.taskId = Interlocked.Increment (ref id);
  119. this.token = cancellationToken;
  120. this.parent = parent = parent == null ? current : parent;
  121. this.contAncestor = contAncestor;
  122. this.status = cancellationToken.IsCancellationRequested && !ignoreCancellation ? TaskStatus.Canceled : TaskStatus.Created;
  123. // Process creationOptions
  124. #if NET_4_5
  125. if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent)
  126. && parent != null && !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
  127. #else
  128. if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
  129. #endif
  130. parent.AddChild ();
  131. if (token.CanBeCanceled && !ignoreCancellation)
  132. cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
  133. }
  134. static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
  135. {
  136. return (opt & member) == member;
  137. }
  138. #region Start
  139. public void Start ()
  140. {
  141. Start (TaskScheduler.Current);
  142. }
  143. public void Start (TaskScheduler scheduler)
  144. {
  145. if (scheduler == null)
  146. throw new ArgumentNullException ("scheduler");
  147. if (status >= TaskStatus.WaitingToRun)
  148. throw new InvalidOperationException ("The Task is not in a valid state to be started.");
  149. if (IsContinuation)
  150. throw new InvalidOperationException ("Start may not be called on a continuation task");
  151. SetupScheduler (scheduler);
  152. Schedule ();
  153. }
  154. internal void SetupScheduler (TaskScheduler scheduler)
  155. {
  156. this.scheduler = scheduler;
  157. Status = TaskStatus.WaitingForActivation;
  158. }
  159. public void RunSynchronously ()
  160. {
  161. RunSynchronously (TaskScheduler.Current);
  162. }
  163. public void RunSynchronously (TaskScheduler scheduler)
  164. {
  165. if (scheduler == null)
  166. throw new ArgumentNullException ("scheduler");
  167. if (Status > TaskStatus.WaitingForActivation)
  168. throw new InvalidOperationException ("The task is not in a valid state to be started");
  169. SetupScheduler (scheduler);
  170. var saveStatus = status;
  171. Status = TaskStatus.WaitingToRun;
  172. try {
  173. if (scheduler.RunInline (this, false))
  174. return;
  175. } catch (Exception inner) {
  176. throw new TaskSchedulerException (inner);
  177. }
  178. Status = saveStatus;
  179. Start (scheduler);
  180. Wait ();
  181. }
  182. #endregion
  183. #region ContinueWith
  184. public Task ContinueWith (Action<Task> continuationAction)
  185. {
  186. return ContinueWith (continuationAction, TaskContinuationOptions.None);
  187. }
  188. public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
  189. {
  190. return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
  191. }
  192. public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
  193. {
  194. return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
  195. }
  196. public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
  197. {
  198. return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
  199. }
  200. public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  201. {
  202. if (continuationAction == null)
  203. throw new ArgumentNullException ("continuationAction");
  204. if (scheduler == null)
  205. throw new ArgumentNullException ("scheduler");
  206. return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
  207. }
  208. internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  209. {
  210. var lazyCancellation = false;
  211. #if NET_4_5
  212. lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
  213. #endif
  214. var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this, lazyCancellation);
  215. ContinueWithCore (continuation, continuationOptions, scheduler);
  216. return continuation;
  217. }
  218. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
  219. {
  220. return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
  221. }
  222. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
  223. {
  224. return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
  225. }
  226. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
  227. {
  228. return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
  229. }
  230. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
  231. {
  232. return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
  233. }
  234. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
  235. TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  236. {
  237. if (continuationFunction == null)
  238. throw new ArgumentNullException ("continuationFunction");
  239. if (scheduler == null)
  240. throw new ArgumentNullException ("scheduler");
  241. return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
  242. }
  243. internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  244. {
  245. var lazyCancellation = false;
  246. #if NET_4_5
  247. lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
  248. #endif
  249. var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this, lazyCancellation);
  250. ContinueWithCore (continuation, continuationOptions, scheduler);
  251. return continuation;
  252. }
  253. internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
  254. {
  255. const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
  256. const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
  257. const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
  258. if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
  259. throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
  260. // Already set the scheduler so that user can call Wait and that sort of stuff
  261. continuation.scheduler = scheduler;
  262. continuation.Status = TaskStatus.WaitingForActivation;
  263. ContinueWith (new TaskContinuation (continuation, options));
  264. }
  265. internal void ContinueWith (IContinuation continuation)
  266. {
  267. if (IsCompleted) {
  268. continuation.Execute ();
  269. return;
  270. }
  271. continuations.Add (continuation);
  272. // Retry in case completion was achieved but event adding was too late
  273. if (IsCompleted && continuations.Remove (continuation))
  274. continuation.Execute ();
  275. }
  276. void RemoveContinuation (IContinuation continuation)
  277. {
  278. continuations.Remove (continuation);
  279. }
  280. static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
  281. {
  282. TaskCreationOptions options = TaskCreationOptions.None;
  283. if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
  284. options |= TaskCreationOptions.AttachedToParent;
  285. if ((kind & TaskContinuationOptions.PreferFairness) > 0)
  286. options |= TaskCreationOptions.PreferFairness;
  287. if ((kind & TaskContinuationOptions.LongRunning) > 0)
  288. options |= TaskCreationOptions.LongRunning;
  289. return options;
  290. }
  291. #endregion
  292. #region Internal and protected thingies
  293. internal void Schedule ()
  294. {
  295. Status = TaskStatus.WaitingToRun;
  296. // If worker is null it means it is a local one, revert to the old behavior
  297. // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
  298. if (scheduler != TaskScheduler.Current || childWorkAdder == null || HasFlag (creationOptions, TaskCreationOptions.PreferFairness)) {
  299. scheduler.QueueTask (this);
  300. } else {
  301. /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
  302. * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
  303. * the correct Thread during the creation
  304. */
  305. childWorkAdder (this);
  306. }
  307. }
  308. void ThreadStart ()
  309. {
  310. /* Allow scheduler to break fairness of deque ordering without
  311. * breaking its semantic (the task can be executed twice but the
  312. * second time it will return immediately
  313. */
  314. if (!executing.TryRelaxedSet ())
  315. return;
  316. // Disable CancellationToken direct cancellation
  317. if (cancellationRegistration != null) {
  318. cancellationRegistration.Value.Dispose ();
  319. cancellationRegistration = null;
  320. }
  321. // If Task are ran inline on the same thread we might trash these values
  322. var saveCurrent = current;
  323. var saveScheduler = TaskScheduler.Current;
  324. current = this;
  325. #if NET_4_5
  326. TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
  327. #else
  328. TaskScheduler.Current = scheduler;
  329. #endif
  330. if (!token.IsCancellationRequested) {
  331. status = TaskStatus.Running;
  332. try {
  333. InnerInvoke ();
  334. } catch (OperationCanceledException oce) {
  335. if (token != CancellationToken.None && oce.CancellationToken == token)
  336. CancelReal ();
  337. else
  338. HandleGenericException (oce);
  339. } catch (Exception e) {
  340. HandleGenericException (e);
  341. }
  342. } else {
  343. CancelReal ();
  344. }
  345. if (saveCurrent != null)
  346. current = saveCurrent;
  347. if (saveScheduler != null)
  348. TaskScheduler.Current = saveScheduler;
  349. Finish ();
  350. }
  351. internal bool TrySetCanceled ()
  352. {
  353. if (IsCompleted)
  354. return false;
  355. if (!executing.TryRelaxedSet ()) {
  356. var sw = new SpinWait ();
  357. while (!IsCompleted)
  358. sw.SpinOnce ();
  359. return false;
  360. }
  361. CancelReal ();
  362. return true;
  363. }
  364. internal bool TrySetException (AggregateException aggregate)
  365. {
  366. if (IsCompleted)
  367. return false;
  368. if (!executing.TryRelaxedSet ()) {
  369. var sw = new SpinWait ();
  370. while (!IsCompleted)
  371. sw.SpinOnce ();
  372. return false;
  373. }
  374. HandleGenericException (aggregate);
  375. return true;
  376. }
  377. internal void Execute ()
  378. {
  379. ThreadStart ();
  380. }
  381. internal void AddChild ()
  382. {
  383. if (childTasks == null)
  384. Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
  385. childTasks.AddCount ();
  386. }
  387. internal void ChildCompleted (AggregateException childEx)
  388. {
  389. if (childEx != null) {
  390. if (ExceptionSlot.ChildExceptions == null)
  391. Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
  392. ExceptionSlot.ChildExceptions.Enqueue (childEx);
  393. }
  394. if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
  395. ProcessChildExceptions ();
  396. Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
  397. ProcessCompleteDelegates ();
  398. if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
  399. parent.ChildCompleted (this.Exception);
  400. }
  401. }
  402. void InnerInvoke ()
  403. {
  404. if (IsContinuation) {
  405. invoker.Invoke (contAncestor, state, this);
  406. } else {
  407. invoker.Invoke (this, state, this);
  408. }
  409. }
  410. internal void Finish ()
  411. {
  412. // If there was children created and they all finished, we set the countdown
  413. if (childTasks != null) {
  414. if (childTasks.Signal ())
  415. ProcessChildExceptions (true);
  416. }
  417. // Don't override Canceled or Faulted
  418. if (status == TaskStatus.Running) {
  419. if (childTasks == null || childTasks.IsSet)
  420. Status = TaskStatus.RanToCompletion;
  421. else
  422. Status = TaskStatus.WaitingForChildrenToComplete;
  423. }
  424. // Completions are already processed when task is canceled or faulted
  425. if (status == TaskStatus.RanToCompletion)
  426. ProcessCompleteDelegates ();
  427. // Reset the current thingies
  428. if (current == this)
  429. current = null;
  430. if (TaskScheduler.Current == scheduler)
  431. TaskScheduler.Current = null;
  432. if (cancellationRegistration.HasValue)
  433. cancellationRegistration.Value.Dispose ();
  434. // Tell parent that we are finished
  435. if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) && parent != null && status != TaskStatus.WaitingForChildrenToComplete) {
  436. parent.ChildCompleted (this.Exception);
  437. }
  438. }
  439. void ProcessCompleteDelegates ()
  440. {
  441. if (continuations.HasElements) {
  442. IContinuation continuation;
  443. while (continuations.TryGetNextCompletion (out continuation))
  444. continuation.Execute ();
  445. }
  446. }
  447. void ProcessChildExceptions (bool isParent = false)
  448. {
  449. if (exSlot == null || exSlot.ChildExceptions == null)
  450. return;
  451. if (ExceptionSlot.Exception == null)
  452. exSlot.Exception = new AggregateException ();
  453. AggregateException childEx;
  454. while (exSlot.ChildExceptions.TryDequeue (out childEx))
  455. exSlot.Exception.AddChildException (childEx);
  456. if (isParent) {
  457. Status = TaskStatus.Faulted;
  458. ProcessCompleteDelegates ();
  459. }
  460. }
  461. #endregion
  462. #region Cancel and Wait related method
  463. internal void CancelReal ()
  464. {
  465. Status = TaskStatus.Canceled;
  466. ProcessCompleteDelegates ();
  467. }
  468. void HandleGenericException (Exception e)
  469. {
  470. HandleGenericException (new AggregateException (e));
  471. }
  472. void HandleGenericException (AggregateException e)
  473. {
  474. ExceptionSlot.Exception = e;
  475. Thread.MemoryBarrier ();
  476. Status = TaskStatus.Faulted;
  477. ProcessCompleteDelegates ();
  478. }
  479. internal bool WaitOnChildren ()
  480. {
  481. if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
  482. childTasks.Wait ();
  483. return true;
  484. }
  485. return false;
  486. }
  487. public void Wait ()
  488. {
  489. Wait (Timeout.Infinite, CancellationToken.None);
  490. }
  491. public void Wait (CancellationToken cancellationToken)
  492. {
  493. Wait (Timeout.Infinite, cancellationToken);
  494. }
  495. public bool Wait (TimeSpan timeout)
  496. {
  497. return Wait (CheckTimeout (timeout), CancellationToken.None);
  498. }
  499. public bool Wait (int millisecondsTimeout)
  500. {
  501. return Wait (millisecondsTimeout, CancellationToken.None);
  502. }
  503. public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
  504. {
  505. if (millisecondsTimeout < -1)
  506. throw new ArgumentOutOfRangeException ("millisecondsTimeout");
  507. bool result = true;
  508. if (!IsCompleted) {
  509. // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
  510. if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
  511. scheduler.RunInline (this, true);
  512. if (!IsCompleted) {
  513. var continuation = new ManualResetContinuation ();
  514. try {
  515. ContinueWith (continuation);
  516. result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
  517. } finally {
  518. if (!result)
  519. RemoveContinuation (continuation);
  520. continuation.Dispose ();
  521. }
  522. }
  523. }
  524. if (IsCanceled)
  525. throw new AggregateException (new TaskCanceledException (this));
  526. var exception = Exception;
  527. if (exception != null)
  528. throw exception;
  529. if (childTasks != null)
  530. childTasks.Wait ();
  531. return result;
  532. }
  533. public static void WaitAll (params Task[] tasks)
  534. {
  535. WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
  536. }
  537. public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
  538. {
  539. WaitAll (tasks, Timeout.Infinite, cancellationToken);
  540. }
  541. public static bool WaitAll (Task[] tasks, TimeSpan timeout)
  542. {
  543. return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
  544. }
  545. public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
  546. {
  547. return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
  548. }
  549. public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
  550. {
  551. if (tasks == null)
  552. throw new ArgumentNullException ("tasks");
  553. bool result = true;
  554. foreach (var t in tasks) {
  555. if (t == null)
  556. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  557. result &= t.Status == TaskStatus.RanToCompletion;
  558. }
  559. if (!result) {
  560. var continuation = new CountdownContinuation (tasks.Length);
  561. try {
  562. foreach (var t in tasks)
  563. t.ContinueWith (continuation);
  564. result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
  565. } finally {
  566. List<Exception> exceptions = null;
  567. foreach (var t in tasks) {
  568. if (result) {
  569. if (t.Status == TaskStatus.RanToCompletion)
  570. continue;
  571. if (exceptions == null)
  572. exceptions = new List<Exception> ();
  573. if (t.Exception != null)
  574. exceptions.AddRange (t.Exception.InnerExceptions);
  575. else
  576. exceptions.Add (new TaskCanceledException (t));
  577. } else {
  578. t.RemoveContinuation (continuation);
  579. }
  580. }
  581. continuation.Dispose ();
  582. if (exceptions != null)
  583. throw new AggregateException (exceptions);
  584. }
  585. }
  586. return result;
  587. }
  588. public static int WaitAny (params Task[] tasks)
  589. {
  590. return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
  591. }
  592. public static int WaitAny (Task[] tasks, TimeSpan timeout)
  593. {
  594. return WaitAny (tasks, CheckTimeout (timeout));
  595. }
  596. public static int WaitAny (Task[] tasks, int millisecondsTimeout)
  597. {
  598. return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
  599. }
  600. public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
  601. {
  602. return WaitAny (tasks, Timeout.Infinite, cancellationToken);
  603. }
  604. public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
  605. {
  606. if (tasks == null)
  607. throw new ArgumentNullException ("tasks");
  608. if (millisecondsTimeout < -1)
  609. throw new ArgumentOutOfRangeException ("millisecondsTimeout");
  610. CheckForNullTasks (tasks);
  611. if (tasks.Length > 0) {
  612. var continuation = new ManualResetContinuation ();
  613. bool result = false;
  614. try {
  615. for (int i = 0; i < tasks.Length; i++) {
  616. var t = tasks[i];
  617. if (t.IsCompleted)
  618. return i;
  619. t.ContinueWith (continuation);
  620. }
  621. if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
  622. return -1;
  623. } finally {
  624. if (!result)
  625. foreach (var t in tasks)
  626. t.RemoveContinuation (continuation);
  627. continuation.Dispose ();
  628. }
  629. }
  630. int firstFinished = -1;
  631. for (int i = 0; i < tasks.Length; i++) {
  632. var t = tasks[i];
  633. if (t.IsCompleted) {
  634. firstFinished = i;
  635. break;
  636. }
  637. }
  638. return firstFinished;
  639. }
  640. static int CheckTimeout (TimeSpan timeout)
  641. {
  642. try {
  643. return checked ((int)timeout.TotalMilliseconds);
  644. } catch (System.OverflowException) {
  645. throw new ArgumentOutOfRangeException ("timeout");
  646. }
  647. }
  648. static void CheckForNullTasks (Task[] tasks)
  649. {
  650. foreach (var t in tasks)
  651. if (t == null)
  652. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  653. }
  654. #endregion
  655. #region Dispose
  656. public void Dispose ()
  657. {
  658. Dispose (true);
  659. }
  660. protected virtual void Dispose (bool disposing)
  661. {
  662. if (!IsCompleted)
  663. throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
  664. // Set action to null so that the GC can collect the delegate and thus
  665. // any big object references that the user might have captured in a anonymous method
  666. if (disposing) {
  667. invoker = null;
  668. state = null;
  669. if (cancellationRegistration != null)
  670. cancellationRegistration.Value.Dispose ();
  671. }
  672. }
  673. #endregion
  674. #if NET_4_5
  675. public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
  676. {
  677. return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
  678. }
  679. public Task ContinueWith (Action<Task, object> continuationAction, object state)
  680. {
  681. return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
  682. }
  683. public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
  684. {
  685. return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
  686. }
  687. public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
  688. {
  689. return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
  690. }
  691. public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
  692. {
  693. return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
  694. }
  695. public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
  696. TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  697. {
  698. if (continuationAction == null)
  699. throw new ArgumentNullException ("continuationAction");
  700. if (scheduler == null)
  701. throw new ArgumentNullException ("scheduler");
  702. Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
  703. state, cancellationToken,
  704. GetCreationOptions (continuationOptions),
  705. parent,
  706. this);
  707. ContinueWithCore (continuation, continuationOptions, scheduler);
  708. return continuation;
  709. }
  710. public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
  711. {
  712. return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
  713. }
  714. public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
  715. {
  716. return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
  717. }
  718. public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
  719. {
  720. return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
  721. }
  722. public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
  723. {
  724. return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
  725. }
  726. public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
  727. TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  728. {
  729. if (continuationFunction == null)
  730. throw new ArgumentNullException ("continuationFunction");
  731. if (scheduler == null)
  732. throw new ArgumentNullException ("scheduler");
  733. var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
  734. state,
  735. cancellationToken,
  736. GetCreationOptions (continuationOptions),
  737. parent,
  738. this);
  739. ContinueWithCore (t, continuationOptions, scheduler);
  740. return t;
  741. }
  742. public static Task Delay (int millisecondsDelay)
  743. {
  744. return Delay (millisecondsDelay, CancellationToken.None);
  745. }
  746. public static Task Delay (TimeSpan delay)
  747. {
  748. return Delay (CheckTimeout (delay), CancellationToken.None);
  749. }
  750. public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
  751. {
  752. return Delay (CheckTimeout (delay), cancellationToken);
  753. }
  754. public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
  755. {
  756. if (millisecondsDelay < -1)
  757. throw new ArgumentOutOfRangeException ("millisecondsDelay");
  758. var task = new Task (TaskActionInvoker.Delay, millisecondsDelay, cancellationToken, TaskCreationOptions.None, null, TaskConstants.Finished);
  759. task.SetupScheduler (TaskScheduler.Current);
  760. if (millisecondsDelay != Timeout.Infinite)
  761. task.scheduler.QueueTask (task);
  762. return task;
  763. }
  764. public static Task<TResult> FromResult<TResult> (TResult result)
  765. {
  766. var tcs = new TaskCompletionSource<TResult> ();
  767. tcs.SetResult (result);
  768. return tcs.Task;
  769. }
  770. public TaskAwaiter GetAwaiter ()
  771. {
  772. return new TaskAwaiter (this);
  773. }
  774. public static Task Run (Action action)
  775. {
  776. return Run (action, CancellationToken.None);
  777. }
  778. public static Task Run (Action action, CancellationToken cancellationToken)
  779. {
  780. if (cancellationToken.IsCancellationRequested)
  781. return TaskConstants.Canceled;
  782. return Task.Factory.StartNew (action, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  783. }
  784. public static Task Run (Func<Task> function)
  785. {
  786. return Run (function, CancellationToken.None);
  787. }
  788. public static Task Run (Func<Task> function, CancellationToken cancellationToken)
  789. {
  790. if (cancellationToken.IsCancellationRequested)
  791. return TaskConstants.Canceled;
  792. return TaskExtensions.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
  793. }
  794. public static Task<TResult> Run<TResult> (Func<TResult> function)
  795. {
  796. return Run (function, CancellationToken.None);
  797. }
  798. public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
  799. {
  800. if (cancellationToken.IsCancellationRequested)
  801. return TaskConstants<TResult>.Canceled;
  802. return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
  803. }
  804. public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
  805. {
  806. return Run (function, CancellationToken.None);
  807. }
  808. public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
  809. {
  810. if (cancellationToken.IsCancellationRequested)
  811. return TaskConstants<TResult>.Canceled;
  812. return TaskExtensions.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
  813. }
  814. public static Task WhenAll (params Task[] tasks)
  815. {
  816. if (tasks == null)
  817. throw new ArgumentNullException ("tasks");
  818. return WhenAllCore (tasks);
  819. }
  820. public static Task WhenAll (IEnumerable<Task> tasks)
  821. {
  822. if (tasks == null)
  823. throw new ArgumentNullException ("tasks");
  824. // Call ToList on input enumeration or we end up
  825. // enumerating it more than once
  826. return WhenAllCore (new List<Task> (tasks));
  827. }
  828. public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
  829. {
  830. if (tasks == null)
  831. throw new ArgumentNullException ("tasks");
  832. return WhenAllCore<TResult> (tasks);
  833. }
  834. public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
  835. {
  836. if (tasks == null)
  837. throw new ArgumentNullException ("tasks");
  838. // Call ToList on input enumeration or we end up
  839. // enumerating it more than once
  840. return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
  841. }
  842. internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
  843. {
  844. foreach (var t in tasks) {
  845. if (t == null)
  846. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  847. }
  848. var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
  849. task.SetupScheduler (TaskScheduler.Current);
  850. var continuation = new WhenAllContinuation<TResult> (task, tasks);
  851. foreach (var t in tasks)
  852. t.ContinueWith (continuation);
  853. return task;
  854. }
  855. public static Task<Task> WhenAny (params Task[] tasks)
  856. {
  857. if (tasks == null)
  858. throw new ArgumentNullException ("tasks");
  859. return WhenAnyCore (tasks);
  860. }
  861. public static Task<Task> WhenAny (IEnumerable<Task> tasks)
  862. {
  863. if (tasks == null)
  864. throw new ArgumentNullException ("tasks");
  865. return WhenAnyCore (new List<Task> (tasks));
  866. }
  867. public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
  868. {
  869. if (tasks == null)
  870. throw new ArgumentNullException ("tasks");
  871. return WhenAnyCore<TResult> (tasks);
  872. }
  873. public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
  874. {
  875. if (tasks == null)
  876. throw new ArgumentNullException ("tasks");
  877. return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
  878. }
  879. static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
  880. {
  881. if (tasks.Count == 0)
  882. throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
  883. int completed_index = -1;
  884. for (int i = 0; i < tasks.Count; ++i) {
  885. var t = tasks[i];
  886. if (t == null)
  887. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  888. if (t.IsCompleted && completed_index < 0)
  889. completed_index = i;
  890. }
  891. var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
  892. if (completed_index > 0) {
  893. task.TrySetResult (tasks[completed_index]);
  894. return task;
  895. }
  896. task.SetupScheduler (TaskScheduler.Current);
  897. var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
  898. foreach (var t in tasks)
  899. t.ContinueWith (continuation);
  900. return task;
  901. }
  902. public static YieldAwaitable Yield ()
  903. {
  904. return new YieldAwaitable ();
  905. }
  906. #endif
  907. internal static Task WhenAllCore (IList<Task> tasks)
  908. {
  909. bool all_completed = true;
  910. foreach (var t in tasks) {
  911. if (t == null)
  912. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  913. all_completed &= t.Status == TaskStatus.RanToCompletion;
  914. }
  915. if (all_completed)
  916. return TaskConstants.Finished;
  917. var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
  918. task.SetupScheduler (TaskScheduler.Current);
  919. var continuation = new WhenAllContinuation (task, tasks);
  920. foreach (var t in tasks)
  921. t.ContinueWith (continuation);
  922. return task;
  923. }
  924. internal static Task<Task> WhenAnyCore (IList<Task> tasks)
  925. {
  926. if (tasks.Count == 0)
  927. throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
  928. int completed_index = -1;
  929. for (int i = 0; i < tasks.Count; ++i) {
  930. var t = tasks [i];
  931. if (t == null)
  932. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  933. if (t.IsCompleted && completed_index < 0)
  934. completed_index = i;
  935. }
  936. var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
  937. if (completed_index > 0) {
  938. task.TrySetResult (tasks[completed_index]);
  939. return task;
  940. }
  941. task.SetupScheduler (TaskScheduler.Current);
  942. var continuation = new WhenAnyContinuation<Task> (task, tasks);
  943. foreach (var t in tasks)
  944. t.ContinueWith (continuation);
  945. return task;
  946. }
  947. #region Properties
  948. internal CancellationToken CancellationToken {
  949. get {
  950. return token;
  951. }
  952. }
  953. public static TaskFactory Factory {
  954. get {
  955. return defaultFactory;
  956. }
  957. }
  958. public static int? CurrentId {
  959. get {
  960. Task t = current;
  961. return t == null ? (int?)null : t.Id;
  962. }
  963. }
  964. public AggregateException Exception {
  965. get {
  966. if (exSlot == null)
  967. return null;
  968. exSlot.Observed = true;
  969. return exSlot.Exception;
  970. }
  971. }
  972. public bool IsCanceled {
  973. get {
  974. return status == TaskStatus.Canceled;
  975. }
  976. }
  977. public bool IsCompleted {
  978. get {
  979. return status >= TaskStatus.RanToCompletion;
  980. }
  981. }
  982. public bool IsFaulted {
  983. get {
  984. return status == TaskStatus.Faulted;
  985. }
  986. }
  987. public TaskCreationOptions CreationOptions {
  988. get {
  989. return creationOptions & MaxTaskCreationOptions;
  990. }
  991. }
  992. public TaskStatus Status {
  993. get {
  994. return status;
  995. }
  996. internal set {
  997. status = value;
  998. Thread.MemoryBarrier ();
  999. }
  1000. }
  1001. TaskExceptionSlot ExceptionSlot {
  1002. get {
  1003. if (exSlot != null)
  1004. return exSlot;
  1005. Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
  1006. return exSlot;
  1007. }
  1008. }
  1009. public object AsyncState {
  1010. get {
  1011. return state;
  1012. }
  1013. }
  1014. bool IAsyncResult.CompletedSynchronously {
  1015. get {
  1016. return true;
  1017. }
  1018. }
  1019. WaitHandle IAsyncResult.AsyncWaitHandle {
  1020. get {
  1021. return null;
  1022. }
  1023. }
  1024. public int Id {
  1025. get {
  1026. return taskId;
  1027. }
  1028. }
  1029. bool IsContinuation {
  1030. get {
  1031. return contAncestor != null;
  1032. }
  1033. }
  1034. internal Task ContinuationAncestor {
  1035. get {
  1036. return contAncestor;
  1037. }
  1038. }
  1039. internal string DisplayActionMethod {
  1040. get {
  1041. Delegate d = invoker.Action;
  1042. return d == null ? "<none>" : d.Method.ToString ();
  1043. }
  1044. }
  1045. #endregion
  1046. }
  1047. }
  1048. #endif