Tiny style fix
[mono-project.git] / mcs / class / corlib / System.Threading.Tasks / Task.cs
blob38de68c1521f5ec1a603ff5caa23f238b9c55ad3
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 || MOBILE
27 using System;
28 using System.Threading;
29 using System.Collections.Concurrent;
31 namespace System.Threading.Tasks
33 [System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}")]
34 [System.Diagnostics.DebuggerTypeProxy ("System.Threading.Tasks.SystemThreadingTasks_TaskDebugView")]
35 public class Task : IDisposable, IAsyncResult
37 // With this attribute each thread has its own value so that it's correct for our Schedule code
38 // and for Parent property.
39 [System.ThreadStatic]
40 static Task current;
41 [System.ThreadStatic]
42 static Action<Task> childWorkAdder;
44 Task parent;
46 static int id = -1;
47 static TaskFactory defaultFactory = new TaskFactory ();
49 CountdownEvent childTasks = new CountdownEvent (1);
51 int taskId;
52 TaskCreationOptions taskCreationOptions;
54 TaskScheduler scheduler;
56 ManualResetEventSlim schedWait = new ManualResetEventSlim (false);
58 volatile AggregateException exception;
59 volatile bool exceptionObserved;
61 TaskStatus status;
63 Action<object> action;
64 Action simpleAction;
65 object state;
66 AtomicBooleanValue executing;
68 ConcurrentQueue<EventHandler> completed;
70 CancellationToken token;
72 public Task (Action action) : this (action, TaskCreationOptions.None)
77 public Task (Action action, TaskCreationOptions creationOptions) : this (action, CancellationToken.None, creationOptions)
82 public Task (Action action, CancellationToken cancellationToken) : this (action, cancellationToken, TaskCreationOptions.None)
87 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
88 : this (null, null, cancellationToken, creationOptions)
90 this.simpleAction = action;
93 public Task (Action<object> action, object state) : this (action, state, TaskCreationOptions.None)
97 public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
98 : this (action, state, CancellationToken.None, creationOptions)
102 public Task (Action<object> action, object state, CancellationToken cancellationToken)
103 : this (action, state, cancellationToken, TaskCreationOptions.None)
107 public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
108 : this (action, state, cancellationToken, creationOptions, current)
113 internal Task (Action<object> action,
114 object state,
115 CancellationToken cancellationToken,
116 TaskCreationOptions creationOptions,
117 Task parent)
119 this.taskCreationOptions = creationOptions;
120 this.action = action;
121 this.state = state;
122 this.taskId = Interlocked.Increment (ref id);
123 this.status = TaskStatus.Created;
124 this.token = cancellationToken;
125 this.parent = parent;
127 // Process taskCreationOptions
128 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
129 parent.AddChild ();
132 ~Task ()
134 if (exception != null && !exceptionObserved)
135 throw exception;
138 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
140 return (opt & member) == member;
143 #region Start
144 public void Start ()
146 Start (TaskScheduler.Current);
149 public void Start (TaskScheduler scheduler)
151 SetupScheduler (scheduler);
152 Schedule ();
155 internal void SetupScheduler (TaskScheduler scheduler)
157 this.scheduler = scheduler;
158 status = TaskStatus.WaitingForActivation;
159 schedWait.Set ();
162 public void RunSynchronously ()
164 RunSynchronously (TaskScheduler.Current);
167 public void RunSynchronously (TaskScheduler scheduler)
169 if (this.Status != TaskStatus.Created)
170 throw new InvalidOperationException ("The task is not in a valid state to be started");
171 if (scheduler.TryExecuteTask (this))
172 return;
174 Start (scheduler);
175 Wait ();
177 #endregion
179 #region ContinueWith
180 public Task ContinueWith (Action<Task> continuationAction)
182 return ContinueWith (continuationAction, TaskContinuationOptions.None);
185 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
187 return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
190 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
192 return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
195 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
197 return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
200 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
202 Task continuation = new Task ((o) => continuationAction ((Task)o),
203 this,
204 cancellationToken,
205 GetCreationOptions (continuationOptions),
206 this);
207 ContinueWithCore (continuation, continuationOptions, scheduler);
209 return continuation;
212 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
214 return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
217 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
219 return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
222 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
224 return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
227 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
229 return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
232 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
233 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
235 if (continuationFunction == null)
236 throw new ArgumentNullException ("continuationFunction");
237 if (scheduler == null)
238 throw new ArgumentNullException ("scheduler");
240 Task<TResult> t = new Task<TResult> ((o) => continuationFunction ((Task)o),
241 this,
242 cancellationToken,
243 GetCreationOptions (continuationOptions),
244 this);
246 ContinueWithCore (t, continuationOptions, scheduler);
248 return t;
251 internal void ContinueWithCore (Task continuation, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
253 ContinueWithCore (continuation, continuationOptions, scheduler, () => true);
256 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind,
257 TaskScheduler scheduler, Func<bool> predicate)
259 // Already set the scheduler so that user can call Wait and that sort of stuff
260 continuation.scheduler = scheduler;
261 continuation.schedWait.Set ();
262 continuation.status = TaskStatus.WaitingForActivation;
264 AtomicBoolean launched = new AtomicBoolean ();
265 EventHandler action = delegate (object sender, EventArgs e) {
266 if (launched.TryRelaxedSet ()) {
267 if (!predicate ())
268 return;
270 if (!ContinuationStatusCheck (kind)) {
271 continuation.CancelReal ();
272 continuation.Dispose ();
274 return;
277 CheckAndSchedule (continuation, kind, scheduler, sender == null);
281 if (IsCompleted) {
282 action (null, EventArgs.Empty);
283 return;
286 if (completed == null)
287 Interlocked.CompareExchange (ref completed, new ConcurrentQueue<EventHandler> (), null);
288 completed.Enqueue (action);
290 // Retry in case completion was achieved but event adding was too late
291 if (IsCompleted)
292 action (null, EventArgs.Empty);
296 bool ContinuationStatusCheck (TaskContinuationOptions kind)
298 if (kind == TaskContinuationOptions.None)
299 return true;
301 int kindCode = (int)kind;
303 if (kindCode >= ((int)TaskContinuationOptions.NotOnRanToCompletion)) {
304 if (status == TaskStatus.Canceled) {
305 if ((kind & TaskContinuationOptions.NotOnCanceled) > 0)
306 return false;
307 if ((kind & TaskContinuationOptions.OnlyOnFaulted) > 0)
308 return false;
309 if ((kind & TaskContinuationOptions.OnlyOnRanToCompletion) > 0)
310 return false;
311 } else if (status == TaskStatus.Faulted) {
312 if ((kind & TaskContinuationOptions.NotOnFaulted) > 0)
313 return false;
314 if ((kind & TaskContinuationOptions.OnlyOnCanceled) > 0)
315 return false;
316 if ((kind & TaskContinuationOptions.OnlyOnRanToCompletion) > 0)
317 return false;
318 } else if (status == TaskStatus.RanToCompletion) {
319 if ((kind & TaskContinuationOptions.NotOnRanToCompletion) > 0)
320 return false;
321 if ((kind & TaskContinuationOptions.OnlyOnFaulted) > 0)
322 return false;
323 if ((kind & TaskContinuationOptions.OnlyOnCanceled) > 0)
324 return false;
328 return true;
331 void CheckAndSchedule (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler, bool fromCaller)
333 if ((options & TaskContinuationOptions.ExecuteSynchronously) > 0)
334 continuation.ThreadStart ();
335 else
336 continuation.Start (scheduler);
339 internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
341 TaskCreationOptions options = TaskCreationOptions.None;
342 if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
343 options |= TaskCreationOptions.AttachedToParent;
344 if ((kind & TaskContinuationOptions.PreferFairness) > 0)
345 options |= TaskCreationOptions.PreferFairness;
346 if ((kind & TaskContinuationOptions.LongRunning) > 0)
347 options |= TaskCreationOptions.LongRunning;
349 return options;
351 #endregion
353 #region Internal and protected thingies
354 internal void Schedule ()
356 status = TaskStatus.WaitingToRun;
358 // If worker is null it means it is a local one, revert to the old behavior
359 // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
360 if (scheduler != TaskScheduler.Current || childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
361 scheduler.QueueTask (this);
362 } else {
363 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
364 * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
365 * the correct Thread during the creation
367 childWorkAdder (this);
371 void ThreadStart ()
373 /* Allow scheduler to break fairness of deque ordering without
374 * breaking its semantic (the task can be executed twice but the
375 * second time it will return immediately
377 if (!executing.TryRelaxedSet ())
378 return;
380 current = this;
381 TaskScheduler.Current = scheduler;
383 if (!token.IsCancellationRequested) {
385 status = TaskStatus.Running;
387 try {
388 InnerInvoke ();
389 } catch (OperationCanceledException oce) {
390 if (oce.CancellationToken == token)
391 CancelReal ();
392 else
393 HandleGenericException (oce);
394 } catch (Exception e) {
395 HandleGenericException (e);
397 } else {
398 CancelReal ();
401 Finish ();
404 internal void Execute (Action<Task> childAdder)
406 childWorkAdder = childAdder;
407 ThreadStart ();
410 internal void AddChild ()
412 childTasks.AddCount ();
415 internal void ChildCompleted ()
417 if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
418 status = TaskStatus.RanToCompletion;
420 ProcessCompleteDelegates ();
424 internal virtual void InnerInvoke ()
426 if (action == null && simpleAction != null)
427 simpleAction ();
428 else if (action != null)
429 action (state);
430 // Set action to null so that the GC can collect the delegate and thus
431 // any big object references that the user might have captured in an anonymous method
432 action = null;
433 simpleAction = null;
434 state = null;
437 internal void Finish ()
439 // If there wasn't any child created in the task we set the CountdownEvent
440 childTasks.Signal ();
442 // Don't override Canceled or Faulted
443 if (status == TaskStatus.Running) {
444 if (childTasks.IsSet)
445 status = TaskStatus.RanToCompletion;
446 else
447 status = TaskStatus.WaitingForChildrenToComplete;
450 if (status != TaskStatus.WaitingForChildrenToComplete)
451 ProcessCompleteDelegates ();
453 // Reset the current thingies
454 current = null;
455 TaskScheduler.Current = null;
457 // Tell parent that we are finished
458 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null) {
459 parent.ChildCompleted ();
463 void ProcessCompleteDelegates ()
465 if (completed == null)
466 return;
468 EventHandler handler;
469 while (completed.TryDequeue (out handler))
470 handler (this, EventArgs.Empty);
472 #endregion
474 #region Cancel and Wait related method
476 internal void CancelReal ()
478 status = TaskStatus.Canceled;
481 internal void HandleGenericException (Exception e)
483 HandleGenericException (new AggregateException (e));
486 internal void HandleGenericException (AggregateException e)
488 exception = e;
489 status = TaskStatus.Faulted;
490 if (scheduler != null && scheduler.FireUnobservedEvent (exception).Observed)
491 exceptionObserved = true;
494 public void Wait ()
496 if (scheduler == null)
497 schedWait.Wait ();
499 if (!IsCompleted)
500 scheduler.ParticipateUntil (this);
501 if (exception != null)
502 throw exception;
503 if (IsCanceled)
504 throw new AggregateException (new TaskCanceledException (this));
507 public void Wait (CancellationToken cancellationToken)
509 Wait (-1, cancellationToken);
512 public bool Wait (TimeSpan timeout)
514 return Wait (CheckTimeout (timeout), CancellationToken.None);
517 public bool Wait (int millisecondsTimeout)
519 return Wait (millisecondsTimeout, CancellationToken.None);
522 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
524 if (millisecondsTimeout < -1)
525 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
527 if (millisecondsTimeout == -1 && token == CancellationToken.None) {
528 Wait ();
529 return true;
532 Watch watch = Watch.StartNew ();
534 if (scheduler == null) {
535 schedWait.Wait (millisecondsTimeout, cancellationToken);
536 millisecondsTimeout = ComputeTimeout (millisecondsTimeout, watch);
539 ManualResetEventSlim predicateEvt = new ManualResetEventSlim (false);
540 if (cancellationToken != CancellationToken.None) {
541 cancellationToken.Register (predicateEvt.Set);
542 cancellationToken.ThrowIfCancellationRequested ();
545 bool result = scheduler.ParticipateUntil (this, predicateEvt, millisecondsTimeout);
547 if (exception != null)
548 throw exception;
549 if (IsCanceled)
550 throw new AggregateException (new TaskCanceledException (this));
552 return !result;
555 public static void WaitAll (params Task[] tasks)
557 if (tasks == null)
558 throw new ArgumentNullException ("tasks");
560 foreach (var t in tasks) {
561 if (t == null)
562 throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
563 t.Wait ();
567 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
569 if (tasks == null)
570 throw new ArgumentNullException ("tasks");
572 foreach (var t in tasks) {
573 if (t == null)
574 throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
576 t.Wait (cancellationToken);
580 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
582 if (tasks == null)
583 throw new ArgumentNullException ("tasks");
585 bool result = true;
586 foreach (var t in tasks) {
587 if (t == null)
588 throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
590 result &= t.Wait (timeout);
592 return result;
595 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
597 if (tasks == null)
598 throw new ArgumentNullException ("tasks");
600 bool result = true;
601 foreach (var t in tasks) {
602 if (t == null)
603 throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
605 result &= t.Wait (millisecondsTimeout);
607 return result;
610 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
612 if (tasks == null)
613 throw new ArgumentNullException ("tasks");
615 bool result = true;
616 foreach (var t in tasks) {
617 if (t == null)
618 throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
620 result &= t.Wait (millisecondsTimeout, cancellationToken);
622 return result;
625 public static int WaitAny (params Task[] tasks)
627 return WaitAny (tasks, -1, CancellationToken.None);
630 public static int WaitAny (Task[] tasks, TimeSpan timeout)
632 return WaitAny (tasks, CheckTimeout (timeout));
635 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
637 if (millisecondsTimeout < -1)
638 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
640 if (millisecondsTimeout == -1)
641 return WaitAny (tasks);
643 return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
646 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
648 return WaitAny (tasks, -1, cancellationToken);
651 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
653 if (tasks == null)
654 throw new ArgumentNullException ("tasks");
655 if (tasks.Length == 0)
656 throw new ArgumentException ("tasks is empty", "tasks");
657 if (tasks.Length == 1) {
658 tasks[0].Wait (millisecondsTimeout, cancellationToken);
659 return 0;
662 int numFinished = 0;
663 int indexFirstFinished = -1;
664 int index = 0;
665 TaskScheduler sched = null;
666 Task task = null;
667 Watch watch = Watch.StartNew ();
668 ManualResetEventSlim predicateEvt = new ManualResetEventSlim (false);
670 foreach (Task t in tasks) {
671 int indexResult = index++;
672 t.ContinueWith (delegate {
673 if (numFinished >= 1)
674 return;
675 int result = Interlocked.Increment (ref numFinished);
677 // Check if we are the first to have finished
678 if (result == 1)
679 indexFirstFinished = indexResult;
681 // Stop waiting
682 predicateEvt.Set ();
683 }, TaskContinuationOptions.ExecuteSynchronously);
685 if (sched == null && t.scheduler != null) {
686 task = t;
687 sched = t.scheduler;
691 // If none of task have a scheduler we are forced to wait for at least one to start
692 if (sched == null) {
693 var handles = Array.ConvertAll (tasks, t => t.schedWait.WaitHandle);
694 int shandle = -1;
695 if ((shandle = WaitHandle.WaitAny (handles, millisecondsTimeout)) == WaitHandle.WaitTimeout)
696 return -1;
697 sched = tasks[shandle].scheduler;
698 task = tasks[shandle];
699 millisecondsTimeout = ComputeTimeout (millisecondsTimeout, watch);
702 // One task already finished
703 if (indexFirstFinished != -1)
704 return indexFirstFinished;
706 if (cancellationToken != CancellationToken.None) {
707 cancellationToken.Register (predicateEvt.Set);
708 cancellationToken.ThrowIfCancellationRequested ();
711 sched.ParticipateUntil (task, predicateEvt, millisecondsTimeout);
713 // Index update is still not done
714 if (indexFirstFinished == -1) {
715 SpinWait wait = new SpinWait ();
716 while (indexFirstFinished == -1)
717 wait.SpinOnce ();
720 return indexFirstFinished;
723 static int CheckTimeout (TimeSpan timeout)
725 try {
726 return checked ((int)timeout.TotalMilliseconds);
727 } catch (System.OverflowException) {
728 throw new ArgumentOutOfRangeException ("timeout");
732 static int ComputeTimeout (int millisecondsTimeout, Watch watch)
734 return millisecondsTimeout == -1 ? -1 : (int)Math.Max (watch.ElapsedMilliseconds - millisecondsTimeout, 1);
737 #endregion
739 #region Dispose
740 public void Dispose ()
742 Dispose (true);
745 protected virtual void Dispose (bool disposing)
747 // Set action to null so that the GC can collect the delegate and thus
748 // any big object references that the user might have captured in a anonymous method
749 if (disposing) {
750 action = null;
751 state = null;
754 #endregion
756 #region Properties
757 public static TaskFactory Factory {
758 get {
759 return defaultFactory;
763 public static int? CurrentId {
764 get {
765 Task t = current;
766 return t == null ? (int?)null : t.Id;
770 public AggregateException Exception {
771 get {
772 exceptionObserved = true;
774 return exception;
776 internal set {
777 exception = value;
781 public bool IsCanceled {
782 get {
783 return status == TaskStatus.Canceled;
787 public bool IsCompleted {
788 get {
789 return status == TaskStatus.RanToCompletion ||
790 status == TaskStatus.Canceled || status == TaskStatus.Faulted;
794 public bool IsFaulted {
795 get {
796 return status == TaskStatus.Faulted;
800 public TaskCreationOptions CreationOptions {
801 get {
802 return taskCreationOptions;
806 public TaskStatus Status {
807 get {
808 return status;
810 internal set {
811 status = value;
815 public object AsyncState {
816 get {
817 return state;
821 bool IAsyncResult.CompletedSynchronously {
822 get {
823 return true;
827 WaitHandle IAsyncResult.AsyncWaitHandle {
828 get {
829 return null;
833 public int Id {
834 get {
835 return taskId;
839 internal Task Parent {
840 get {
841 return parent;
844 #endregion
847 #endif