Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[mono.git] / mcs / class / corlib / System.Threading.Tasks / TaskContinuation.cs
1 //
2 // TaskContinuation.cs
3 //
4 // Authors:
5 //    Jérémie Laval <jeremie dot laval at xamarin dot com>
6 //    Marek Safar  <marek.safar@gmail.com>
7 //
8 // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28 //
29
30 #if NET_4_0
31
32 using System.Collections.Generic;
33
34 namespace System.Threading.Tasks
35 {
36         interface IContinuation
37         {
38                 void Execute ();
39         }
40
41         class TaskContinuation : IContinuation
42         {
43                 readonly Task task;
44                 readonly TaskContinuationOptions continuationOptions;
45
46                 public TaskContinuation (Task task, TaskContinuationOptions continuationOptions)
47                 {
48                         this.task = task;
49                         this.continuationOptions = continuationOptions;
50                 }
51
52                 bool ContinuationStatusCheck (TaskContinuationOptions kind)
53                 {
54                         if (kind == TaskContinuationOptions.None)
55                                 return true;
56
57                         int kindCode = (int) kind;
58                         var status = task.ContinuationAncestor.Status;
59
60                         if (kindCode >= ((int) TaskContinuationOptions.NotOnRanToCompletion)) {
61                                 // Remove other options
62                                 kind &= ~(TaskContinuationOptions.PreferFairness
63                                                   | TaskContinuationOptions.LongRunning
64                                                   | TaskContinuationOptions.AttachedToParent
65                                                   | TaskContinuationOptions.ExecuteSynchronously);
66
67                                 if (status == TaskStatus.Canceled) {
68                                         if (kind == TaskContinuationOptions.NotOnCanceled)
69                                                 return false;
70                                         if (kind == TaskContinuationOptions.OnlyOnFaulted)
71                                                 return false;
72                                         if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
73                                                 return false;
74                                 } else if (status == TaskStatus.Faulted) {
75                                         if (kind == TaskContinuationOptions.NotOnFaulted)
76                                                 return false;
77                                         if (kind == TaskContinuationOptions.OnlyOnCanceled)
78                                                 return false;
79                                         if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
80                                                 return false;
81                                 } else if (status == TaskStatus.RanToCompletion) {
82                                         if (kind == TaskContinuationOptions.NotOnRanToCompletion)
83                                                 return false;
84                                         if (kind == TaskContinuationOptions.OnlyOnFaulted)
85                                                 return false;
86                                         if (kind == TaskContinuationOptions.OnlyOnCanceled)
87                                                 return false;
88                                 }
89                         }
90
91                         return true;
92                 }
93
94                 public void Execute ()
95                 {
96                         if (!ContinuationStatusCheck (continuationOptions)) {
97                                 task.CancelReal ();
98                                 task.Dispose ();
99                                 return;
100                         }
101
102                         // The task may have been canceled externally
103                         if (task.IsCompleted)
104                                 return;
105
106                         if ((continuationOptions & TaskContinuationOptions.ExecuteSynchronously) != 0)
107                                 task.RunSynchronouslyCore (task.scheduler);
108                         else
109                                 task.Schedule ();
110                 }
111         }
112
113         class ActionContinuation : IContinuation
114         {
115                 readonly Action action;
116
117                 public ActionContinuation (Action action)
118                 {
119                         this.action = action;
120                 }
121
122                 public void Execute ()
123                 {
124                         action ();
125                 }
126         }
127
128         class SchedulerAwaitContinuation : IContinuation
129         {
130                 readonly Task task;
131
132                 public SchedulerAwaitContinuation (Task task)
133                 {
134                         this.task = task;
135                 }
136
137                 public void Execute ()
138                 {
139                         task.RunSynchronouslyCore (task.scheduler);
140                 }
141         }
142
143         class SynchronizationContextContinuation : IContinuation
144         {
145                 readonly Action action;
146                 readonly SynchronizationContext ctx;
147
148                 public SynchronizationContextContinuation (Action action, SynchronizationContext ctx)
149                 {
150                         this.action = action;
151                         this.ctx = ctx;
152                 }
153
154                 public void Execute ()
155                 {
156                         ctx.Post (l => ((Action) l) (), action);
157                 }
158         }
159
160         sealed class WhenAllContinuation : IContinuation
161         {
162                 readonly Task owner;
163                 readonly IList<Task> tasks;
164                 int counter;
165
166                 public WhenAllContinuation (Task owner, IList<Task> tasks)
167                 {
168                         this.owner = owner;
169                         this.counter = tasks.Count;
170                         this.tasks = tasks;
171                 }
172
173                 public void Execute ()
174                 {
175                         if (Interlocked.Decrement (ref counter) != 0)
176                                 return;
177
178                         owner.Status = TaskStatus.Running;
179
180                         bool canceled = false;
181                         List<Exception> exceptions = null;
182                         foreach (var task in tasks) {
183                                 if (task.IsFaulted) {
184                                         if (exceptions == null)
185                                                 exceptions = new List<Exception> ();
186
187                                         exceptions.AddRange (task.Exception.InnerExceptions);
188                                         continue;
189                                 }
190
191                                 if (task.IsCanceled) {
192                                         canceled = true;
193                                 }
194                         }
195
196                         if (exceptions != null) {
197                                 owner.TrySetException (new AggregateException (exceptions));
198                                 return;
199                         }
200
201                         if (canceled) {
202                                 owner.CancelReal ();
203                                 return;
204                         }
205
206                         owner.Finish ();
207                 }
208         }
209
210         sealed class WhenAllContinuation<TResult> : IContinuation
211         {
212                 readonly Task<TResult[]> owner;
213                 readonly IList<Task<TResult>> tasks;
214                 int counter;
215
216                 public WhenAllContinuation (Task<TResult[]> owner, IList<Task<TResult>> tasks)
217                 {
218                         this.owner = owner;
219                         this.counter = tasks.Count;
220                         this.tasks = tasks;
221                 }
222
223                 public void Execute ()
224                 {
225                         if (Interlocked.Decrement (ref counter) != 0)
226                                 return;
227
228                         bool canceled = false;
229                         List<Exception> exceptions = null;
230                         TResult[] results = null;
231                         for (int i = 0; i < tasks.Count; ++i) {
232                                 var task = tasks [i];
233                                 if (task.IsFaulted) {
234                                         if (exceptions == null)
235                                                 exceptions = new List<Exception> ();
236
237                                         exceptions.AddRange (task.Exception.InnerExceptions);
238                                         continue;
239                                 }
240
241                                 if (task.IsCanceled) {
242                                         canceled = true;
243                                         continue;
244                                 }
245
246                                 if (results == null) {
247                                         if (canceled || exceptions != null)
248                                                 continue;
249
250                                         results = new TResult[tasks.Count];
251                                 }
252
253                                 results[i] = task.Result;
254                         }
255
256                         if (exceptions != null) {
257                                 owner.TrySetException (new AggregateException (exceptions));
258                                 return;
259                         }
260
261                         if (canceled) {
262                                 owner.CancelReal ();
263                                 return;
264                         }
265
266                         owner.TrySetResult (results);
267                 }
268         }
269
270         sealed class WhenAnyContinuation<T> : IContinuation where T : Task
271         {
272                 readonly Task<T> owner;
273                 readonly IList<T> tasks;
274                 AtomicBooleanValue executed;
275
276                 public WhenAnyContinuation (Task<T> owner, IList<T> tasks)
277                 {
278                         this.owner = owner;
279                         this.tasks = tasks;
280                         executed = new AtomicBooleanValue ();
281                 }
282
283                 public void Execute ()
284                 {
285                         if (!executed.TryRelaxedSet ())
286                                 return;
287
288                         bool owner_notified = false;
289                         for (int i = 0; i < tasks.Count; ++i) {
290                                 var task = tasks[i];
291                                 if (!task.IsCompleted) {
292                                         task.RemoveContinuation (this);
293                                         continue;
294                                 }
295
296                                 if (owner_notified)
297                                         continue;
298
299                                 owner.TrySetResult (task);
300                                 owner_notified = true;
301                         }
302                 }
303         }
304
305         sealed class ManualResetContinuation : IContinuation, IDisposable
306         {
307                 readonly ManualResetEventSlim evt;
308
309                 public ManualResetContinuation ()
310                 {
311                         this.evt = new ManualResetEventSlim ();
312                 }
313
314                 public ManualResetEventSlim Event {
315                         get {
316                                 return evt;
317                         }
318                 }
319
320                 public void Dispose ()
321                 {
322                         evt.Dispose ();
323                 }
324
325                 public void Execute ()
326                 {
327                         evt.Set ();
328                 }
329         }
330
331         sealed class CountdownContinuation : IContinuation, IDisposable
332         {
333                 readonly CountdownEvent evt;
334
335                 public CountdownContinuation (int initialCount)
336                 {
337                         this.evt = new CountdownEvent (initialCount);
338                 }
339
340                 public CountdownEvent Event {
341                         get {
342                                 return evt;
343                         }
344                 }
345
346                 public void Dispose ()
347                 {
348                         evt.Dispose ();
349                 }
350
351                 public void Execute ()
352                 {
353                         evt.Signal ();
354                 }
355         }
356
357         sealed class DisposeContinuation : IContinuation
358         {
359                 readonly IDisposable instance;
360
361                 public DisposeContinuation (IDisposable instance)
362                 {
363                         this.instance = instance;
364                 }
365
366                 public void Execute ()
367                 {
368                         instance.Dispose ();
369                 }
370         }
371 }
372
373 #endif