PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/corlib/System.Threading.Tasks/Task.cs

https://bitbucket.org/danipen/mono
C# | 1320 lines | 1013 code | 247 blank | 60 comment | 231 complexity | 1cdf40608bce5732b086f6767a6fe071 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // Task.cs
  3. //
  4. // Authors:
  5. // Marek Safar <marek.safar@gmail.com>
  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
  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 (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent)
  126. && !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
  127. #else
  128. if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
  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. if (IsContinuation)
  170. throw new InvalidOperationException ("RunSynchronously may not be called on a continuation task");
  171. RunSynchronouslyCore (scheduler);
  172. }
  173. internal void RunSynchronouslyCore (TaskScheduler scheduler)
  174. {
  175. SetupScheduler (scheduler);
  176. var saveStatus = status;
  177. Status = TaskStatus.WaitingToRun;
  178. try {
  179. if (scheduler.RunInline (this, false))
  180. return;
  181. } catch (Exception inner) {
  182. throw new TaskSchedulerException (inner);
  183. }
  184. Status = saveStatus;
  185. Start (scheduler);
  186. Wait ();
  187. }
  188. #endregion
  189. #region ContinueWith
  190. public Task ContinueWith (Action<Task> continuationAction)
  191. {
  192. return ContinueWith (continuationAction, TaskContinuationOptions.None);
  193. }
  194. public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
  195. {
  196. return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
  197. }
  198. public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
  199. {
  200. return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
  201. }
  202. public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
  203. {
  204. return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
  205. }
  206. public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  207. {
  208. if (continuationAction == null)
  209. throw new ArgumentNullException ("continuationAction");
  210. if (scheduler == null)
  211. throw new ArgumentNullException ("scheduler");
  212. return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
  213. }
  214. internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  215. {
  216. var lazyCancellation = false;
  217. #if NET_4_5
  218. lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
  219. #endif
  220. var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this, lazyCancellation);
  221. ContinueWithCore (continuation, continuationOptions, scheduler);
  222. return continuation;
  223. }
  224. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
  225. {
  226. return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
  227. }
  228. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
  229. {
  230. return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
  231. }
  232. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
  233. {
  234. return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
  235. }
  236. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
  237. {
  238. return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
  239. }
  240. public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
  241. TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  242. {
  243. if (continuationFunction == null)
  244. throw new ArgumentNullException ("continuationFunction");
  245. if (scheduler == null)
  246. throw new ArgumentNullException ("scheduler");
  247. return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
  248. }
  249. internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
  250. {
  251. var lazyCancellation = false;
  252. #if NET_4_5
  253. lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
  254. #endif
  255. var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this, lazyCancellation);
  256. ContinueWithCore (continuation, continuationOptions, scheduler);
  257. return continuation;
  258. }
  259. internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
  260. {
  261. const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
  262. const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
  263. const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
  264. if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
  265. throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
  266. // Already set the scheduler so that user can call Wait and that sort of stuff
  267. continuation.scheduler = scheduler;
  268. continuation.Status = TaskStatus.WaitingForActivation;
  269. ContinueWith (new TaskContinuation (continuation, options));
  270. }
  271. internal void ContinueWith (IContinuation continuation)
  272. {
  273. if (IsCompleted) {
  274. continuation.Execute ();
  275. return;
  276. }
  277. continuations.Add (continuation);
  278. // Retry in case completion was achieved but event adding was too late
  279. if (IsCompleted && continuations.Remove (continuation))
  280. continuation.Execute ();
  281. }
  282. void RemoveContinuation (IContinuation continuation)
  283. {
  284. continuations.Remove (continuation);
  285. }
  286. static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
  287. {
  288. TaskCreationOptions options = TaskCreationOptions.None;
  289. if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
  290. options |= TaskCreationOptions.AttachedToParent;
  291. if ((kind & TaskContinuationOptions.PreferFairness) > 0)
  292. options |= TaskCreationOptions.PreferFairness;
  293. if ((kind & TaskContinuationOptions.LongRunning) > 0)
  294. options |= TaskCreationOptions.LongRunning;
  295. return options;
  296. }
  297. #endregion
  298. #region Internal and protected thingies
  299. internal void Schedule ()
  300. {
  301. Status = TaskStatus.WaitingToRun;
  302. // If worker is null it means it is a local one, revert to the old behavior
  303. // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
  304. if (scheduler != TaskScheduler.Current || childWorkAdder == null || HasFlag (creationOptions, TaskCreationOptions.PreferFairness)) {
  305. scheduler.QueueTask (this);
  306. } else {
  307. /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
  308. * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
  309. * the correct Thread during the creation
  310. */
  311. childWorkAdder (this);
  312. }
  313. }
  314. void ThreadStart ()
  315. {
  316. /* Allow scheduler to break fairness of deque ordering without
  317. * breaking its semantic (the task can be executed twice but the
  318. * second time it will return immediately
  319. */
  320. if (!executing.TryRelaxedSet ())
  321. return;
  322. // Disable CancellationToken direct cancellation
  323. if (cancellationRegistration != null) {
  324. cancellationRegistration.Value.Dispose ();
  325. cancellationRegistration = null;
  326. }
  327. // If Task are ran inline on the same thread we might trash these values
  328. var saveCurrent = current;
  329. var saveScheduler = TaskScheduler.Current;
  330. current = this;
  331. #if NET_4_5
  332. TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
  333. #else
  334. TaskScheduler.Current = scheduler;
  335. #endif
  336. if (!token.IsCancellationRequested) {
  337. status = TaskStatus.Running;
  338. try {
  339. InnerInvoke ();
  340. } catch (OperationCanceledException oce) {
  341. if (token != CancellationToken.None && oce.CancellationToken == token)
  342. CancelReal ();
  343. else
  344. HandleGenericException (oce);
  345. } catch (Exception e) {
  346. HandleGenericException (e);
  347. }
  348. } else {
  349. CancelReal ();
  350. }
  351. if (saveCurrent != null)
  352. current = saveCurrent;
  353. if (saveScheduler != null)
  354. TaskScheduler.Current = saveScheduler;
  355. Finish ();
  356. }
  357. internal bool TrySetCanceled ()
  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. CancelReal ();
  368. return true;
  369. }
  370. internal bool TrySetException (AggregateException aggregate)
  371. {
  372. if (IsCompleted)
  373. return false;
  374. if (!executing.TryRelaxedSet ()) {
  375. var sw = new SpinWait ();
  376. while (!IsCompleted)
  377. sw.SpinOnce ();
  378. return false;
  379. }
  380. HandleGenericException (aggregate);
  381. return true;
  382. }
  383. internal bool TrySetExceptionObserved ()
  384. {
  385. if (exSlot != null) {
  386. exSlot.Observed = true;
  387. return true;
  388. }
  389. return false;
  390. }
  391. internal void Execute ()
  392. {
  393. ThreadStart ();
  394. }
  395. internal void AddChild ()
  396. {
  397. if (childTasks == null)
  398. Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
  399. childTasks.AddCount ();
  400. }
  401. internal void ChildCompleted (AggregateException childEx)
  402. {
  403. if (childEx != null) {
  404. if (ExceptionSlot.ChildExceptions == null)
  405. Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
  406. ExceptionSlot.ChildExceptions.Enqueue (childEx);
  407. }
  408. if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
  409. ProcessChildExceptions ();
  410. Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
  411. ProcessCompleteDelegates ();
  412. if (parent != null &&
  413. #if NET_4_5
  414. !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
  415. #endif
  416. HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
  417. parent.ChildCompleted (this.Exception);
  418. }
  419. }
  420. void InnerInvoke ()
  421. {
  422. if (IsContinuation) {
  423. invoker.Invoke (contAncestor, state, this);
  424. } else {
  425. invoker.Invoke (this, state, this);
  426. }
  427. }
  428. internal void Finish ()
  429. {
  430. // If there was children created and they all finished, we set the countdown
  431. if (childTasks != null) {
  432. if (childTasks.Signal ())
  433. ProcessChildExceptions (true);
  434. }
  435. // Don't override Canceled or Faulted
  436. if (status == TaskStatus.Running) {
  437. if (childTasks == null || childTasks.IsSet)
  438. Status = TaskStatus.RanToCompletion;
  439. else
  440. Status = TaskStatus.WaitingForChildrenToComplete;
  441. }
  442. // Tell parent that we are finished
  443. if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) &&
  444. #if NET_4_5
  445. !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
  446. #endif
  447. status != TaskStatus.WaitingForChildrenToComplete) {
  448. parent.ChildCompleted (this.Exception);
  449. }
  450. // Completions are already processed when task is canceled or faulted
  451. if (status == TaskStatus.RanToCompletion)
  452. ProcessCompleteDelegates ();
  453. // Reset the current thingies
  454. if (current == this)
  455. current = null;
  456. if (TaskScheduler.Current == scheduler)
  457. TaskScheduler.Current = null;
  458. if (cancellationRegistration.HasValue)
  459. cancellationRegistration.Value.Dispose ();
  460. }
  461. void ProcessCompleteDelegates ()
  462. {
  463. if (continuations.HasElements) {
  464. IContinuation continuation;
  465. while (continuations.TryGetNextCompletion (out continuation))
  466. continuation.Execute ();
  467. }
  468. }
  469. void ProcessChildExceptions (bool isParent = false)
  470. {
  471. if (exSlot == null || exSlot.ChildExceptions == null)
  472. return;
  473. if (ExceptionSlot.Exception == null)
  474. exSlot.Exception = new AggregateException ();
  475. AggregateException childEx;
  476. while (exSlot.ChildExceptions.TryDequeue (out childEx))
  477. exSlot.Exception.AddChildException (childEx);
  478. if (isParent) {
  479. Status = TaskStatus.Faulted;
  480. ProcessCompleteDelegates ();
  481. }
  482. }
  483. #endregion
  484. #region Cancel and Wait related method
  485. internal void CancelReal ()
  486. {
  487. Status = TaskStatus.Canceled;
  488. ProcessCompleteDelegates ();
  489. }
  490. void HandleGenericException (Exception e)
  491. {
  492. HandleGenericException (new AggregateException (e));
  493. }
  494. void HandleGenericException (AggregateException e)
  495. {
  496. ExceptionSlot.Exception = e;
  497. Thread.MemoryBarrier ();
  498. Status = TaskStatus.Faulted;
  499. ProcessCompleteDelegates ();
  500. }
  501. internal bool WaitOnChildren ()
  502. {
  503. if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
  504. childTasks.Wait ();
  505. return true;
  506. }
  507. return false;
  508. }
  509. public void Wait ()
  510. {
  511. Wait (Timeout.Infinite, CancellationToken.None);
  512. }
  513. public void Wait (CancellationToken cancellationToken)
  514. {
  515. Wait (Timeout.Infinite, cancellationToken);
  516. }
  517. public bool Wait (TimeSpan timeout)
  518. {
  519. return Wait (CheckTimeout (timeout), CancellationToken.None);
  520. }
  521. public bool Wait (int millisecondsTimeout)
  522. {
  523. return Wait (millisecondsTimeout, CancellationToken.None);
  524. }
  525. public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
  526. {
  527. if (millisecondsTimeout < -1)
  528. throw new ArgumentOutOfRangeException ("millisecondsTimeout");
  529. bool result = true;
  530. if (!IsCompleted) {
  531. // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
  532. if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
  533. scheduler.RunInline (this, true);
  534. if (!IsCompleted) {
  535. var continuation = new ManualResetContinuation ();
  536. try {
  537. ContinueWith (continuation);
  538. result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
  539. } finally {
  540. if (!result)
  541. RemoveContinuation (continuation);
  542. continuation.Dispose ();
  543. }
  544. }
  545. }
  546. if (IsCanceled)
  547. throw new AggregateException (new TaskCanceledException (this));
  548. var exception = Exception;
  549. if (exception != null)
  550. throw exception;
  551. return result;
  552. }
  553. public static void WaitAll (params Task[] tasks)
  554. {
  555. WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
  556. }
  557. public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
  558. {
  559. WaitAll (tasks, Timeout.Infinite, cancellationToken);
  560. }
  561. public static bool WaitAll (Task[] tasks, TimeSpan timeout)
  562. {
  563. return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
  564. }
  565. public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
  566. {
  567. return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
  568. }
  569. public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
  570. {
  571. if (tasks == null)
  572. throw new ArgumentNullException ("tasks");
  573. bool result = true;
  574. foreach (var t in tasks) {
  575. if (t == null)
  576. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  577. result &= t.Status == TaskStatus.RanToCompletion;
  578. }
  579. if (!result) {
  580. var continuation = new CountdownContinuation (tasks.Length);
  581. try {
  582. foreach (var t in tasks)
  583. t.ContinueWith (continuation);
  584. result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
  585. } finally {
  586. List<Exception> exceptions = null;
  587. foreach (var t in tasks) {
  588. if (result) {
  589. if (t.Status == TaskStatus.RanToCompletion)
  590. continue;
  591. if (exceptions == null)
  592. exceptions = new List<Exception> ();
  593. if (t.Exception != null)
  594. exceptions.AddRange (t.Exception.InnerExceptions);
  595. else
  596. exceptions.Add (new TaskCanceledException (t));
  597. } else {
  598. t.RemoveContinuation (continuation);
  599. }
  600. }
  601. continuation.Dispose ();
  602. if (exceptions != null)
  603. throw new AggregateException (exceptions);
  604. }
  605. }
  606. return result;
  607. }
  608. public static int WaitAny (params Task[] tasks)
  609. {
  610. return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
  611. }
  612. public static int WaitAny (Task[] tasks, TimeSpan timeout)
  613. {
  614. return WaitAny (tasks, CheckTimeout (timeout));
  615. }
  616. public static int WaitAny (Task[] tasks, int millisecondsTimeout)
  617. {
  618. return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
  619. }
  620. public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
  621. {
  622. return WaitAny (tasks, Timeout.Infinite, cancellationToken);
  623. }
  624. public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
  625. {
  626. if (tasks == null)
  627. throw new ArgumentNullException ("tasks");
  628. if (millisecondsTimeout < -1)
  629. throw new ArgumentOutOfRangeException ("millisecondsTimeout");
  630. CheckForNullTasks (tasks);
  631. if (tasks.Length > 0) {
  632. var continuation = new ManualResetContinuation ();
  633. bool result = false;
  634. try {
  635. for (int i = 0; i < tasks.Length; i++) {
  636. var t = tasks[i];
  637. if (t.IsCompleted)
  638. return i;
  639. t.ContinueWith (continuation);
  640. }
  641. if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
  642. return -1;
  643. } finally {
  644. if (!result)
  645. foreach (var t in tasks)
  646. t.RemoveContinuation (continuation);
  647. continuation.Dispose ();
  648. }
  649. }
  650. int firstFinished = -1;
  651. for (int i = 0; i < tasks.Length; i++) {
  652. var t = tasks[i];
  653. if (t.IsCompleted) {
  654. firstFinished = i;
  655. break;
  656. }
  657. }
  658. return firstFinished;
  659. }
  660. static int CheckTimeout (TimeSpan timeout)
  661. {
  662. try {
  663. return checked ((int)timeout.TotalMilliseconds);
  664. } catch (System.OverflowException) {
  665. throw new ArgumentOutOfRangeException ("timeout");
  666. }
  667. }
  668. static void CheckForNullTasks (Task[] tasks)
  669. {
  670. foreach (var t in tasks)
  671. if (t == null)
  672. throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  673. }
  674. #endregion
  675. #region Dispose
  676. public void Dispose ()
  677. {
  678. Dispose (true);
  679. }
  680. protected virtual void Dispose (bool disposing)
  681. {
  682. if (!IsCompleted)
  683. throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
  684. // Set action to null so that the GC can collect the delegate and thus
  685. // any big object references that the user might have captured in a anonymous method
  686. if (disposing) {
  687. invoker = null;
  688. state = null;
  689. if (cancellationRegistration != null)
  690. cancellationRegistration.Value.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.Current);
  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. return null;
  1047. }
  1048. }
  1049. public int Id {
  1050. get {
  1051. return taskId;
  1052. }
  1053. }
  1054. bool IsContinuation {
  1055. get {
  1056. return contAncestor != null;
  1057. }
  1058. }
  1059. internal Task ContinuationAncestor {
  1060. get {
  1061. return contAncestor;
  1062. }
  1063. }
  1064. internal string DisplayActionMethod {
  1065. get {
  1066. Delegate d = invoker.Action;
  1067. return d == null ? "<none>" : d.Method.ToString ();
  1068. }
  1069. }
  1070. #endregion
  1071. }
  1072. }
  1073. #endif