copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / corlib / System.Threading.Tasks / Task.cs
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:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
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.
22 //
23 //
24
25 #if NET_4_0 || BOOTSTRAP_NET_4_0
26
27 using System;
28 using System.Threading;
29 using System.Collections.Concurrent;
30
31 namespace System.Threading.Tasks
32 {
33         public class Task : IDisposable, IAsyncResult
34         {
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;
41                 
42                 Task parent;
43                 
44                 static int          id = -1;
45                 static TaskFactory  defaultFactory = new TaskFactory ();
46                 
47                 CountdownEvent childTasks = new CountdownEvent (1);
48                 
49                 int                 taskId;
50                 TaskCreationOptions taskCreationOptions;
51                 
52                 IScheduler          scheduler;
53                 TaskScheduler       taskScheduler;
54                 
55                 volatile AggregateException  exception;
56                 volatile bool                exceptionObserved;
57                 volatile TaskStatus          status;
58                 
59                 Action<object> action;
60                 object         state;
61                 EventHandler   completed;
62                 
63                 CancellationToken token;                        
64                 
65                 public Task (Action action) : this (action, TaskCreationOptions.None)
66                 {
67                         
68                 }
69                 
70                 public Task (Action action, TaskCreationOptions options) : this (action, CancellationToken.None, options)
71                 {
72                         
73                 }
74                 
75                 public Task (Action action, CancellationToken token) : this (action, token, TaskCreationOptions.None)
76                 {
77                         
78                 }
79                 
80                 public Task (Action action, CancellationToken token, TaskCreationOptions options)
81                         : this ((o) => { if (action != null) action (); }, null, token, options)
82                 {
83                 }
84                 
85                 public Task (Action<object> action, object state) : this (action, state, TaskCreationOptions.None)
86                 {       
87                 }
88                 
89                 public Task (Action<object> action, object state, TaskCreationOptions options)
90                         : this (action, state, CancellationToken.None, options)
91                 {
92                 }
93                 
94                 public Task (Action<object> action, object state, CancellationToken token)
95                         : this (action, state, token, TaskCreationOptions.None)
96                 {       
97                 }
98                 
99                 public Task (Action<object> action, object state, CancellationToken token, TaskCreationOptions options)
100                 {
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;
107
108                         // Process taskCreationOptions
109                         if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent)) {
110                                 parent = current;
111                                 if (parent != null)
112                                         parent.AddChild ();
113                         }
114                 }
115                 
116                 ~Task ()
117                 {
118                         if (exception != null && !exceptionObserved)
119                                 throw exception;
120                 }
121
122                 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
123                 {
124                         return (opt & member) == member;
125                 }
126
127                 static void EmptyFunc (object o)
128                 {
129                 }
130                 
131                 #region Start
132                 public void Start ()
133                 {
134                         Start (TaskScheduler.Current);
135                 }
136                 
137                 public void Start (TaskScheduler tscheduler)
138                 {
139                         this.taskScheduler = tscheduler;
140                         Start (ProxifyScheduler (tscheduler));
141                 }
142                 
143                 void Start (IScheduler scheduler)
144                 {
145                         this.scheduler = scheduler;
146                         status = TaskStatus.WaitingForActivation;
147                         Schedule ();
148                 }
149                 
150                 IScheduler ProxifyScheduler (TaskScheduler tscheduler)
151                 {
152                         IScheduler sched = tscheduler as IScheduler;
153                         return sched != null ? sched : new SchedulerProxy (tscheduler);
154                 }
155                 
156                 public void RunSynchronously ()
157                 {
158                         RunSynchronously (TaskScheduler.Current);
159                 }
160                 
161                 public void RunSynchronously (TaskScheduler tscheduler) 
162                 {
163                         // Adopt this scheme for the moment
164                         ThreadStart ();
165                 }
166                 #endregion
167                 
168                 #region ContinueWith
169                 public Task ContinueWith (Action<Task> a)
170                 {
171                         return ContinueWith (a, TaskContinuationOptions.None);
172                 }
173                 
174                 public Task ContinueWith (Action<Task> a, TaskContinuationOptions kind)
175                 {
176                         return ContinueWith (a, CancellationToken.None, kind, TaskScheduler.Current);
177                 }
178                 
179                 public Task ContinueWith (Action<Task> a, CancellationToken token)
180                 {
181                         return ContinueWith (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
182                 }
183                 
184                 public Task ContinueWith (Action<Task> a, TaskScheduler scheduler)
185                 {
186                         return ContinueWith (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
187                 }
188                 
189                 public Task ContinueWith (Action<Task> a, CancellationToken token, TaskContinuationOptions kind, TaskScheduler scheduler)
190                 {
191                         Task continuation = new Task ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
192                         ContinueWithCore (continuation, kind, scheduler);
193                         return continuation;
194                 }
195                 
196                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a)
197                 {
198                         return ContinueWith<TResult> (a, TaskContinuationOptions.None);
199                 }
200                 
201                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskContinuationOptions options)
202                 {
203                         return ContinueWith<TResult> (a, CancellationToken.None, options, TaskScheduler.Current);
204                 }
205                 
206                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token)
207                 {
208                         return ContinueWith<TResult> (a, token, TaskContinuationOptions.None, TaskScheduler.Current);
209                 }
210                 
211                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, TaskScheduler scheduler)
212                 {
213                         return ContinueWith<TResult> (a, CancellationToken.None, TaskContinuationOptions.None, scheduler);
214                 }
215                 
216                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> a, CancellationToken token,
217                                                             TaskContinuationOptions kind, TaskScheduler scheduler)
218                 {
219                         Task<TResult> t = new Task<TResult> ((o) => a ((Task)o), this, token, GetCreationOptions (kind));
220                         
221                         ContinueWithCore (t, kind, scheduler);
222                         
223                         return t;
224                 }
225                 
226                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind, TaskScheduler scheduler)
227                 {
228                         ContinueWithCore (continuation, kind, scheduler, () => true);
229                 }
230                 
231                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind,
232                                                 TaskScheduler scheduler, Func<bool> predicate)
233                 {
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);
237                         
238                         AtomicBoolean launched = new AtomicBoolean ();
239                         EventHandler action = delegate (object sender, EventArgs e) {
240                                 if (!predicate ()) return;
241                                 
242                                 if (!launched.Value && launched.TrySet ()) {
243                                         if (!ContinuationStatusCheck (kind)) {
244                                                 continuation.CancelReal ();
245                                                 continuation.Dispose ();
246                                                 
247                                                 return;
248                                         }
249                                         
250                                         CheckAndSchedule (continuation, kind, scheduler, sender == null);
251                                 }
252                         };
253                         
254                         if (IsCompleted) {
255                                 action (null, EventArgs.Empty);
256                                 return;
257                         }
258                         
259                         completed += action;
260                         
261                         // Retry in case completion was achieved but event adding was too late
262                         if (IsCompleted)
263                                 action (null, EventArgs.Empty);
264                 }
265                 
266                 bool ContinuationStatusCheck (TaskContinuationOptions kind)
267                 {
268                         if (kind == TaskContinuationOptions.None)
269                                 return true;
270                         
271                         int kindCode = (int)kind;
272                         
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;
295                                 }
296                         }
297                         
298                         return true;
299                 }
300                 
301                 void CheckAndSchedule (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler, bool fromCaller)
302                 {
303                         if (!fromCaller 
304                             && (options == TaskContinuationOptions.None || (options & TaskContinuationOptions.ExecuteSynchronously) > 0))
305                                 continuation.ThreadStart ();
306                         else
307                                 continuation.Start (scheduler);
308                 }
309                 
310                 internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
311                 {
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;
319                         
320                         return options;
321                 }
322                 #endregion
323                 
324                 #region Internal and protected thingies
325                 internal void Schedule ()
326                 {       
327                         status = TaskStatus.WaitingToRun;
328                         
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
336                                  */
337                                 childWorkAdder (this);
338                         }
339                 }
340                 
341                 void ThreadStart ()
342                 {                       
343                         current = this;
344                         TaskScheduler.Current = taskScheduler;
345                         
346                         if (!token.IsCancellationRequested) {
347                                 
348                                 status = TaskStatus.Running;
349                                 
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;
357                                 }
358                         } else {
359                                 CancelReal ();
360                         }
361                         
362                         Finish ();
363                 }
364                 
365                 internal void Execute (Action<Task> childAdder)
366                 {
367                         childWorkAdder = childAdder;
368                         ThreadStart ();
369                 }
370                 
371                 internal void AddChild ()
372                 {
373                         childTasks.AddCount ();
374                 }
375
376                 internal void ChildCompleted ()
377                 {
378                         childTasks.Signal ();
379                         if (childTasks.IsSet && status == TaskStatus.WaitingForChildrenToComplete) {
380                                 status = TaskStatus.RanToCompletion;
381                                 
382                                 // Let continuation creation process
383                                 EventHandler tempCompleted = completed;
384                                 if (tempCompleted != null) 
385                                         tempCompleted (this, EventArgs.Empty);
386                         }
387                 }
388
389                 internal virtual void InnerInvoke ()
390                 {
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;
397                 }
398                 
399                 internal void Finish ()
400                 {
401                         // If there wasn't any child created in the task we set the CountdownEvent
402                         childTasks.Signal ();
403                         
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;
410                         }
411                 
412                         if (status != TaskStatus.WaitingForChildrenToComplete) {
413                                 // Let continuation creation process
414                                 EventHandler tempCompleted = completed;
415                                 if (tempCompleted != null)
416                                         tempCompleted (this, EventArgs.Empty);
417                         }
418                         
419                         // Reset the current thingies
420                         current = null;
421                         TaskScheduler.Current = null;
422                         
423                         // Tell parent that we are finished
424                         if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null){
425                                 parent.ChildCompleted ();
426                         }
427                         
428                         Dispose ();
429                 }
430                 #endregion
431                 
432                 #region Cancel and Wait related method
433                 
434                 internal void CancelReal ()
435                 {
436                         exception = new AggregateException (new TaskCanceledException (this));
437                         status = TaskStatus.Canceled;
438                 }
439                 
440                 public void Wait ()
441                 {
442                         if (scheduler == null)
443                                 throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
444                         
445                         scheduler.ParticipateUntil (this);
446                         if (exception != null)
447                                 throw exception;
448                 }
449
450                 public void Wait (CancellationToken token)
451                 {
452                         Wait (null, token);
453                 }
454                 
455                 public bool Wait (TimeSpan ts)
456                 {
457                         return Wait ((int)ts.TotalMilliseconds, CancellationToken.None);
458                 }
459                 
460                 public bool Wait (int millisecondsTimeout)
461                 {
462                         return Wait (millisecondsTimeout, CancellationToken.None);
463                 }
464                 
465                 public bool Wait (int millisecondsTimeout, CancellationToken token)
466                 {
467                         Watch sw = Watch.StartNew ();
468                         return Wait (() => sw.ElapsedMilliseconds >= millisecondsTimeout, token);
469                 }
470
471                 bool Wait (Func<bool> stopFunc, CancellationToken token)
472                 {
473                         if (scheduler == null)
474                                 throw new InvalidOperationException ("The Task hasn't been Started and thus can't be waited on");
475                         
476                         bool result = scheduler.ParticipateUntil (this, delegate { 
477                                 if (token.IsCancellationRequested)
478                                         throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
479                                 
480                                 return (stopFunc != null) ? stopFunc () : false;
481                         });
482
483                         if (exception != null)
484                                 throw exception;
485                         
486                         return !result;
487                 }
488                 
489                 public static void WaitAll (params Task[] tasks)
490                 {
491                         if (tasks == null)
492                                 throw new ArgumentNullException ("tasks");
493                         if (tasks.Length == 0)
494                                 throw new ArgumentException ("tasks is empty", "tasks");
495                         
496                         foreach (var t in tasks)
497                                 t.Wait ();
498                 }
499
500                 public static void WaitAll (Task[] tasks, CancellationToken token)
501                 {
502                         if (tasks == null)
503                                 throw new ArgumentNullException ("tasks");
504                         if (tasks.Length == 0)
505                                 throw new ArgumentException ("tasks is empty", "tasks");
506                         
507                         foreach (var t in tasks)
508                                 t.Wait (token);
509                 }
510                 
511                 public static bool WaitAll (Task[] tasks, TimeSpan ts)
512                 {
513                         if (tasks == null)
514                                 throw new ArgumentNullException ("tasks");
515                         if (tasks.Length == 0)
516                                 throw new ArgumentException ("tasks is empty", "tasks");
517                         
518                         bool result = true;
519                         foreach (var t in tasks)
520                                 result &= t.Wait (ts);
521                         return result;
522                 }
523                 
524                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
525                 {
526                         if (tasks == null)
527                                 throw new ArgumentNullException ("tasks");
528                         if (tasks.Length == 0)
529                                 throw new ArgumentException ("tasks is empty", "tasks");
530                         
531                         bool result = true;
532                         foreach (var t in tasks)
533                                 result &= t.Wait (millisecondsTimeout);
534                         return result;
535                 }
536                 
537                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken token)
538                 {
539                         if (tasks == null)
540                                 throw new ArgumentNullException ("tasks");
541                         if (tasks.Length == 0)
542                                 throw new ArgumentException ("tasks is empty", "tasks");
543                         
544                         bool result = true;
545                         foreach (var t in tasks)
546                                 result &= t.Wait (millisecondsTimeout, token);
547                         return result;
548                 }
549                 
550                 public static int WaitAny (params Task[] tasks)
551                 {
552                         return WaitAny (tasks, null, null);
553                 }
554                 
555                 static int WaitAny (Task[] tasks, Func<bool> stopFunc, CancellationToken? token)
556                 {
557                         if (tasks == null)
558                                 throw new ArgumentNullException ("tasks");
559                         if (tasks.Length == 0)
560                                 throw new ArgumentException ("tasks is empty", "tasks");
561                         
562                         int numFinished = 0;
563                         int indexFirstFinished = -1;
564                         int index = 0;
565                         
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++;
575                         }
576                         
577                         // One task already finished
578                         if (indexFirstFinished != -1)
579                                 return indexFirstFinished;
580                         
581                         // All tasks are supposed to use the same TaskManager
582                         tasks[0].scheduler.ParticipateUntil (delegate {
583                                 if (stopFunc != null && stopFunc ())
584                                         return true;
585                                 
586                                 if (token.HasValue && token.Value.IsCancellationRequested)
587                                         throw new OperationCanceledException ("The CancellationToken has had cancellation requested.");
588                                 
589                                 return numFinished >= 1;
590                         });
591                         
592                         return indexFirstFinished;
593                 }
594                 
595                 public static int WaitAny (Task[] tasks, TimeSpan ts)
596                 {
597                         return WaitAny (tasks, (int)ts.TotalMilliseconds);
598                 }
599                 
600                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
601                 {
602                         if (millisecondsTimeout < -1)
603                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
604                         
605                         if (millisecondsTimeout == -1)
606                                 return WaitAny (tasks);
607                         
608                         Watch sw = Watch.StartNew ();
609                         return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, null);
610                 }
611
612                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken token)
613                 {                       
614                         if (millisecondsTimeout < -1)
615                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
616                         
617                         if (millisecondsTimeout == -1)
618                                 return WaitAny (tasks);
619                         
620                         Watch sw = Watch.StartNew ();
621                         return WaitAny (tasks, () => sw.ElapsedMilliseconds > millisecondsTimeout, token);
622                 }
623                 
624                 public static int WaitAny (Task[] tasks, CancellationToken token)
625                 {                       
626                         return WaitAny (tasks, null, token);
627                 }
628                 #endregion
629                 
630                 #region Dispose
631                 public void Dispose ()
632                 {
633                         Dispose (true);
634                 }
635                 
636                 protected virtual void Dispose (bool disposeManagedRes)
637                 {
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;
644                         }
645                 }
646                 #endregion
647                 
648                 #region Properties
649                 public static TaskFactory Factory {
650                         get {
651                                 return defaultFactory;
652                         }
653                 }
654                 
655                 public static int? CurrentId {
656                         get {
657                                 Task t = current;
658                                 return t == null ? (int?)null : t.Id;
659                         }
660                 }
661                 
662                 public AggregateException Exception {
663                         get {
664                                 exceptionObserved = true;
665                                 
666                                 return exception;       
667                         }
668                         internal set {
669                                 exception = value;
670                         }
671                 }
672                 
673                 public bool IsCanceled {
674                         get {
675                                 return status == TaskStatus.Canceled;
676                         }
677                 }
678
679                 public bool IsCompleted {
680                         get {
681                                 return status == TaskStatus.RanToCompletion ||
682                                         status == TaskStatus.Canceled || status == TaskStatus.Faulted;
683                         }
684                 }
685                 
686                 public bool IsFaulted {
687                         get {
688                                 return status == TaskStatus.Faulted;
689                         }
690                 }
691
692                 public TaskCreationOptions CreationOptions {
693                         get {
694                                 return taskCreationOptions;
695                         }
696                 }
697                 
698                 public TaskStatus Status {
699                         get {
700                                 return status;
701                         }
702                         internal set {
703                                 status = value;
704                         }
705                 }
706
707                 public object AsyncState {
708                         get {
709                                 return state;
710                         }
711                 }
712                 
713                 bool IAsyncResult.CompletedSynchronously {
714                         get {
715                                 return true;
716                         }
717                 }
718
719                 WaitHandle IAsyncResult.AsyncWaitHandle {
720                         get {
721                                 return null;
722                         }
723                 }
724                 
725                 public int Id {
726                         get {
727                                 return taskId;
728                         }
729                 }
730                 #endregion
731         }
732 }
733 #endif