Task.cs 39 KB

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