Merge pull request #819 from brendanzagaeski/patch-1
[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, false);
108                         else
109                                 task.Schedule (false);
110                 }
111         }
112
113         class AwaiterActionContinuation : IContinuation
114         {
115                 readonly Action action;
116
117                 public AwaiterActionContinuation (Action action)
118                 {
119                         this.action = action;
120                 }
121
122                 public void Execute ()
123                 {
124                         //
125                         // Continuation can be inlined only when the current context allows it. This is different to awaiter setup
126                         // because the context where the awaiter task is set to completed can be anywhere (due to TaskCompletionSource)
127                         //
128                         if ((SynchronizationContext.Current == null || SynchronizationContext.Current.GetType () == typeof (SynchronizationContext)) && TaskScheduler.IsDefault) {
129                                 action ();
130                         } else {
131                                 ThreadPool.UnsafeQueueUserWorkItem (l => ((Action) l) (), action);
132                         }
133                 }
134         }
135
136         class SchedulerAwaitContinuation : IContinuation
137         {
138                 readonly Task task;
139
140                 public SchedulerAwaitContinuation (Task task)
141                 {
142                         this.task = task;
143                 }
144
145                 public void Execute ()
146                 {
147                         task.RunSynchronouslyCore (task.scheduler, true);
148                 }
149         }
150
151         class SynchronizationContextContinuation : IContinuation
152         {
153                 readonly Action action;
154                 readonly SynchronizationContext ctx;
155
156                 public SynchronizationContextContinuation (Action action, SynchronizationContext ctx)
157                 {
158                         this.action = action;
159                         this.ctx = ctx;
160                 }
161
162                 public void Execute ()
163                 {
164                         ctx.Post (l => ((Action) l) (), action);
165                 }
166         }
167
168         sealed class WhenAllContinuation : IContinuation
169         {
170                 readonly Task owner;
171                 readonly IList<Task> tasks;
172                 int counter;
173
174                 public WhenAllContinuation (Task owner, IList<Task> tasks)
175                 {
176                         this.owner = owner;
177                         this.counter = tasks.Count;
178                         this.tasks = tasks;
179                 }
180
181                 public void Execute ()
182                 {
183                         if (Interlocked.Decrement (ref counter) != 0)
184                                 return;
185
186                         owner.Status = TaskStatus.Running;
187
188                         bool canceled = false;
189                         List<Exception> exceptions = null;
190                         foreach (var task in tasks) {
191                                 if (task.IsFaulted) {
192                                         if (exceptions == null)
193                                                 exceptions = new List<Exception> ();
194
195                                         exceptions.AddRange (task.Exception.InnerExceptions);
196                                         continue;
197                                 }
198
199                                 if (task.IsCanceled) {
200                                         canceled = true;
201                                 }
202                         }
203
204                         if (exceptions != null) {
205                                 owner.TrySetException (new AggregateException (exceptions), false, false);
206                                 return;
207                         }
208
209                         if (canceled) {
210                                 owner.CancelReal ();
211                                 return;
212                         }
213
214                         owner.Finish ();
215                 }
216         }
217
218         sealed class WhenAllContinuation<TResult> : IContinuation
219         {
220                 readonly Task<TResult[]> owner;
221                 readonly IList<Task<TResult>> tasks;
222                 int counter;
223
224                 public WhenAllContinuation (Task<TResult[]> owner, IList<Task<TResult>> tasks)
225                 {
226                         this.owner = owner;
227                         this.counter = tasks.Count;
228                         this.tasks = tasks;
229                 }
230
231                 public void Execute ()
232                 {
233                         if (Interlocked.Decrement (ref counter) != 0)
234                                 return;
235
236                         bool canceled = false;
237                         List<Exception> exceptions = null;
238                         TResult[] results = null;
239                         for (int i = 0; i < tasks.Count; ++i) {
240                                 var task = tasks [i];
241                                 if (task.IsFaulted) {
242                                         if (exceptions == null)
243                                                 exceptions = new List<Exception> ();
244
245                                         exceptions.AddRange (task.Exception.InnerExceptions);
246                                         continue;
247                                 }
248
249                                 if (task.IsCanceled) {
250                                         canceled = true;
251                                         continue;
252                                 }
253
254                                 if (results == null) {
255                                         if (canceled || exceptions != null)
256                                                 continue;
257
258                                         results = new TResult[tasks.Count];
259                                 }
260
261                                 results[i] = task.Result;
262                         }
263
264                         if (exceptions != null) {
265                                 owner.TrySetException (new AggregateException (exceptions), false, false);
266                                 return;
267                         }
268
269                         if (canceled) {
270                                 owner.CancelReal ();
271                                 return;
272                         }
273
274                         owner.TrySetResult (results);
275                 }
276         }
277
278         sealed class WhenAnyContinuation<T> : IContinuation where T : Task
279         {
280                 readonly Task<T> owner;
281                 readonly IList<T> tasks;
282                 AtomicBooleanValue executed;
283
284                 public WhenAnyContinuation (Task<T> owner, IList<T> tasks)
285                 {
286                         this.owner = owner;
287                         this.tasks = tasks;
288                         executed = new AtomicBooleanValue ();
289                 }
290
291                 public void Execute ()
292                 {
293                         if (!executed.TryRelaxedSet ())
294                                 return;
295
296                         bool owner_notified = false;
297                         for (int i = 0; i < tasks.Count; ++i) {
298                                 var task = tasks[i];
299                                 if (!task.IsCompleted) {
300                                         task.RemoveContinuation (this);
301                                         continue;
302                                 }
303
304                                 if (owner_notified)
305                                         continue;
306
307                                 owner.TrySetResult (task);
308                                 owner_notified = true;
309                         }
310                 }
311         }
312
313         sealed class ManualResetContinuation : IContinuation, IDisposable
314         {
315                 readonly ManualResetEventSlim evt;
316
317                 public ManualResetContinuation ()
318                 {
319                         this.evt = new ManualResetEventSlim ();
320                 }
321
322                 public ManualResetEventSlim Event {
323                         get {
324                                 return evt;
325                         }
326                 }
327
328                 public void Dispose ()
329                 {
330                         evt.Dispose ();
331                 }
332
333                 public void Execute ()
334                 {
335                         evt.Set ();
336                 }
337         }
338
339         sealed class CountdownContinuation : IContinuation, IDisposable
340         {
341                 readonly CountdownEvent evt;
342                 bool disposed;
343
344                 public CountdownContinuation (int initialCount)
345                 {
346                         this.evt = new CountdownEvent (initialCount);
347                 }
348
349                 public CountdownEvent Event {
350                         get {
351                                 return evt;
352                         }
353                 }
354
355                 public void Dispose ()
356                 {
357                         disposed = true;
358                         Thread.MemoryBarrier ();
359         
360                         evt.Dispose ();
361                 }
362
363                 public void Execute ()
364                 {
365                         // Guard against possible race when continuation is disposed and some tasks may still
366                         // execute it (removal was late and the execution is slower than the Dispose thread)
367                         if (!disposed)
368                                 evt.Signal ();
369                 }
370         }
371
372         sealed class DisposeContinuation : IContinuation
373         {
374                 readonly IDisposable instance;
375
376                 public DisposeContinuation (IDisposable instance)
377                 {
378                         this.instance = instance;
379                 }
380
381                 public void Execute ()
382                 {
383                         instance.Dispose ();
384                 }
385         }
386 }
387
388 #endif