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
{