| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 |
- #if NET_4_0
- // Task.cs
- //
- // Copyright (c) 2008 Jérémie "Garuma" Laval
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- //
- using System;
- using System.Threading;
- using System.Collections.Concurrent;
- namespace System.Threading.Tasks
- {
- public class Task : IDisposable, IAsyncResult
- {
- // With this attribute each thread has its own value so that it's correct for our Schedule code
- // and for Parent property.
- [System.ThreadStatic]
- static Task current;
- [System.ThreadStatic]
- static Action<Task> childWorkAdder;
-
- Task parent;
-
- static int id = -1;
- static TaskFactory defaultFactory = new TaskFactory ();
-
- CountdownEvent childTasks = new CountdownEvent (1);
-
- int taskId;
- TaskCreationOptions taskCreationOptions;
-
- IScheduler scheduler;
- TaskScheduler taskScheduler;
-
- volatile AggregateException exception;
- volatile bool exceptionObserved;
- volatile TaskStatus status;
-
- Action<object> action;
- object state;
- EventHandler completed;
-
- CancellationToken token;
-
- public Task (Action action) : this (action, TaskCreationOptions.None)
- {
- }
-
- public Task (Action action, TaskCreationOptions options) : this (action, CancellationToken.None, options)
- {
- }
-
- public Task (Action action, CancellationToken token) : this (action, token, TaskCreationOptions.None)
- {
- }
-
- public Task (Action action, CancellationToken token, TaskCreationOptions options)
- : this ((o) => action (), null, token, options)
- {
- }
-
- public Task (Action<object> action, object state) : this (action, state, TaskCreationOptions.None)
- {
- }
-
- public Task (Action<object> action, object state, TaskCreationOptions options)
- : this (action, state, CancellationToken.None, options)
- {
- }
-
- public Task (Action<object> action, object state, CancellationToken token)
- : this (action, state, token, TaskCreationOptions.None)
- {
- }
-
- public Task (Action<object> action, object state, CancellationToken token, TaskCreationOptions options)
- {
- this.taskCreationOptions = options;
- this.action = action == null ? EmptyFunc : action;
- this.state = state;
- this.taskId = Interlocked.Increment (ref id);
- this.status = TaskStatus.Created;
- this.token = token;
- // Process taskCreationOptions
- if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent)) {
- parent = current;
- if (parent != null)
- parent.AddChild ();
- }
- }
-
- ~Task ()
- {
- if (exception != null && !exceptionObserved)
- throw exception;
- }
- bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
- {
- return (opt & member) == member;
- }
- static void EmptyFunc (object o)
- {
- }
-
- #region Start
- public void Start ()
- {
- Start (TaskScheduler.Current);
- }
-
- public void Start (TaskScheduler tscheduler)
- {
- this.taskScheduler = tscheduler;
- Start (ProxifyScheduler (tscheduler));
- }
-
- void Start (IScheduler scheduler)
- {
- this.scheduler = scheduler;
- status = TaskStatus.WaitingForActivation;
- Schedule ();
- }
-
- IScheduler ProxifyScheduler (TaskScheduler tscheduler)
- {
- IScheduler sched = tscheduler as IScheduler;
- return sched != null ? sched : new SchedulerProxy (tscheduler);
- }
-
- public void RunSynchronously ()
- {
- RunSynchronously (TaskScheduler.Current);
- }
-
- public void RunSynchronously (TaskScheduler tscheduler)
- {
- // Adopt this scheme for the moment
- ThreadStart ();
- }
- #endregion
-
- #region ContinueWith
- public Task ContinueWith (Action<Task> a)
- {
- return ContinueWith (a, TaskContinuationOptions.None);
- }
-
- public Task ContinueWith (Action<Task> a, TaskContinuationOptions kind)
- {
- return ContinueWith (a, CancellationToken.None, kind, TaskScheduler.Current);
- }
-
- public Task ContinueWith (Action<Task> a, CancellationToken token)
- {
- return ContinueWith (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
- }
-
- public Task ContinueWith (Action<Task> a, TaskScheduler scheduler)
- {
- return ContinueWith (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
- }
-
- public Task ContinueWith (Action<Task> a, CancellationToken token, TaskContinuationOptions kind, TaskScheduler scheduler)
- {
- Task continuation = new Task ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
- ContinueWithCore (continuation, kind, scheduler);
- return continuation;
- }
-
- public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a)
- {
- return ContinueWith<TResult> (a, TaskContinuationOptions.None);
- }
-
- public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskContinuationOptions options)
- {
- return ContinueWith<TResult> (a, CancellationToken.None, options, TaskScheduler.Current);
- }
-
- public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token)
- {
- return ContinueWith<TResult> (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
- }
-
- public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskScheduler scheduler)
- {
- return ContinueWith<TResult> (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
- }
-
- public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token,
- TaskContinuationOptions kind, TaskScheduler scheduler)
- {
- Task<TResult> t = new Task<TResult> ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
-
- ContinueWithCore (t, kind, scheduler);
-
- return t;
- }
-
- internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind, TaskScheduler scheduler)
- {
- ContinueWithCore (continuation, kind, scheduler, () => true);
- }
-
- internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind,
- TaskScheduler scheduler, Func<bool> predicate)
- {
- // Already set the scheduler so that user can call Wait and that sort of stuff
- continuation.taskScheduler = scheduler;
- continuation.scheduler = ProxifyScheduler (scheduler);
-
- AtomicBoolean launched = new AtomicBoolean ();
- EventHandler action = delegate {
- if (!predicate ()) return;
-
- if (!launched.Value && !launched.Exchange (true)) {
- if (!ContinuationStatusCheck (kind)) {
- continuation.CancelReal ();
- continuation.Dispose ();
-
- return;
- }
-
- CheckAndSchedule (continuation, kind, scheduler);
- }
- };
-
- if (IsCompleted) {
- action (this, EventArgs.Empty);
- return;
- }
-
- completed += action;
-
- // Retry in case completion was achieved but event adding was too late
- if (IsCompleted)
- action (this, EventArgs.Empty);
- }
-
- bool ContinuationStatusCheck (TaskContinuationOptions kind)
- {
- if (kind == TaskContinuationOptions.None)
- return true;
-
- int kindCode = (int)kind;
-
- if (kindCode >= ((int)TaskContinuationOptions.NotOnRanToCompletion)) {
- if (status == TaskStatus.Canceled) {
- if (kind == TaskContinuationOptions.NotOnCanceled)
- return false;
- if (kind == TaskContinuationOptions.OnlyOnFaulted)
- return false;
- if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
- return false;
- } else if (status == TaskStatus.Faulted) {
- if (kind == TaskContinuationOptions.NotOnFaulted)
- return false;
- if (kind == TaskContinuationOptions.OnlyOnCanceled)
- return false;
- if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
- return false;
- } else if (status == TaskStatus.RanToCompletion) {
- if (kind == TaskContinuationOptions.NotOnRanToCompletion)
- return false;
- if (kind == TaskContinuationOptions.OnlyOnFaulted)
- return false;
- if (kind == TaskContinuationOptions.OnlyOnCanceled)
- return false;
- }
- }
-
- return true;
- }
-
- void CheckAndSchedule (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
- {
- if (options == TaskContinuationOptions.None || (options & TaskContinuationOptions.ExecuteSynchronously) > 0)
- continuation.ThreadStart ();
- else
- continuation.Start (scheduler);
- }
-
- internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
- {
- TaskCreationOptions options = TaskCreationOptions.None;
- if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
- options |= TaskCreationOptions.AttachedToParent;
- if ((kind & TaskContinuationOptions.PreferFairness) > 0)
- options |= TaskCreationOptions.PreferFairness;
- if ((kind & TaskContinuationOptions.LongRunning) > 0)
- options |= TaskCreationOptions.LongRunning;
-
- return options;
- }
- #endregion
-
- #region Internal and protected thingies
- internal void Schedule ()
- {
- status = TaskStatus.WaitingToRun;
-
- // If worker is null it means it is a local one, revert to the old behavior
- if (childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
- scheduler.AddWork (this);
- } else {
- /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
- * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
- * the correct Thread during the creation
- */
- childWorkAdder (this);
- }
- }
-
- void ThreadStart ()
- {
- current = this;
- TaskScheduler.Current = taskScheduler;
-
- if (!token.IsCancellationRequested) {
-
- status = TaskStatus.Running;
-
- try {
- InnerInvoke ();
- } catch (Exception e) {
- exception = new AggregateException (e);
- status = TaskStatus.Faulted;
- if (taskScheduler.FireUnobservedEvent (Exception).Observed)
- exceptionObserved = true;
- }
- } else {
- CancelReal ();
- }
-
- Finish ();
- }
-
- internal void Execute (Action<Task> childAdder)
- {
- childWorkAdder = childAdder;
- ThreadStart ();
- }
-
- internal void AddChild ()
- {
- childTasks.AddCount ();
- }
- internal void ChildCompleted ()
- {
- childTasks.Signal ();
- if (childTasks.IsSet && status == TaskStatus.WaitingForChildrenToComplete)
- status = TaskStatus.RanToCompletion;
- }
- internal virtual void InnerInvoke ()
- {
- if (action != null)
- action (state);
- // Set action to null so that the GC can collect the delegate and thus
- // any big object references that the user might have captured in an anonymous method
- action = null;
- state = null;
- }
-
- internal void Finish ()
- {
- // If there wasn't any child created in the task we set the CountdownEvent
- childTasks.Signal ();
-
- // Don't override Canceled or Faulted
- if (status == TaskStatus.Running) {
- if (childTasks.IsSet )
- status = TaskStatus.RanToCompletion;
- else
- status = TaskStatus.WaitingForChildrenToComplete;
- }
-
- // Call the event in the correct style
- EventHandler tempCompleted = completed;
- if (tempCompleted != null)
- tempCompleted (this, EventArgs.Empty);
-
- // Reset the current thingies
- current = null;
- TaskScheduler.Current = null;
-
- // Tell parent that we are finished
- if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null){
- parent.ChildCompleted ();
- }
-
- Dispose ();
- }
- #endregion
-
- #region Cancel and Wait related method
-
- internal void CancelReal ()
- {
- exception = new AggregateException (new TaskCanceledException (this));
- status = TaskStatus.Canceled;
- }
-
- public void Wait ()
- {
- if (scheduler == null)
- throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
-
- scheduler.ParticipateUntil (this);
- if (exception != null)
- throw exception;
- }
- public void Wait (CancellationToken token)
- {
- Wait (null, token);
- }
-
- public bool Wait (TimeSpan ts)
- {
- return Wait ((int)ts.TotalMilliseconds, CancellationToken.None);
- }
-
- public bool Wait (int millisecondsTimeout)
- {
- return Wait (millisecondsTimeout, CancellationToken.None);
- }
-
- public bool Wait (int millisecondsTimeout, CancellationToken token)
- {
- Watch sw = Watch.StartNew ();
- return Wait (() => sw.ElapsedMilliseconds >= millisecondsTimeout, token);
- }
- bool Wait (Func<bool> stopFunc, CancellationToken token)
- {
- if (scheduler == null)
- throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
-
- bool result = scheduler.ParticipateUntil (this, delegate {
- if (token.IsCancellationRequested)
- throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
-
- return (stopFunc != null) ? stopFunc () : false;
- });
- if (exception != null)
- throw exception;
-
- return !result;
- }
-
- public static void WaitAll (params Task[] tasks)
- {
- if (tasks == null)
- throw new ArgumentNullException ("tasks");
- if (tasks.Length == 0)
- throw new ArgumentException ("tasks is empty", "tasks");
-
- foreach (var t in tasks)
- t.Wait ();
- }
- public static void WaitAll (Task[] tasks, CancellationToken token)
- {
- if (tasks == null)
- throw new ArgumentNullException ("tasks");
- if (tasks.Length == 0)
- throw new ArgumentException ("tasks is empty", "tasks");
-
- foreach (var t in tasks)
- t.Wait (token);
- }
-
- public static bool WaitAll (Task[] tasks, TimeSpan ts)
- {
- if (tasks == null)
- throw new ArgumentNullException ("tasks");
- if (tasks.Length == 0)
- throw new ArgumentException ("tasks is empty", "tasks");
-
- bool result = true;
- foreach (var t in tasks)
- result &= t.Wait (ts);
- return result;
- }
-
- public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
- {
- if (tasks == null)
- throw new ArgumentNullException ("tasks");
- if (tasks.Length == 0)
- throw new ArgumentException ("tasks is empty", "tasks");
-
- bool result = true;
- foreach (var t in tasks)
- result &= t.Wait (millisecondsTimeout);
- return result;
- }
-
- public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken token)
- {
- if (tasks == null)
- throw new ArgumentNullException ("tasks");
- if (tasks.Length == 0)
- throw new ArgumentException ("tasks is empty", "tasks");
-
- bool result = true;
- foreach (var t in tasks)
- result &= t.Wait (millisecondsTimeout, token);
- return result;
- }
-
- public static int WaitAny (params Task[] tasks)
- {
- return WaitAny (tasks, null, null);
- }
-
- static int WaitAny (Task[] tasks, Func<bool> stopFunc, CancellationToken? token)
- {
- if (tasks == null)
- throw new ArgumentNullException ("tasks");
- if (tasks.Length == 0)
- throw new ArgumentException ("tasks is empty", "tasks");
-
- int numFinished = 0;
- int indexFirstFinished = -1;
- int index = 0;
-
- foreach (Task t in tasks) {
- t.ContinueWith (delegate {
- int indexResult = index;
- int result = Interlocked.Increment (ref numFinished);
- // Check if we are the first to have finished
- if (result == 1)
- indexFirstFinished = indexResult;
- });
- index++;
- }
-
- // One task already finished
- if (indexFirstFinished != -1)
- return indexFirstFinished;
-
- // All tasks are supposed to use the same TaskManager
- tasks[0].scheduler.ParticipateUntil (delegate {
- if (stopFunc != null && stopFunc ())
- return true;
-
- if (token.HasValue && token.Value.IsCancellationRequested)
- throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
-
- return numFinished >= 1;
- });
-
- return indexFirstFinished;
- }
-
- public static int WaitAny (Task[] tasks, TimeSpan ts)
- {
- return WaitAny (tasks, (int)ts.TotalMilliseconds);
- }
-
- public static int WaitAny (Task[] tasks, int millisecondsTimeout)
- {
- if (millisecondsTimeout < -1)
- throw new ArgumentOutOfRangeException ("millisecondsTimeout");
-
- if (millisecondsTimeout == -1)
- return WaitAny (tasks);
-
- Watch sw = Watch.StartNew ();
- return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, null);
- }
- public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken token)
- {
- if (millisecondsTimeout < -1)
- throw new ArgumentOutOfRangeException ("millisecondsTimeout");
-
- if (millisecondsTimeout == -1)
- return WaitAny (tasks);
-
- Watch sw = Watch.StartNew ();
- return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, token);
- }
-
- public static int WaitAny (Task[] tasks, CancellationToken token)
- {
- return WaitAny (tasks, null, token);
- }
- #endregion
-
- #region Dispose
- public void Dispose ()
- {
- Dispose (true);
- }
-
- protected virtual void Dispose (bool disposeManagedRes)
- {
- // Set action to null so that the GC can collect the delegate and thus
- // any big object references that the user might have captured in a anonymous method
- if (disposeManagedRes) {
- action = null;
- completed = null;
- state = null;
- }
- }
- #endregion
-
- #region Properties
- public static TaskFactory Factory {
- get {
- return defaultFactory;
- }
- }
-
- public static int? CurrentId {
- get {
- Task t = current;
- return t == null ? (int?)null : t.Id;
- }
- }
-
- public AggregateException Exception {
- get {
- exceptionObserved = true;
-
- return exception;
- }
- internal set {
- exception = value;
- }
- }
-
- public bool IsCanceled {
- get {
- return status == TaskStatus.Canceled;
- }
- }
- public bool IsCompleted {
- get {
- return status == TaskStatus.RanToCompletion ||
- status == TaskStatus.Canceled || status == TaskStatus.Faulted;
- }
- }
-
- public bool IsFaulted {
- get {
- return status == TaskStatus.Faulted;
- }
- }
- public TaskCreationOptions CreationOptions {
- get {
- return taskCreationOptions;
- }
- }
-
- public TaskStatus Status {
- get {
- return status;
- }
- internal set {
- status = value;
- }
- }
- public object AsyncState {
- get {
- return state;
- }
- }
-
- bool IAsyncResult.CompletedSynchronously {
- get {
- return true;
- }
- }
- WaitHandle IAsyncResult.AsyncWaitHandle {
- get {
- return null;
- }
- }
-
- public int Id {
- get {
- return taskId;
- }
- }
- #endregion
- }
- }
- #endif
|