Rework TaskFactory.FromAsync, it was a bit messy.
[mono.git] / mcs / class / corlib / System.Threading.Tasks / TaskFactory_T.cs
1 // 
2 // TaskFactory_T.cs
3 //  
4 // Authors:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 //       Marek Safar <marek.safar@gmail.com>
7 // 
8 // Copyright (c) 2009 Jérémie "Garuma" Laval
9 // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 // 
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28
29 #if NET_4_0 || MOBILE
30
31 using System;
32 using System.Threading;
33
34 namespace System.Threading.Tasks
35 {
36         public class TaskFactory<TResult>
37         {
38                 readonly TaskScheduler scheduler;
39                 TaskCreationOptions creationOptions;
40                 TaskContinuationOptions continuationOptions;
41                 CancellationToken cancellationToken;
42                 
43                 TaskFactory parent;
44
45                 const TaskCreationOptions FromAsyncOptionsNotSupported = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
46                 
47                 public TaskFactory ()
48                         : this (CancellationToken.None)
49                 {       
50                 }
51                 
52                 public TaskFactory (TaskScheduler scheduler)
53                         : this (CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, scheduler)
54                 {       
55                 }
56                 
57                 public TaskFactory (CancellationToken cancellationToken)
58                         : this (cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, null)
59                 {       
60                 }
61                 
62                 public TaskFactory (TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions)
63                         : this (CancellationToken.None, creationOptions, continuationOptions, null)
64                 {       
65                 }
66                 
67                 public TaskFactory (CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
68                 {
69                         this.cancellationToken = cancellationToken;
70                         this.scheduler = scheduler;
71                         this.creationOptions = creationOptions;
72                         this.continuationOptions = continuationOptions;
73
74                         TaskFactory.CheckContinuationOptions (continuationOptions);
75                         
76                         this.parent = new TaskFactory (cancellationToken, creationOptions, continuationOptions, scheduler);
77                 }
78
79                 public TaskScheduler Scheduler {
80                         get {
81                                 return scheduler;
82                         }
83                 }
84                 
85                 public TaskContinuationOptions ContinuationOptions {
86                         get {
87                                 return continuationOptions;
88                         }
89                 }
90                 
91                 public TaskCreationOptions CreationOptions {
92                         get {
93                                 return creationOptions;
94                         }
95                 }
96                 
97                 public CancellationToken CancellationToken {
98                         get {
99                                 return cancellationToken;
100                         }
101                 }
102                 
103                 #region StartNew for Task<TResult>      
104                 public Task<TResult> StartNew (Func<TResult> function)
105                 {
106                         return StartNew (function, cancellationToken, creationOptions, GetScheduler ());
107                 }
108                 
109                 public Task<TResult> StartNew (Func<TResult> function, TaskCreationOptions creationOptions)
110                 {
111                         return StartNew (function, cancellationToken, creationOptions, GetScheduler ());
112                 }
113                 
114                 public Task<TResult> StartNew (Func<TResult> function, CancellationToken cancellationToken)
115                 {
116                         return StartNew (function, cancellationToken, creationOptions, GetScheduler ());
117                 }
118                 
119                 public Task<TResult> StartNew (Func<TResult> function, 
120                                                CancellationToken cancellationToken,
121                                                TaskCreationOptions creationOptions,
122                                                TaskScheduler scheduler)
123                 {
124                         return StartNew ((o) => function (), null, cancellationToken, creationOptions, scheduler);
125                 }
126                 
127                 public Task<TResult> StartNew (Func<object, TResult> function, object state)
128                 {
129                         return StartNew (function, state, cancellationToken, creationOptions, GetScheduler ());
130                 }
131                 
132                 public Task<TResult> StartNew (Func<object, TResult> function, object state, TaskCreationOptions creationOptions)
133                 {
134                         return StartNew (function, state, cancellationToken, creationOptions, GetScheduler ());
135                 }
136                 
137                 public Task<TResult> StartNew (Func<object, TResult> function, object state, CancellationToken cancellationToken)
138                 {
139                         return StartNew (function, state, cancellationToken, creationOptions, GetScheduler ());
140                 }
141                 
142                 public Task<TResult> StartNew (Func<object, TResult> function, object state, 
143                                                CancellationToken cancellationToken,
144                                                TaskCreationOptions creationOptions,
145                                                TaskScheduler scheduler)
146                 {
147                         return parent.StartNew<TResult> (function, state, cancellationToken, creationOptions, scheduler);
148                 }
149                 #endregion
150                 
151                 #region Continue
152
153                 public Task<TResult> ContinueWhenAny (Task[] tasks,
154                                                       Func<Task, TResult> continuationFunction)
155                 {
156                         return ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
157                 }
158
159                 public Task<TResult> ContinueWhenAny (Task[] tasks,
160                                                       Func<Task, TResult> continuationFunction,
161                                                       CancellationToken cancellationToken)
162                 {
163                         return ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
164                 }
165
166                 public Task<TResult> ContinueWhenAny (Task[] tasks,
167                                                       Func<Task, TResult> continuationFunction,
168                                                       TaskContinuationOptions continuationOptions)
169                 {
170                         return ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
171                 }
172
173                 public Task<TResult> ContinueWhenAny (Task[] tasks,
174                                                       Func<Task, TResult> continuationFunction,
175                                                       CancellationToken cancellationToken,
176                                                       TaskContinuationOptions continuationOptions,
177                                                       TaskScheduler scheduler)
178                 {
179                         return parent.ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, scheduler);
180                 }
181
182                 public Task<TResult> ContinueWhenAny<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
183                                                                          Func<Task<TAntecedentResult>, TResult> continuationFunction)
184                 {
185                         return ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
186                 }
187
188                 public Task<TResult> ContinueWhenAny<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
189                                                                          Func<Task<TAntecedentResult>, TResult> continuationFunction,
190                                                                          CancellationToken cancellationToken)
191                 {
192                         return ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
193                 }
194
195                 public Task<TResult> ContinueWhenAny<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
196                                                                          Func<Task<TAntecedentResult>, TResult> continuationFunction,
197                                                                          TaskContinuationOptions continuationOptions)
198                 {
199                         return ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
200                 }
201
202                 public Task<TResult> ContinueWhenAny<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
203                                                                          Func<Task<TAntecedentResult>, TResult> continuationFunction,
204                                                                          CancellationToken cancellationToken,
205                                                                          TaskContinuationOptions continuationOptions,
206                                                                          TaskScheduler scheduler)
207                 {
208                         return parent.ContinueWhenAny (tasks, continuationFunction, cancellationToken, continuationOptions, scheduler);
209                 }
210                 
211                 public Task<TResult> ContinueWhenAll (Task[] tasks, Func<Task[], TResult> continuationFunction)
212                 {
213                         return ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
214                 }
215                 
216                 public Task<TResult> ContinueWhenAll (Task[] tasks,
217                                                       Func<Task[], TResult> continuationFunction,
218                                                       TaskContinuationOptions continuationOptions)
219                 {
220                         return ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
221                 }
222                 
223                 public Task<TResult> ContinueWhenAll (Task[] tasks,
224                                                       Func<Task[], TResult> continuationFunction,
225                                                       CancellationToken cancellationToken)
226                 {
227                         return ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
228                 }
229                 
230                 public Task<TResult> ContinueWhenAll (Task[] tasks,
231                                                       Func<Task[], TResult> continuationFunction,
232                                                       CancellationToken cancellationToken,
233                                                       TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
234                 {
235                         return parent.ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, scheduler);
236                 }
237                 
238                 public Task<TResult> ContinueWhenAll<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
239                                                                          Func<Task<TAntecedentResult>[], TResult> continuationFunction)
240                 {
241                         return ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
242                 }
243                 
244                 public Task<TResult> ContinueWhenAll<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
245                                                                          Func<Task<TAntecedentResult>[], TResult> continuationFunction,
246                                                                          TaskContinuationOptions continuationOptions)
247                 {
248                         return ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
249                 }
250                 
251                 public Task<TResult> ContinueWhenAll<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
252                                                                          Func<Task<TAntecedentResult>[], TResult> continuationFunction,
253                                                                          CancellationToken cancellationToken)
254                 {
255                         return ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, GetScheduler ());
256                 }
257                 
258                 public Task<TResult> ContinueWhenAll<TAntecedentResult> (Task<TAntecedentResult>[] tasks,
259                                                                          Func<Task<TAntecedentResult>[], TResult> continuationFunction,
260                                                                          CancellationToken cancellationToken,
261                                                                          TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
262                 {
263                         return parent.ContinueWhenAll (tasks, continuationFunction, cancellationToken, continuationOptions, scheduler);
264                 }
265
266                 #endregion
267                 
268                 #region FromAsync
269                 
270                 public Task<TResult> FromAsync (IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod)
271                 {
272                         return FromAsync (asyncResult, endMethod, creationOptions);
273                 }
274                 
275                 public Task<TResult> FromAsync (IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod, TaskCreationOptions creationOptions)
276                 {
277                         return FromAsync (asyncResult, endMethod, creationOptions, GetScheduler ());
278                 }
279                 
280                 public Task<TResult> FromAsync (IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod, TaskCreationOptions creationOptions, TaskScheduler scheduler)
281                 {
282                         return FromIAsyncResult (asyncResult, endMethod, creationOptions, scheduler);
283                 }
284
285                 internal static Task<TResult> FromIAsyncResult (IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod, TaskCreationOptions creationOptions, TaskScheduler scheduler)
286                 {
287                         if (asyncResult == null)
288                             throw new ArgumentNullException ("asyncResult");
289
290                         if (endMethod == null)
291                             throw new ArgumentNullException ("endMethod");
292
293                         if (scheduler == null)
294                                 throw new ArgumentNullException ("scheduler");
295
296                         if ((creationOptions & FromAsyncOptionsNotSupported) != 0)
297                                 throw new ArgumentOutOfRangeException ("creationOptions");
298
299                         var source = new CancellationTokenSource ();
300                         var task = new Task<TResult> (l => {
301                                 try {
302                                         return endMethod (asyncResult);
303                                 } catch (OperationCanceledException) {
304                                         source.Cancel ();
305                                         source.Token.ThrowIfCancellationRequested ();
306                                 }
307                                 return default (TResult);
308                         }, null, source.Token, creationOptions);
309
310                         // Take quick path for completed operations
311                         if (asyncResult.IsCompleted) {
312                                 task.RunSynchronously (scheduler);
313                         } else {
314                                 ThreadPool.RegisterWaitForSingleObject (asyncResult.AsyncWaitHandle,
315                                                                         (s, t) => task.RunSynchronously (scheduler),
316                                                                         null, Timeout.Infinite, true);
317                         }
318
319                         return task;
320                 }
321
322                 public Task<TResult> FromAsync (Func<AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
323                                                 object state)
324                 {
325                         return FromAsync (beginMethod, endMethod, state, creationOptions);
326                 }
327
328                 public Task<TResult> FromAsync (Func<AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
329                                                 object state, TaskCreationOptions creationOptions)
330                 {
331                         return FromAsyncBeginEnd (beginMethod, endMethod, state, creationOptions);
332                 }
333
334                 internal static Task<TResult> FromAsyncBeginEnd (Func<AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
335                                                                  object state, TaskCreationOptions creationOptions)
336                 {
337                         if (beginMethod == null)
338                                 throw new ArgumentNullException ("beginMethod");
339
340                         if (endMethod == null)
341                                 throw new ArgumentNullException ("endMethod");
342
343                         if ((creationOptions & FromAsyncOptionsNotSupported) != 0)
344                                 throw new ArgumentOutOfRangeException ("creationOptions");
345
346                         var tcs = new TaskCompletionSource<TResult> (state, creationOptions);
347                         beginMethod (l => InnerInvoke (tcs, endMethod, l), state);
348
349                         return tcs.Task;
350                 }
351
352                 public Task<TResult> FromAsync<TArg1> (Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
353                                                        TArg1 arg1, object state)
354                 {
355                         return FromAsync (beginMethod, endMethod, arg1, state, creationOptions);
356                 }
357
358                 public Task<TResult> FromAsync<TArg1> (Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
359                                                        TArg1 arg1, object state, TaskCreationOptions creationOptions)
360                 {
361                         return FromAsyncBeginEnd (beginMethod, endMethod, arg1, state, creationOptions);
362                 }
363
364                 internal static Task<TResult> FromAsyncBeginEnd<TArg1> (Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod,
365                                                                         Func<IAsyncResult, TResult> endMethod,
366                                                                         TArg1 arg1, object state, TaskCreationOptions creationOptions)
367                 {
368                         if (beginMethod == null)
369                                 throw new ArgumentNullException ("beginMethod");
370
371                         if (endMethod == null)
372                                 throw new ArgumentNullException ("endMethod");
373
374                         if ((creationOptions & FromAsyncOptionsNotSupported) != 0)
375                                 throw new ArgumentOutOfRangeException ("creationOptions");
376
377                         var tcs = new TaskCompletionSource<TResult> (state, creationOptions);
378                         beginMethod (arg1, l => InnerInvoke (tcs, endMethod, l), state);
379
380                         return tcs.Task;
381                 }
382
383                 public Task<TResult> FromAsync<TArg1, TArg2> (Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
384                                                               TArg1 arg1, TArg2 arg2, object state)
385                 {
386                         return FromAsync (beginMethod, endMethod, arg1, arg2, state, creationOptions);
387                 }
388
389                 public Task<TResult> FromAsync<TArg1, TArg2> (Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
390                                                               TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
391                 {
392                         return FromAsyncBeginEnd (beginMethod, endMethod, arg1, arg2, state, creationOptions);
393                 }
394
395                 internal static Task<TResult> FromAsyncBeginEnd<TArg1, TArg2> (Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
396                                                                                   TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
397                 {
398                         if (beginMethod == null)
399                                 throw new ArgumentNullException ("beginMethod");
400
401                         if (endMethod == null)
402                                 throw new ArgumentNullException ("endMethod");
403
404                         if ((creationOptions & FromAsyncOptionsNotSupported) != 0)
405                                 throw new ArgumentOutOfRangeException ("creationOptions");
406
407                         var tcs = new TaskCompletionSource<TResult> (state, creationOptions);
408                         beginMethod (arg1, arg2, l => InnerInvoke (tcs, endMethod, l), state);
409
410                         return tcs.Task;
411                 }
412
413                 public Task<TResult> FromAsync<TArg1, TArg2, TArg3> (Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
414                                                                      TArg1 arg1, TArg2 arg2, TArg3 arg3, object state)
415                 {
416                         return FromAsync (beginMethod, endMethod, arg1, arg2, arg3, state, creationOptions);
417                 }
418
419                 public Task<TResult> FromAsync<TArg1, TArg2, TArg3> (Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
420                                                                      TArg1 arg1, TArg2 arg2, TArg3 arg3, object state,
421                                                                      TaskCreationOptions creationOptions)
422                 {
423                         return FromAsyncBeginEnd (beginMethod, endMethod, arg1, arg2, arg3, state, creationOptions);
424                 }
425
426                 internal static Task<TResult> FromAsyncBeginEnd<TArg1, TArg2, TArg3> (Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, Func<IAsyncResult, TResult> endMethod,
427                                                                                   TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
428                 {
429                         if (beginMethod == null)
430                                 throw new ArgumentNullException ("beginMethod");
431
432                         if (endMethod == null)
433                                 throw new ArgumentNullException ("endMethod");
434
435                         if ((creationOptions & FromAsyncOptionsNotSupported) != 0)
436                                 throw new ArgumentOutOfRangeException ("creationOptions");
437
438                         var tcs = new TaskCompletionSource<TResult> (state, creationOptions);
439                         beginMethod (arg1, arg2, arg3, l => InnerInvoke (tcs, endMethod, l), state);
440
441                         return tcs.Task;
442                 }
443
444                 #endregion
445
446                 TaskScheduler GetScheduler ()
447                 {
448                         return scheduler ?? TaskScheduler.Current;
449                 }
450
451                 static void InnerInvoke (TaskCompletionSource<TResult> tcs, Func<IAsyncResult, TResult> endMethod, IAsyncResult l)
452                 {
453                         try {
454                                 tcs.SetResult (endMethod (l));
455                         } catch (OperationCanceledException) {
456                                 tcs.SetCanceled ();
457                         } catch (Exception e) {
458                                 tcs.SetException (e);
459                         }
460                 }
461         }
462 }
463 #endif