2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / corlib / System.Threading.Tasks / Task.cs
blobae6fb42636341011ffcd846da9bf6fc74f7384b7
1 #if NET_4_0
2 // Task.cs
3 //
4 // Copyright (c) 2008 Jérémie "Garuma" Laval
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 // THE SOFTWARE.
26 using System;
27 using System.Threading;
28 using System.Collections.Concurrent;
30 namespace System.Threading.Tasks
32 public class Task : IDisposable, IAsyncResult
34 // With this attribute each thread has its own value so that it's correct for our Schedule code
35 // and for Parent property.
36 [System.ThreadStatic]
37 static Task current;
38 [System.ThreadStatic]
39 static Action<Task> childWorkAdder;
41 Task parent;
43 static int id = -1;
44 static TaskFactory defaultFactory = new TaskFactory ();
46 CountdownEvent childTasks = new CountdownEvent (1);
48 int taskId;
49 TaskCreationOptions taskCreationOptions;
51 IScheduler scheduler;
52 TaskScheduler taskScheduler;
54 volatile AggregateException exception;
55 volatile bool exceptionObserved;
56 volatile TaskStatus status;
58 Action<object> action;
59 object state;
60 EventHandler completed;
62 CancellationToken token;
64 public Task (Action action) : this (action, TaskCreationOptions.None)
69 public Task (Action action, TaskCreationOptions options) : this (action, CancellationToken.None, options)
74 public Task (Action action, CancellationToken token) : this (action, token, TaskCreationOptions.None)
79 public Task (Action action, CancellationToken token, TaskCreationOptions options)
80 : this ((o) => { if (action != null) action (); }, null, token, options)
84 public Task (Action<object> action, object state) : this (action, state, TaskCreationOptions.None)
88 public Task (Action<object> action, object state, TaskCreationOptions options)
89 : this (action, state, CancellationToken.None, options)
93 public Task (Action<object> action, object state, CancellationToken token)
94 : this (action, state, token, TaskCreationOptions.None)
98 public Task (Action<object> action, object state, CancellationToken token, TaskCreationOptions options)
100 this.taskCreationOptions = options;
101 this.action = action == null ? EmptyFunc : action;
102 this.state = state;
103 this.taskId = Interlocked.Increment (ref id);
104 this.status = TaskStatus.Created;
105 this.token = token;
107 // Process taskCreationOptions
108 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent)) {
109 parent = current;
110 if (parent != null)
111 parent.AddChild ();
115 ~Task ()
117 if (exception != null && !exceptionObserved)
118 throw exception;
121 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
123 return (opt & member) == member;
126 static void EmptyFunc (object o)
130 #region Start
131 public void Start ()
133 Start (TaskScheduler.Current);
136 public void Start (TaskScheduler tscheduler)
138 this.taskScheduler = tscheduler;
139 Start (ProxifyScheduler (tscheduler));
142 void Start (IScheduler scheduler)
144 this.scheduler = scheduler;
145 status = TaskStatus.WaitingForActivation;
146 Schedule ();
149 IScheduler ProxifyScheduler (TaskScheduler tscheduler)
151 IScheduler sched = tscheduler as IScheduler;
152 return sched != null ? sched : new SchedulerProxy (tscheduler);
155 public void RunSynchronously ()
157 RunSynchronously (TaskScheduler.Current);
160 public void RunSynchronously (TaskScheduler tscheduler)
162 // Adopt this scheme for the moment
163 ThreadStart ();
165 #endregion
167 #region ContinueWith
168 public Task ContinueWith (Action<Task> a)
170 return ContinueWith (a, TaskContinuationOptions.None);
173 public Task ContinueWith (Action<Task> a, TaskContinuationOptions kind)
175 return ContinueWith (a, CancellationToken.None, kind, TaskScheduler.Current);
178 public Task ContinueWith (Action<Task> a, CancellationToken token)
180 return ContinueWith (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
183 public Task ContinueWith (Action<Task> a, TaskScheduler scheduler)
185 return ContinueWith (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
188 public Task ContinueWith (Action<Task> a, CancellationToken token, TaskContinuationOptions kind, TaskScheduler scheduler)
190 Task continuation = new Task ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
191 ContinueWithCore (continuation, kind, scheduler);
192 return continuation;
195 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a)
197 return ContinueWith<TResult> (a, TaskContinuationOptions.None);
200 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskContinuationOptions options)
202 return ContinueWith<TResult> (a, CancellationToken.None, options, TaskScheduler.Current);
205 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token)
207 return ContinueWith<TResult> (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
210 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskScheduler scheduler)
212 return ContinueWith<TResult> (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
215 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token,
216 TaskContinuationOptions kind, TaskScheduler scheduler)
218 Task<TResult> t = new Task<TResult> ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
220 ContinueWithCore (t, kind, scheduler);
222 return t;
225 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind, TaskScheduler scheduler)
227 ContinueWithCore (continuation, kind, scheduler, () => true);
230 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind,
231 TaskScheduler scheduler, Func<bool> predicate)
233 // Already set the scheduler so that user can call Wait and that sort of stuff
234 continuation.taskScheduler = scheduler;
235 continuation.scheduler = ProxifyScheduler (scheduler);
237 AtomicBoolean launched = new AtomicBoolean ();
238 EventHandler action = delegate (object sender, EventArgs e) {
239 if (!predicate ()) return;
241 if (!launched.Value && launched.TrySet ()) {
242 if (!ContinuationStatusCheck (kind)) {
243 continuation.CancelReal ();
244 continuation.Dispose ();
246 return;
249 CheckAndSchedule (continuation, kind, scheduler, sender == null);
253 if (IsCompleted) {
254 action (null, EventArgs.Empty);
255 return;
258 completed += action;
260 // Retry in case completion was achieved but event adding was too late
261 if (IsCompleted)
262 action (null, EventArgs.Empty);
265 bool ContinuationStatusCheck (TaskContinuationOptions kind)
267 if (kind == TaskContinuationOptions.None)
268 return true;
270 int kindCode = (int)kind;
272 if (kindCode >= ((int)TaskContinuationOptions.NotOnRanToCompletion)) {
273 if (status == TaskStatus.Canceled) {
274 if (kind == TaskContinuationOptions.NotOnCanceled)
275 return false;
276 if (kind == TaskContinuationOptions.OnlyOnFaulted)
277 return false;
278 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
279 return false;
280 } else if (status == TaskStatus.Faulted) {
281 if (kind == TaskContinuationOptions.NotOnFaulted)
282 return false;
283 if (kind == TaskContinuationOptions.OnlyOnCanceled)
284 return false;
285 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
286 return false;
287 } else if (status == TaskStatus.RanToCompletion) {
288 if (kind == TaskContinuationOptions.NotOnRanToCompletion)
289 return false;
290 if (kind == TaskContinuationOptions.OnlyOnFaulted)
291 return false;
292 if (kind == TaskContinuationOptions.OnlyOnCanceled)
293 return false;
297 return true;
300 void CheckAndSchedule (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler, bool fromCaller)
302 if (!fromCaller
303 && (options == TaskContinuationOptions.None || (options & TaskContinuationOptions.ExecuteSynchronously) > 0))
304 continuation.ThreadStart ();
305 else
306 continuation.Start (scheduler);
309 internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
311 TaskCreationOptions options = TaskCreationOptions.None;
312 if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
313 options |= TaskCreationOptions.AttachedToParent;
314 if ((kind & TaskContinuationOptions.PreferFairness) > 0)
315 options |= TaskCreationOptions.PreferFairness;
316 if ((kind & TaskContinuationOptions.LongRunning) > 0)
317 options |= TaskCreationOptions.LongRunning;
319 return options;
321 #endregion
323 #region Internal and protected thingies
324 internal void Schedule ()
326 status = TaskStatus.WaitingToRun;
328 // If worker is null it means it is a local one, revert to the old behavior
329 if (childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
330 scheduler.AddWork (this);
331 } else {
332 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
333 * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
334 * the correct Thread during the creation
336 childWorkAdder (this);
340 void ThreadStart ()
342 current = this;
343 TaskScheduler.Current = taskScheduler;
345 if (!token.IsCancellationRequested) {
347 status = TaskStatus.Running;
349 try {
350 InnerInvoke ();
351 } catch (Exception e) {
352 exception = new AggregateException (e);
353 status = TaskStatus.Faulted;
354 if (taskScheduler.FireUnobservedEvent (exception).Observed)
355 exceptionObserved = true;
357 } else {
358 CancelReal ();
361 Finish ();
364 internal void Execute (Action<Task> childAdder)
366 childWorkAdder = childAdder;
367 ThreadStart ();
370 internal void AddChild ()
372 childTasks.AddCount ();
375 internal void ChildCompleted ()
377 childTasks.Signal ();
378 if (childTasks.IsSet && status == TaskStatus.WaitingForChildrenToComplete) {
379 status = TaskStatus.RanToCompletion;
381 // Let continuation creation process
382 EventHandler tempCompleted = completed;
383 if (tempCompleted != null)
384 tempCompleted (this, EventArgs.Empty);
388 internal virtual void InnerInvoke ()
390 if (action != null)
391 action (state);
392 // Set action to null so that the GC can collect the delegate and thus
393 // any big object references that the user might have captured in an anonymous method
394 action = null;
395 state = null;
398 internal void Finish ()
400 // If there wasn't any child created in the task we set the CountdownEvent
401 childTasks.Signal ();
403 // Don't override Canceled or Faulted
404 if (status == TaskStatus.Running) {
405 if (childTasks.IsSet)
406 status = TaskStatus.RanToCompletion;
407 else
408 status = TaskStatus.WaitingForChildrenToComplete;
411 if (status != TaskStatus.WaitingForChildrenToComplete) {
412 // Let continuation creation process
413 EventHandler tempCompleted = completed;
414 if (tempCompleted != null)
415 tempCompleted (this, EventArgs.Empty);
418 // Reset the current thingies
419 current = null;
420 TaskScheduler.Current = null;
422 // Tell parent that we are finished
423 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null){
424 parent.ChildCompleted ();
427 Dispose ();
429 #endregion
431 #region Cancel and Wait related method
433 internal void CancelReal ()
435 exception = new AggregateException (new TaskCanceledException (this));
436 status = TaskStatus.Canceled;
439 public void Wait ()
441 if (scheduler == null)
442 throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
444 scheduler.ParticipateUntil (this);
445 if (exception != null)
446 throw exception;
449 public void Wait (CancellationToken token)
451 Wait (null, token);
454 public bool Wait (TimeSpan ts)
456 return Wait ((int)ts.TotalMilliseconds, CancellationToken.None);
459 public bool Wait (int millisecondsTimeout)
461 return Wait (millisecondsTimeout, CancellationToken.None);
464 public bool Wait (int millisecondsTimeout, CancellationToken token)
466 Watch sw = Watch.StartNew ();
467 return Wait (() => sw.ElapsedMilliseconds >= millisecondsTimeout, token);
470 bool Wait (Func<bool> stopFunc, CancellationToken token)
472 if (scheduler == null)
473 throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
475 bool result = scheduler.ParticipateUntil (this, delegate {
476 if (token.IsCancellationRequested)
477 throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
479 return (stopFunc != null) ? stopFunc () : false;
482 if (exception != null)
483 throw exception;
485 return !result;
488 public static void WaitAll (params Task[] tasks)
490 if (tasks == null)
491 throw new ArgumentNullException ("tasks");
492 if (tasks.Length == 0)
493 throw new ArgumentException ("tasks is empty", "tasks");
495 foreach (var t in tasks)
496 t.Wait ();
499 public static void WaitAll (Task[] tasks, CancellationToken token)
501 if (tasks == null)
502 throw new ArgumentNullException ("tasks");
503 if (tasks.Length == 0)
504 throw new ArgumentException ("tasks is empty", "tasks");
506 foreach (var t in tasks)
507 t.Wait (token);
510 public static bool WaitAll (Task[] tasks, TimeSpan ts)
512 if (tasks == null)
513 throw new ArgumentNullException ("tasks");
514 if (tasks.Length == 0)
515 throw new ArgumentException ("tasks is empty", "tasks");
517 bool result = true;
518 foreach (var t in tasks)
519 result &= t.Wait (ts);
520 return result;
523 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
525 if (tasks == null)
526 throw new ArgumentNullException ("tasks");
527 if (tasks.Length == 0)
528 throw new ArgumentException ("tasks is empty", "tasks");
530 bool result = true;
531 foreach (var t in tasks)
532 result &= t.Wait (millisecondsTimeout);
533 return result;
536 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken token)
538 if (tasks == null)
539 throw new ArgumentNullException ("tasks");
540 if (tasks.Length == 0)
541 throw new ArgumentException ("tasks is empty", "tasks");
543 bool result = true;
544 foreach (var t in tasks)
545 result &= t.Wait (millisecondsTimeout, token);
546 return result;
549 public static int WaitAny (params Task[] tasks)
551 return WaitAny (tasks, null, null);
554 static int WaitAny (Task[] tasks, Func<bool> stopFunc, CancellationToken? token)
556 if (tasks == null)
557 throw new ArgumentNullException ("tasks");
558 if (tasks.Length == 0)
559 throw new ArgumentException ("tasks is empty", "tasks");
561 int numFinished = 0;
562 int indexFirstFinished = -1;
563 int index = 0;
565 foreach (Task t in tasks) {
566 t.ContinueWith (delegate {
567 int indexResult = index;
568 int result = Interlocked.Increment (ref numFinished);
569 // Check if we are the first to have finished
570 if (result == 1)
571 indexFirstFinished = indexResult;
572 });
573 index++;
576 // One task already finished
577 if (indexFirstFinished != -1)
578 return indexFirstFinished;
580 // All tasks are supposed to use the same TaskManager
581 tasks[0].scheduler.ParticipateUntil (delegate {
582 if (stopFunc != null && stopFunc ())
583 return true;
585 if (token.HasValue && token.Value.IsCancellationRequested)
586 throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
588 return numFinished >= 1;
591 return indexFirstFinished;
594 public static int WaitAny (Task[] tasks, TimeSpan ts)
596 return WaitAny (tasks, (int)ts.TotalMilliseconds);
599 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
601 if (millisecondsTimeout < -1)
602 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
604 if (millisecondsTimeout == -1)
605 return WaitAny (tasks);
607 Watch sw = Watch.StartNew ();
608 return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, null);
611 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken token)
613 if (millisecondsTimeout < -1)
614 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
616 if (millisecondsTimeout == -1)
617 return WaitAny (tasks);
619 Watch sw = Watch.StartNew ();
620 return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, token);
623 public static int WaitAny (Task[] tasks, CancellationToken token)
625 return WaitAny (tasks, null, token);
627 #endregion
629 #region Dispose
630 public void Dispose ()
632 Dispose (true);
635 protected virtual void Dispose (bool disposeManagedRes)
637 // Set action to null so that the GC can collect the delegate and thus
638 // any big object references that the user might have captured in a anonymous method
639 if (disposeManagedRes) {
640 action = null;
641 completed = null;
642 state = null;
645 #endregion
647 #region Properties
648 public static TaskFactory Factory {
649 get {
650 return defaultFactory;
654 public static int? CurrentId {
655 get {
656 Task t = current;
657 return t == null ? (int?)null : t.Id;
661 public AggregateException Exception {
662 get {
663 exceptionObserved = true;
665 return exception;
667 internal set {
668 exception = value;
672 public bool IsCanceled {
673 get {
674 return status == TaskStatus.Canceled;
678 public bool IsCompleted {
679 get {
680 return status == TaskStatus.RanToCompletion ||
681 status == TaskStatus.Canceled || status == TaskStatus.Faulted;
685 public bool IsFaulted {
686 get {
687 return status == TaskStatus.Faulted;
691 public TaskCreationOptions CreationOptions {
692 get {
693 return taskCreationOptions;
697 public TaskStatus Status {
698 get {
699 return status;
701 internal set {
702 status = value;
706 public object AsyncState {
707 get {
708 return state;
712 bool IAsyncResult.CompletedSynchronously {
713 get {
714 return true;
718 WaitHandle IAsyncResult.AsyncWaitHandle {
719 get {
720 return null;
724 public int Id {
725 get {
726 return taskId;
729 #endregion
732 #endif