TaskContinuation.cs
Go to the documentation of this file.
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 
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 SynchronizationContextContinuation : IContinuation
129  {
130  readonly Action action;
131  readonly SynchronizationContext ctx;
132 
133  public SynchronizationContextContinuation (Action action, SynchronizationContext ctx)
134  {
135  this.action = action;
136  this.ctx = ctx;
137  }
138 
139  public void Execute ()
140  {
141  ctx.Post (l => ((Action) l) (), action);
142  }
143  }
144 
145  sealed class WhenAllContinuation : IContinuation
146  {
147  readonly Task owner;
148  readonly IList<Task> tasks;
149  int counter;
150 
151  public WhenAllContinuation (Task owner, IList<Task> tasks)
152  {
153  this.owner = owner;
154  this.counter = tasks.Count;
155  this.tasks = tasks;
156  }
157 
158  public void Execute ()
159  {
160  if (Interlocked.Decrement (ref counter) != 0)
161  return;
162 
163  owner.Status = TaskStatus.Running;
164 
165  bool canceled = false;
166  List<Exception> exceptions = null;
167  foreach (var task in tasks) {
168  if (task.IsFaulted) {
169  if (exceptions == null)
170  exceptions = new List<Exception> ();
171 
172  exceptions.AddRange (task.Exception.InnerExceptions);
173  continue;
174  }
175 
176  if (task.IsCanceled) {
177  canceled = true;
178  }
179  }
180 
181  if (exceptions != null) {
182  owner.TrySetException (new AggregateException (exceptions));
183  return;
184  }
185 
186  if (canceled) {
187  owner.CancelReal ();
188  return;
189  }
190 
191  owner.Finish ();
192  }
193  }
194 
195  sealed class WhenAllContinuation<TResult> : IContinuation
196  {
197  readonly Task<TResult[]> owner;
198  readonly IList<Task<TResult>> tasks;
199  int counter;
200 
201  public WhenAllContinuation (Task<TResult[]> owner, IList<Task<TResult>> tasks)
202  {
203  this.owner = owner;
204  this.counter = tasks.Count;
205  this.tasks = tasks;
206  }
207 
208  public void Execute ()
209  {
210  if (Interlocked.Decrement (ref counter) != 0)
211  return;
212 
213  bool canceled = false;
214  List<Exception> exceptions = null;
215  TResult[] results = null;
216  for (int i = 0; i < tasks.Count; ++i) {
217  var task = tasks [i];
218  if (task.IsFaulted) {
219  if (exceptions == null)
220  exceptions = new List<Exception> ();
221 
222  exceptions.AddRange (task.Exception.InnerExceptions);
223  continue;
224  }
225 
226  if (task.IsCanceled) {
227  canceled = true;
228  continue;
229  }
230 
231  if (results == null) {
232  if (canceled || exceptions != null)
233  continue;
234 
235  results = new TResult[tasks.Count];
236  }
237 
238  results[i] = task.Result;
239  }
240 
241  if (exceptions != null) {
242  owner.TrySetException (new AggregateException (exceptions));
243  return;
244  }
245 
246  if (canceled) {
247  owner.CancelReal ();
248  return;
249  }
250 
251  owner.TrySetResult (results);
252  }
253  }
254 
255  sealed class WhenAnyContinuation<T> : IContinuation where T : Task
256  {
257  readonly Task<T> owner;
258  readonly IList<T> tasks;
259  AtomicBooleanValue executed;
260 
261  public WhenAnyContinuation (Task<T> owner, IList<T> tasks)
262  {
263  this.owner = owner;
264  this.tasks = tasks;
265  executed = new AtomicBooleanValue ();
266  }
267 
268  public void Execute ()
269  {
270  if (!executed.TryRelaxedSet ())
271  return;
272 
273  bool owner_notified = false;
274  for (int i = 0; i < tasks.Count; ++i) {
275  var task = tasks[i];
276  if (!task.IsCompleted) {
277  task.RemoveContinuation (this);
278  continue;
279  }
280 
281  if (owner_notified)
282  continue;
283 
284  owner.TrySetResult (task);
285  owner_notified = true;
286  }
287  }
288  }
289 
290  sealed class ManualResetContinuation : IContinuation, IDisposable
291  {
292  readonly ManualResetEventSlim evt;
293 
294  public ManualResetContinuation ()
295  {
296  this.evt = new ManualResetEventSlim ();
297  }
298 
299  public ManualResetEventSlim Event {
300  get {
301  return evt;
302  }
303  }
304 
305  public void Dispose ()
306  {
307  evt.Dispose ();
308  }
309 
310  public void Execute ()
311  {
312  evt.Set ();
313  }
314  }
315 
316  sealed class CountdownContinuation : IContinuation, IDisposable
317  {
318  readonly CountdownEvent evt;
319 
320  public CountdownContinuation (int initialCount)
321  {
322  this.evt = new CountdownEvent (initialCount);
323  }
324 
325  public CountdownEvent Event {
326  get {
327  return evt;
328  }
329  }
330 
331  public void Dispose ()
332  {
333  evt.Dispose ();
334  }
335 
336  public void Execute ()
337  {
338  evt.Signal ();
339  }
340  }
341 }
342 
343 #endif