disable broken tests on net_4_0
[mcs.git] / class / corlib / System.Threading.Tasks / Task.cs
blobacc2b5aa1fd00a12a83c3229be80236635ca751a
1 // Task.cs
2 //
3 // Copyright (c) 2008 Jérémie "Garuma" Laval
4 //
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
21 // THE SOFTWARE.
25 #if NET_4_0 || BOOTSTRAP_NET_4_0
27 using System;
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.
37 [System.ThreadStatic]
38 static Task current;
39 [System.ThreadStatic]
40 static Action<Task> childWorkAdder;
42 Task parent;
44 static int id = -1;
45 static TaskFactory defaultFactory = new TaskFactory ();
47 CountdownEvent childTasks = new CountdownEvent (1);
49 int taskId;
50 TaskCreationOptions taskCreationOptions;
52 IScheduler scheduler;
53 TaskScheduler taskScheduler;
55 volatile AggregateException exception;
56 volatile bool exceptionObserved;
57 volatile TaskStatus status;
59 Action<object> action;
60 object state;
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;
103 this.state = state;
104 this.taskId = Interlocked.Increment (ref id);
105 this.status = TaskStatus.Created;
106 this.token = token;
108 // Process taskCreationOptions
109 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent)) {
110 parent = current;
111 if (parent != null)
112 parent.AddChild ();
116 ~Task ()
118 if (exception != null && !exceptionObserved)
119 throw exception;
122 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
124 return (opt & member) == member;
127 static void EmptyFunc (object o)
131 #region Start
132 public void Start ()
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;
147 Schedule ();
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
164 ThreadStart ();
166 #endregion
168 #region ContinueWith
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);
193 return continuation;
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);
223 return t;
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 ();
247 return;
250 CheckAndSchedule (continuation, kind, scheduler, sender == null);
254 if (IsCompleted) {
255 action (null, EventArgs.Empty);
256 return;
259 completed += action;
261 // Retry in case completion was achieved but event adding was too late
262 if (IsCompleted)
263 action (null, EventArgs.Empty);
266 bool ContinuationStatusCheck (TaskContinuationOptions kind)
268 if (kind == TaskContinuationOptions.None)
269 return true;
271 int kindCode = (int)kind;
273 if (kindCode >= ((int)TaskContinuationOptions.NotOnRanToCompletion)) {
274 if (status == TaskStatus.Canceled) {
275 if (kind == TaskContinuationOptions.NotOnCanceled)
276 return false;
277 if (kind == TaskContinuationOptions.OnlyOnFaulted)
278 return false;
279 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
280 return false;
281 } else if (status == TaskStatus.Faulted) {
282 if (kind == TaskContinuationOptions.NotOnFaulted)
283 return false;
284 if (kind == TaskContinuationOptions.OnlyOnCanceled)
285 return false;
286 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
287 return false;
288 } else if (status == TaskStatus.RanToCompletion) {
289 if (kind == TaskContinuationOptions.NotOnRanToCompletion)
290 return false;
291 if (kind == TaskContinuationOptions.OnlyOnFaulted)
292 return false;
293 if (kind == TaskContinuationOptions.OnlyOnCanceled)
294 return false;
298 return true;
301 void CheckAndSchedule (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler, bool fromCaller)
303 if (!fromCaller
304 && (options == TaskContinuationOptions.None || (options & TaskContinuationOptions.ExecuteSynchronously) > 0))
305 continuation.ThreadStart ();
306 else
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;
320 return options;
322 #endregion
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);
332 } else {
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);
341 void ThreadStart ()
343 current = this;
344 TaskScheduler.Current = taskScheduler;
346 if (!token.IsCancellationRequested) {
348 status = TaskStatus.Running;
350 try {
351 InnerInvoke ();
352 } catch (Exception e) {
353 exception = new AggregateException (e);
354 status = TaskStatus.Faulted;
355 if (taskScheduler.FireUnobservedEvent (exception).Observed)
356 exceptionObserved = true;
358 } else {
359 CancelReal ();
362 Finish ();
365 internal void Execute (Action<Task> childAdder)
367 childWorkAdder = childAdder;
368 ThreadStart ();
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 ()
391 if (action != null)
392 action (state);
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
395 action = null;
396 state = null;
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;
408 else
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
420 current = null;
421 TaskScheduler.Current = null;
423 // Tell parent that we are finished
424 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null){
425 parent.ChildCompleted ();
428 Dispose ();
430 #endregion
432 #region Cancel and Wait related method
434 internal void CancelReal ()
436 exception = new AggregateException (new TaskCanceledException (this));
437 status = TaskStatus.Canceled;
440 public void Wait ()
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)
447 throw exception;
450 public void Wait (CancellationToken token)
452 Wait (null, 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)
484 throw exception;
486 return !result;
489 public static void WaitAll (params Task[] tasks)
491 if (tasks == null)
492 throw new ArgumentNullException ("tasks");
493 if (tasks.Length == 0)
494 throw new ArgumentException ("tasks is empty", "tasks");
496 foreach (var t in tasks)
497 t.Wait ();
500 public static void WaitAll (Task[] tasks, CancellationToken token)
502 if (tasks == null)
503 throw new ArgumentNullException ("tasks");
504 if (tasks.Length == 0)
505 throw new ArgumentException ("tasks is empty", "tasks");
507 foreach (var t in tasks)
508 t.Wait (token);
511 public static bool WaitAll (Task[] tasks, TimeSpan ts)
513 if (tasks == null)
514 throw new ArgumentNullException ("tasks");
515 if (tasks.Length == 0)
516 throw new ArgumentException ("tasks is empty", "tasks");
518 bool result = true;
519 foreach (var t in tasks)
520 result &= t.Wait (ts);
521 return result;
524 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
526 if (tasks == null)
527 throw new ArgumentNullException ("tasks");
528 if (tasks.Length == 0)
529 throw new ArgumentException ("tasks is empty", "tasks");
531 bool result = true;
532 foreach (var t in tasks)
533 result &= t.Wait (millisecondsTimeout);
534 return result;
537 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken token)
539 if (tasks == null)
540 throw new ArgumentNullException ("tasks");
541 if (tasks.Length == 0)
542 throw new ArgumentException ("tasks is empty", "tasks");
544 bool result = true;
545 foreach (var t in tasks)
546 result &= t.Wait (millisecondsTimeout, token);
547 return result;
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)
557 if (tasks == null)
558 throw new ArgumentNullException ("tasks");
559 if (tasks.Length == 0)
560 throw new ArgumentException ("tasks is empty", "tasks");
562 int numFinished = 0;
563 int indexFirstFinished = -1;
564 int index = 0;
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
571 if (result == 1)
572 indexFirstFinished = indexResult;
573 });
574 index++;
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 ())
584 return true;
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);
628 #endregion
630 #region Dispose
631 public void Dispose ()
633 Dispose (true);
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) {
641 action = null;
642 completed = null;
643 state = null;
646 #endregion
648 #region Properties
649 public static TaskFactory Factory {
650 get {
651 return defaultFactory;
655 public static int? CurrentId {
656 get {
657 Task t = current;
658 return t == null ? (int?)null : t.Id;
662 public AggregateException Exception {
663 get {
664 exceptionObserved = true;
666 return exception;
668 internal set {
669 exception = value;
673 public bool IsCanceled {
674 get {
675 return status == TaskStatus.Canceled;
679 public bool IsCompleted {
680 get {
681 return status == TaskStatus.RanToCompletion ||
682 status == TaskStatus.Canceled || status == TaskStatus.Faulted;
686 public bool IsFaulted {
687 get {
688 return status == TaskStatus.Faulted;
692 public TaskCreationOptions CreationOptions {
693 get {
694 return taskCreationOptions;
698 public TaskStatus Status {
699 get {
700 return status;
702 internal set {
703 status = value;
707 public object AsyncState {
708 get {
709 return state;
713 bool IAsyncResult.CompletedSynchronously {
714 get {
715 return true;
719 WaitHandle IAsyncResult.AsyncWaitHandle {
720 get {
721 return null;
725 public int Id {
726 get {
727 return taskId;
730 #endregion
733 #endif