Add a third AOT location, alongside ~/.mono/aot-cache and the assembly dir
[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 (notifyParent : true);
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                         // No context switch when we are on correct context
165                         if (ctx == SynchronizationContext.Current)
166                                 action ();
167                         else
168                                 ctx.Post (l => ((Action) l) (), action);
169                 }
170         }
171
172         sealed class WhenAllContinuation : IContinuation
173         {
174                 readonly Task owner;
175                 readonly IList<Task> tasks;
176                 int counter;
177
178                 public WhenAllContinuation (Task owner, IList<Task> tasks)
179                 {
180                         this.owner = owner;
181                         this.counter = tasks.Count;
182                         this.tasks = tasks;
183                 }
184
185                 public void Execute ()
186                 {
187                         if (Interlocked.Decrement (ref counter) != 0)
188                                 return;
189
190                         owner.Status = TaskStatus.Running;
191
192                         bool canceled = false;
193                         List<Exception> exceptions = null;
194                         foreach (var task in tasks) {
195                                 if (task.IsFaulted) {
196                                         if (exceptions == null)
197                                                 exceptions = new List<Exception> ();
198
199                                         exceptions.AddRange (task.Exception.InnerExceptions);
200                                         continue;
201                                 }
202
203                                 if (task.IsCanceled) {
204                                         canceled = true;
205                                 }
206                         }
207
208                         if (exceptions != null) {
209                                 owner.TrySetException (new AggregateException (exceptions), false, false);
210                                 return;
211                         }
212
213                         if (canceled) {
214                                 owner.CancelReal ();
215                                 return;
216                         }
217
218                         owner.Finish ();
219                 }
220         }
221
222         sealed class WhenAllContinuation<TResult> : IContinuation
223         {
224                 readonly Task<TResult[]> owner;
225                 readonly IList<Task<TResult>> tasks;
226                 int counter;
227
228                 public WhenAllContinuation (Task<TResult[]> owner, IList<Task<TResult>> tasks)
229                 {
230                         this.owner = owner;
231                         this.counter = tasks.Count;
232                         this.tasks = tasks;
233                 }
234
235                 public void Execute ()
236                 {
237                         if (Interlocked.Decrement (ref counter) != 0)
238                                 return;
239
240                         bool canceled = false;
241                         List<Exception> exceptions = null;
242                         TResult[] results = null;
243                         for (int i = 0; i < tasks.Count; ++i) {
244                                 var task = tasks [i];
245                                 if (task.IsFaulted) {
246                                         if (exceptions == null)
247                                                 exceptions = new List<Exception> ();
248
249                                         exceptions.AddRange (task.Exception.InnerExceptions);
250                                         continue;
251                                 }
252
253                                 if (task.IsCanceled) {
254                                         canceled = true;
255                                         continue;
256                                 }
257
258                                 if (results == null) {
259                                         if (canceled || exceptions != null)
260                                                 continue;
261
262                                         results = new TResult[tasks.Count];
263                                 }
264
265                                 results[i] = task.Result;
266                         }
267
268                         if (exceptions != null) {
269                                 owner.TrySetException (new AggregateException (exceptions), false, false);
270                                 return;
271                         }
272
273                         if (canceled) {
274                                 owner.CancelReal ();
275                                 return;
276                         }
277
278                         owner.TrySetResult (results);
279                 }
280         }
281
282         sealed class WhenAnyContinuation<T> : IContinuation where T : Task
283         {
284                 readonly Task<T> owner;
285                 readonly IList<T> tasks;
286                 AtomicBooleanValue executed;
287
288                 public WhenAnyContinuation (Task<T> owner, IList<T> tasks)
289                 {
290                         this.owner = owner;
291                         this.tasks = tasks;
292                         executed = new AtomicBooleanValue ();
293                 }
294
295                 public void Execute ()
296                 {
297                         if (!executed.TryRelaxedSet ())
298                                 return;
299
300                         bool owner_notified = false;
301                         for (int i = 0; i < tasks.Count; ++i) {
302                                 var task = tasks[i];
303                                 if (!task.IsCompleted) {
304                                         task.RemoveContinuation (this);
305                                         continue;
306                                 }
307
308                                 if (owner_notified)
309                                         continue;
310
311                                 owner.TrySetResult (task);
312                                 owner_notified = true;
313                         }
314                 }
315         }
316
317         sealed class ManualResetContinuation : IContinuation, IDisposable
318         {
319                 readonly ManualResetEventSlim evt;
320
321                 public ManualResetContinuation ()
322                 {
323                         this.evt = new ManualResetEventSlim ();
324                 }
325
326                 public ManualResetEventSlim Event {
327                         get {
328                                 return evt;
329                         }
330                 }
331
332                 public void Dispose ()
333                 {
334                         evt.Dispose ();
335                 }
336
337                 public void Execute ()
338                 {
339                         evt.Set ();
340                 }
341         }
342
343         sealed class CountdownContinuation : IContinuation, IDisposable
344         {
345                 readonly CountdownEvent evt;
346                 bool disposed;
347
348                 public CountdownContinuation (int initialCount)
349                 {
350                         this.evt = new CountdownEvent (initialCount);
351                 }
352
353                 public CountdownEvent Event {
354                         get {
355                                 return evt;
356                         }
357                 }
358
359                 public void Dispose ()
360                 {
361                         disposed = true;
362                         Thread.MemoryBarrier ();
363         
364                         evt.Dispose ();
365                 }
366
367                 public void Execute ()
368                 {
369                         // Guard against possible race when continuation is disposed and some tasks may still
370                         // execute it (removal was late and the execution is slower than the Dispose thread)
371                         if (!disposed)
372                                 evt.Signal ();
373                 }
374         }
375
376         sealed class DisposeContinuation : IContinuation
377         {
378                 readonly IDisposable instance;
379
380                 public DisposeContinuation (IDisposable instance)
381                 {
382                         this.instance = instance;
383                 }
384
385                 public void Execute ()
386                 {
387                         instance.Dispose ();
388                 }
389         }
390 }
391
392 #endif