5 // Jérémie Laval <jeremie dot laval at xamarin dot com>
6 // Marek Safar <marek.safar@gmail.com>
8 // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
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:
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
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
32 using System.Collections.Generic;
34 namespace System.Threading.Tasks
36 interface IContinuation
41 class TaskContinuation : IContinuation
44 readonly TaskContinuationOptions continuationOptions;
46 public TaskContinuation (Task task, TaskContinuationOptions continuationOptions)
49 this.continuationOptions = continuationOptions;
52 bool ContinuationStatusCheck (TaskContinuationOptions kind)
54 if (kind == TaskContinuationOptions.None)
57 int kindCode = (int) kind;
58 var status = task.ContinuationAncestor.Status;
60 if (kindCode >= ((int) TaskContinuationOptions.NotOnRanToCompletion)) {
61 // Remove other options
62 kind &= ~(TaskContinuationOptions.PreferFairness
63 | TaskContinuationOptions.LongRunning
64 | TaskContinuationOptions.AttachedToParent
65 | TaskContinuationOptions.ExecuteSynchronously);
67 if (status == TaskStatus.Canceled) {
68 if (kind == TaskContinuationOptions.NotOnCanceled)
70 if (kind == TaskContinuationOptions.OnlyOnFaulted)
72 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
74 } else if (status == TaskStatus.Faulted) {
75 if (kind == TaskContinuationOptions.NotOnFaulted)
77 if (kind == TaskContinuationOptions.OnlyOnCanceled)
79 if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
81 } else if (status == TaskStatus.RanToCompletion) {
82 if (kind == TaskContinuationOptions.NotOnRanToCompletion)
84 if (kind == TaskContinuationOptions.OnlyOnFaulted)
86 if (kind == TaskContinuationOptions.OnlyOnCanceled)
94 public void Execute ()
96 if (!ContinuationStatusCheck (continuationOptions)) {
97 task.CancelReal (notifyParent : true);
102 // The task may have been canceled externally
103 if (task.IsCompleted)
106 if ((continuationOptions & TaskContinuationOptions.ExecuteSynchronously) != 0)
107 task.RunSynchronouslyCore (task.scheduler, false);
109 task.Schedule (false);
113 class AwaiterActionContinuation : IContinuation
115 readonly Action action;
116 readonly ExecutionContext ec;
118 public AwaiterActionContinuation (Action action)
120 this.action = action;
122 // Capture execution context because the continuation can be inlined
123 // and we still need to run in original exection context regardless
124 // of UnsafeOnCompleted/OnCompleted entry
125 ec = ExecutionContext.Capture (false, true);
128 public void Execute ()
131 // Continuation can be inlined only when the current context allows it. This is different to awaiter setup
132 // because the context where the awaiter task is set to completed can be anywhere (due to TaskCompletionSource)
134 if ((SynchronizationContext.Current == null || SynchronizationContext.Current.GetType () == typeof (SynchronizationContext)) && TaskScheduler.IsDefault) {
136 ExecutionContext.Run (ec, l => ((Action)l) (), action);
140 ThreadPool.UnsafeQueueUserWorkItem (l => ((Action) l) (), action);
145 class SchedulerAwaitContinuation : IContinuation
149 public SchedulerAwaitContinuation (Task task)
154 public void Execute ()
156 task.RunSynchronouslyCore (task.scheduler, true);
160 class SynchronizationContextContinuation : IContinuation
162 readonly Action action;
163 readonly SynchronizationContext ctx;
165 public SynchronizationContextContinuation (Action action, SynchronizationContext ctx)
167 this.action = action;
171 public void Execute ()
173 // No context switch when we are on correct context
174 if (ctx == SynchronizationContext.Current)
177 ctx.Post (l => ((Action) l) (), action);
181 sealed class WhenAllContinuation : IContinuation
184 readonly IList<Task> tasks;
187 public WhenAllContinuation (Task owner, IList<Task> tasks)
190 this.counter = tasks.Count;
194 public void Execute ()
196 if (Interlocked.Decrement (ref counter) != 0)
199 owner.Status = TaskStatus.Running;
201 bool canceled = false;
202 List<Exception> exceptions = null;
203 foreach (var task in tasks) {
204 if (task.IsFaulted) {
205 if (exceptions == null)
206 exceptions = new List<Exception> ();
208 exceptions.AddRange (task.Exception.InnerExceptions);
212 if (task.IsCanceled) {
217 if (exceptions != null) {
218 owner.TrySetException (new AggregateException (exceptions), false, false);
231 sealed class WhenAllContinuation<TResult> : IContinuation
233 readonly Task<TResult[]> owner;
234 readonly IList<Task<TResult>> tasks;
237 public WhenAllContinuation (Task<TResult[]> owner, IList<Task<TResult>> tasks)
240 this.counter = tasks.Count;
244 public void Execute ()
246 if (Interlocked.Decrement (ref counter) != 0)
249 bool canceled = false;
250 List<Exception> exceptions = null;
251 TResult[] results = null;
252 for (int i = 0; i < tasks.Count; ++i) {
253 var task = tasks [i];
254 if (task.IsFaulted) {
255 if (exceptions == null)
256 exceptions = new List<Exception> ();
258 exceptions.AddRange (task.Exception.InnerExceptions);
262 if (task.IsCanceled) {
267 if (results == null) {
268 if (canceled || exceptions != null)
271 results = new TResult[tasks.Count];
274 results[i] = task.Result;
277 if (exceptions != null) {
278 owner.TrySetException (new AggregateException (exceptions), false, false);
287 owner.TrySetResult (results);
291 sealed class WhenAnyContinuation<T> : IContinuation where T : Task
293 readonly Task<T> owner;
294 readonly IList<T> tasks;
295 AtomicBooleanValue executed;
297 public WhenAnyContinuation (Task<T> owner, IList<T> tasks)
301 executed = new AtomicBooleanValue ();
304 public void Execute ()
306 if (!executed.TryRelaxedSet ())
309 bool owner_notified = false;
310 for (int i = 0; i < tasks.Count; ++i) {
312 if (!task.IsCompleted) {
313 task.RemoveContinuation (this);
320 owner.TrySetResult (task);
321 owner_notified = true;
326 sealed class ManualResetContinuation : IContinuation, IDisposable
328 readonly ManualResetEventSlim evt;
330 public ManualResetContinuation ()
332 this.evt = new ManualResetEventSlim ();
335 public ManualResetEventSlim Event {
341 public void Dispose ()
346 public void Execute ()
352 sealed class CountdownContinuation : IContinuation, IDisposable
354 readonly CountdownEvent evt;
357 public CountdownContinuation (int initialCount)
359 this.evt = new CountdownEvent (initialCount);
362 public CountdownEvent Event {
368 public void Dispose ()
371 Thread.MemoryBarrier ();
376 public void Execute ()
378 // Guard against possible race when continuation is disposed and some tasks may still
379 // execute it (removal was late and the execution is slower than the Dispose thread)
385 sealed class DisposeContinuation : IContinuation
387 readonly IDisposable instance;
389 public DisposeContinuation (IDisposable instance)
391 this.instance = instance;
394 public void Execute ()