3 // Copyright (c) 2008 Jérémie "Garuma" Laval
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 #if NET_4_0 || BOOTSTRAP_NET_4_0
28 using System.Threading;
29 using System.Collections.Concurrent;
31 namespace System.Threading.Tasks
33 public class Task : IDisposable, IAsyncResult
35 // With this attribute each thread has its own value so that it's correct for our Schedule code
36 // and for Parent property.
40 static Action<Task> childWorkAdder;
45 static TaskFactory defaultFactory = new TaskFactory ();
47 CountdownEvent childTasks = new CountdownEvent (1);
50 TaskCreationOptions taskCreationOptions;
53 TaskScheduler taskScheduler;
55 volatile AggregateException exception;
56 volatile bool exceptionObserved;
57 volatile TaskStatus status;
59 Action<object> action;
61 EventHandler completed;
63 CancellationToken token;
65 public Task (Action action) : this (action, TaskCreationOptions.None)
70 public Task (Action action, TaskCreationOptions options) : this (action, CancellationToken.None, options)
75 public Task (Action action, CancellationToken token) : this (action, token, TaskCreationOptions.None)
80 public Task (Action action, CancellationToken token, TaskCreationOptions options)
81 : this ((o) => { if (action != null) action (); }, null, token, options)
85 public Task (Action<object> action, object state) : this (action, state, TaskCreationOptions.None)
89 public Task (Action<object> action, object state, TaskCreationOptions options)
90 : this (action, state, CancellationToken.None, options)
94 public Task (Action<object> action, object state, CancellationToken token)
95 : this (action, state, token, TaskCreationOptions.None)
99 public Task (Action<object> action, object state, CancellationToken token, TaskCreationOptions options)
101 this.taskCreationOptions = options;
102 this.action = action == null ? EmptyFunc : action;
104 this.taskId = Interlocked.Increment (ref id);
105 this.status = TaskStatus.Created;
108 // Process taskCreationOptions
109 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent)) {
118 if (exception != null && !exceptionObserved)
122 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
124 return (opt & member) == member;
127 static void EmptyFunc (object o)
134 Start (TaskScheduler.Current);
137 public void Start (TaskScheduler tscheduler)
139 this.taskScheduler = tscheduler;
140 Start (ProxifyScheduler (tscheduler));
143 void Start (IScheduler scheduler)
145 this.scheduler = scheduler;
146 status = TaskStatus.WaitingForActivation;
150 IScheduler ProxifyScheduler (TaskScheduler tscheduler)
152 IScheduler sched = tscheduler as IScheduler;
153 return sched != null ? sched : new SchedulerProxy (tscheduler);
156 public void RunSynchronously ()
158 RunSynchronously (TaskScheduler.Current);
161 public void RunSynchronously (TaskScheduler tscheduler)
163 // Adopt this scheme for the moment
169 public Task ContinueWith (Action<Task> a)
171 return ContinueWith (a, TaskContinuationOptions.None);
174 public Task ContinueWith (Action<Task> a, TaskContinuationOptions kind)
176 return ContinueWith (a, CancellationToken.None, kind, TaskScheduler.Current);
179 public Task ContinueWith (Action<Task> a, CancellationToken token)
181 return ContinueWith (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
184 public Task ContinueWith (Action<Task> a, TaskScheduler scheduler)
186 return ContinueWith (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
189 public Task ContinueWith (Action<Task> a, CancellationToken token, TaskContinuationOptions kind, TaskScheduler scheduler)
191 Task continuation = new Task ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
192 ContinueWithCore (continuation, kind, scheduler);
196 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a)
198 return ContinueWith<TResult> (a, TaskContinuationOptions.None);
201 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskContinuationOptions options)
203 return ContinueWith<TResult> (a, CancellationToken.None, options, TaskScheduler.Current);
206 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token)
208 return ContinueWith<TResult> (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
211 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskScheduler scheduler)
213 return ContinueWith<TResult> (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
216 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token,
217 TaskContinuationOptions kind, TaskScheduler scheduler)
219 Task<TResult> t = new Task<TResult> ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
221 ContinueWithCore (t, kind, scheduler);
226 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind, TaskScheduler scheduler)
228 ContinueWithCore (continuation, kind, scheduler, () => true);
231 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind,
232 TaskScheduler scheduler, Func<bool> predicate)
234 // Already set the scheduler so that user can call Wait and that sort of stuff
235 continuation.taskScheduler = scheduler;
236 continuation.scheduler = ProxifyScheduler (scheduler);
238 AtomicBoolean launched = new AtomicBoolean ();
239 EventHandler action = delegate (object sender, EventArgs e) {
240 if (!predicate ()) return;
242 if (!launched.Value && launched.TrySet ()) {
243 if (!ContinuationStatusCheck (kind)) {
244 continuation.CancelReal ();
245 continuation.Dispose ();
250 CheckAndSchedule (continuation, kind, scheduler, sender == null);
255 action (null, EventArgs.Empty);
261 // Retry in case completion was achieved but event adding was too late
263 action (null, EventArgs.Empty);
266 bool ContinuationStatusCheck (TaskContinuationOptions kind)
268 if (kind == TaskContinuationOptions.None)
271 int kindCode = (int)kind;
273 if (kindCode >= ((int)TaskContinuationOptions.NotOnRanToCompletion)) {
274 if (status == TaskStatus.Canceled) {
275 if (kind == TaskContinuationOptions.NotOnCanceled)
277 if (kind == TaskContinuationOptions.OnlyOnFaulted)
279 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
281 } else if (status == TaskStatus.Faulted) {
282 if (kind == TaskContinuationOptions.NotOnFaulted)
284 if (kind == TaskContinuationOptions.OnlyOnCanceled)
286 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
288 } else if (status == TaskStatus.RanToCompletion) {
289 if (kind == TaskContinuationOptions.NotOnRanToCompletion)
291 if (kind == TaskContinuationOptions.OnlyOnFaulted)
293 if (kind == TaskContinuationOptions.OnlyOnCanceled)
301 void CheckAndSchedule (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler, bool fromCaller)
304 && (options == TaskContinuationOptions.None || (options & TaskContinuationOptions.ExecuteSynchronously) > 0))
305 continuation.ThreadStart ();
307 continuation.Start (scheduler);
310 internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
312 TaskCreationOptions options = TaskCreationOptions.None;
313 if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
314 options |= TaskCreationOptions.AttachedToParent;
315 if ((kind & TaskContinuationOptions.PreferFairness) > 0)
316 options |= TaskCreationOptions.PreferFairness;
317 if ((kind & TaskContinuationOptions.LongRunning) > 0)
318 options |= TaskCreationOptions.LongRunning;
324 #region Internal and protected thingies
325 internal void Schedule ()
327 status = TaskStatus.WaitingToRun;
329 // If worker is null it means it is a local one, revert to the old behavior
330 if (childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
331 scheduler.AddWork (this);
333 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
334 * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
335 * the correct Thread during the creation
337 childWorkAdder (this);
344 TaskScheduler.Current = taskScheduler;
346 if (!token.IsCancellationRequested) {
348 status = TaskStatus.Running;
352 } catch (Exception e) {
353 exception = new AggregateException (e);
354 status = TaskStatus.Faulted;
355 if (taskScheduler.FireUnobservedEvent (exception).Observed)
356 exceptionObserved = true;
365 internal void Execute (Action<Task> childAdder)
367 childWorkAdder = childAdder;
371 internal void AddChild ()
373 childTasks.AddCount ();
376 internal void ChildCompleted ()
378 childTasks.Signal ();
379 if (childTasks.IsSet && status == TaskStatus.WaitingForChildrenToComplete) {
380 status = TaskStatus.RanToCompletion;
382 // Let continuation creation process
383 EventHandler tempCompleted = completed;
384 if (tempCompleted != null)
385 tempCompleted (this, EventArgs.Empty);
389 internal virtual void InnerInvoke ()
393 // Set action to null so that the GC can collect the delegate and thus
394 // any big object references that the user might have captured in an anonymous method
399 internal void Finish ()
401 // If there wasn't any child created in the task we set the CountdownEvent
402 childTasks.Signal ();
404 // Don't override Canceled or Faulted
405 if (status == TaskStatus.Running) {
406 if (childTasks.IsSet)
407 status = TaskStatus.RanToCompletion;
409 status = TaskStatus.WaitingForChildrenToComplete;
412 if (status != TaskStatus.WaitingForChildrenToComplete) {
413 // Let continuation creation process
414 EventHandler tempCompleted = completed;
415 if (tempCompleted != null)
416 tempCompleted (this, EventArgs.Empty);
419 // Reset the current thingies
421 TaskScheduler.Current = null;
423 // Tell parent that we are finished
424 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null){
425 parent.ChildCompleted ();
432 #region Cancel and Wait related method
434 internal void CancelReal ()
436 exception = new AggregateException (new TaskCanceledException (this));
437 status = TaskStatus.Canceled;
442 if (scheduler == null)
443 throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
445 scheduler.ParticipateUntil (this);
446 if (exception != null)
450 public void Wait (CancellationToken token)
455 public bool Wait (TimeSpan ts)
457 return Wait ((int)ts.TotalMilliseconds, CancellationToken.None);
460 public bool Wait (int millisecondsTimeout)
462 return Wait (millisecondsTimeout, CancellationToken.None);
465 public bool Wait (int millisecondsTimeout, CancellationToken token)
467 Watch sw = Watch.StartNew ();
468 return Wait (() => sw.ElapsedMilliseconds >= millisecondsTimeout, token);
471 bool Wait (Func<bool> stopFunc, CancellationToken token)
473 if (scheduler == null)
474 throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
476 bool result = scheduler.ParticipateUntil (this, delegate {
477 if (token.IsCancellationRequested)
478 throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
480 return (stopFunc != null) ? stopFunc () : false;
483 if (exception != null)
489 public static void WaitAll (params Task[] tasks)
492 throw new ArgumentNullException ("tasks");
493 if (tasks.Length == 0)
494 throw new ArgumentException ("tasks is empty", "tasks");
496 foreach (var t in tasks)
500 public static void WaitAll (Task[] tasks, CancellationToken token)
503 throw new ArgumentNullException ("tasks");
504 if (tasks.Length == 0)
505 throw new ArgumentException ("tasks is empty", "tasks");
507 foreach (var t in tasks)
511 public static bool WaitAll (Task[] tasks, TimeSpan ts)
514 throw new ArgumentNullException ("tasks");
515 if (tasks.Length == 0)
516 throw new ArgumentException ("tasks is empty", "tasks");
519 foreach (var t in tasks)
520 result &= t.Wait (ts);
524 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
527 throw new ArgumentNullException ("tasks");
528 if (tasks.Length == 0)
529 throw new ArgumentException ("tasks is empty", "tasks");
532 foreach (var t in tasks)
533 result &= t.Wait (millisecondsTimeout);
537 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken token)
540 throw new ArgumentNullException ("tasks");
541 if (tasks.Length == 0)
542 throw new ArgumentException ("tasks is empty", "tasks");
545 foreach (var t in tasks)
546 result &= t.Wait (millisecondsTimeout, token);
550 public static int WaitAny (params Task[] tasks)
552 return WaitAny (tasks, null, null);
555 static int WaitAny (Task[] tasks, Func<bool> stopFunc, CancellationToken? token)
558 throw new ArgumentNullException ("tasks");
559 if (tasks.Length == 0)
560 throw new ArgumentException ("tasks is empty", "tasks");
563 int indexFirstFinished = -1;
566 foreach (Task t in tasks) {
567 t.ContinueWith (delegate {
568 int indexResult = index;
569 int result = Interlocked.Increment (ref numFinished);
570 // Check if we are the first to have finished
572 indexFirstFinished = indexResult;
577 // One task already finished
578 if (indexFirstFinished != -1)
579 return indexFirstFinished;
581 // All tasks are supposed to use the same TaskManager
582 tasks[0].scheduler.ParticipateUntil (delegate {
583 if (stopFunc != null && stopFunc ())
586 if (token.HasValue && token.Value.IsCancellationRequested)
587 throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
589 return numFinished >= 1;
592 return indexFirstFinished;
595 public static int WaitAny (Task[] tasks, TimeSpan ts)
597 return WaitAny (tasks, (int)ts.TotalMilliseconds);
600 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
602 if (millisecondsTimeout < -1)
603 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
605 if (millisecondsTimeout == -1)
606 return WaitAny (tasks);
608 Watch sw = Watch.StartNew ();
609 return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, null);
612 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken token)
614 if (millisecondsTimeout < -1)
615 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
617 if (millisecondsTimeout == -1)
618 return WaitAny (tasks);
620 Watch sw = Watch.StartNew ();
621 return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, token);
624 public static int WaitAny (Task[] tasks, CancellationToken token)
626 return WaitAny (tasks, null, token);
631 public void Dispose ()
636 protected virtual void Dispose (bool disposeManagedRes)
638 // Set action to null so that the GC can collect the delegate and thus
639 // any big object references that the user might have captured in a anonymous method
640 if (disposeManagedRes) {
649 public static TaskFactory Factory {
651 return defaultFactory;
655 public static int? CurrentId {
658 return t == null ? (int?)null : t.Id;
662 public AggregateException Exception {
664 exceptionObserved = true;
673 public bool IsCanceled {
675 return status == TaskStatus.Canceled;
679 public bool IsCompleted {
681 return status == TaskStatus.RanToCompletion ||
682 status == TaskStatus.Canceled || status == TaskStatus.Faulted;
686 public bool IsFaulted {
688 return status == TaskStatus.Faulted;
692 public TaskCreationOptions CreationOptions {
694 return taskCreationOptions;
698 public TaskStatus Status {
707 public object AsyncState {
713 bool IAsyncResult.CompletedSynchronously {
719 WaitHandle IAsyncResult.AsyncWaitHandle {