Replace SIZEOF_REGISTER with sizeof(mgreg_t) for consistency with sizeof(gpointer)
[mono.git] / mcs / class / corlib / System.Threading.Tasks / Parallel.cs
1 // Parallel.cs
2 //
3 // Copyright (c) 2008 Jérémie "Garuma" Laval
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 //
24
25 #if NET_4_0
26 using System;
27 using System.Collections.Generic;
28 using System.Collections.Concurrent;
29 using System.Threading;
30
31 namespace System.Threading.Tasks
32 {
33         public static class Parallel
34         {
35                 internal static int GetBestWorkerNumber ()
36                 {
37                         return GetBestWorkerNumber (TaskScheduler.Current);
38                 }
39
40                 internal static int GetBestWorkerNumber (TaskScheduler scheduler)
41                 {
42                         return scheduler.MaximumConcurrencyLevel;
43                 }
44
45                 static int GetBestWorkerNumber (int from, int to, ParallelOptions options, out int step)
46                 {
47                         int num = Math.Min (GetBestWorkerNumber (),
48                                             options != null && options.MaxDegreeOfParallelism != -1 ? options.MaxDegreeOfParallelism : int.MaxValue);
49                         // Integer range that each task process
50                         if ((step = (to - from) / num) < 5) {
51                                 step = 5;
52                                 num = (to - from) / 5;
53                                 if (num < 1)
54                                         num = 1;
55                         }
56
57                         return num;
58                 }
59
60                 static void HandleExceptions (IEnumerable<Task> tasks)
61                 {
62                         HandleExceptions (tasks, null);
63                 }
64
65                 static void HandleExceptions (IEnumerable<Task> tasks, ParallelLoopState.ExternalInfos infos)
66                 {
67                         List<Exception> exs = new List<Exception> ();
68                         foreach (Task t in tasks) {
69                                 if (t.Exception != null)
70                                         exs.Add (t.Exception);
71                         }
72
73                         if (exs.Count > 0) {
74                                 if (infos != null)
75                                         infos.IsExceptional = true;
76
77                                 throw new AggregateException (exs);
78                         }
79                 }
80
81                 static void InitTasks (Task[] tasks, int count, Action action, ParallelOptions options)
82                 {
83                         TaskCreationOptions creation = TaskCreationOptions.LongRunning;
84
85                         for (int i = 0; i < count; i++) {
86                                 if (options == null)
87                                         tasks [i] = Task.Factory.StartNew (action, creation);
88                                 else
89                                         tasks [i] = Task.Factory.StartNew (action, options.CancellationToken, creation, options.TaskScheduler);
90                         }
91                 }
92
93                 #region For
94
95                 public static ParallelLoopResult For (int from, int to, Action<int> action)
96                 {
97                         return For (from, to, ParallelOptions.Default, action);
98                 }
99
100                 public static ParallelLoopResult For (int from, int to, Action<int, ParallelLoopState> action)
101                 {
102                         return For (from, to, ParallelOptions.Default, action);
103                 }
104
105                 public static ParallelLoopResult For (int from, int to, ParallelOptions options, Action<int> action)
106                 {
107                         return For (from, to, options, (index, state) => action (index));
108                 }
109
110                 public static ParallelLoopResult For (int from, int to, ParallelOptions options, Action<int, ParallelLoopState> action)
111                 {
112                         return For<object> (from, to, options, () => null, (i, s, l) => { action (i, s); return null; }, _ => {});
113                 }
114
115                 public static ParallelLoopResult For<TLocal> (int from,
116                                                               int to,
117                                                               Func<TLocal> init,
118                                                               Func<int, ParallelLoopState, TLocal, TLocal> action,
119                                                               Action<TLocal> destruct)
120                 {
121                         return For<TLocal> (from, to, ParallelOptions.Default, init, action, destruct);
122                 }
123
124                 public static ParallelLoopResult For<TLocal> (int from,
125                                                               int to,
126                                                               ParallelOptions options,
127                                                               Func<TLocal> init,
128                                                               Func<int, ParallelLoopState, TLocal, TLocal> action,
129                                                               Action<TLocal> destruct)
130                 {
131                         if (action == null)
132                                 throw new ArgumentNullException ("action");
133                         if (init == null)
134                                 throw new ArgumentNullException ("localInit");
135                         if (destruct == null)
136                                 throw new ArgumentNullException ("localFinally");
137                         if (options == null)
138                                 throw new ArgumentNullException ("options");
139                         if (from >= to)
140                                 return new ParallelLoopResult (null, true);
141
142                         // Number of task to be launched (normally == Env.ProcessorCount)
143                         int step;
144                         int num = GetBestWorkerNumber (from, to, options, out step);
145
146                         Task[] tasks = new Task [num];
147
148                         StealRange[] ranges = new StealRange[num];
149                         for (int i = 0; i < num; i++)
150                                 ranges[i] = new StealRange (from, i, step);
151
152                         ParallelLoopState.ExternalInfos infos = new ParallelLoopState.ExternalInfos ();
153
154                         int currentIndex = -1;
155
156                         Action workerMethod = delegate {
157                                 int localWorker = Interlocked.Increment (ref currentIndex);
158                                 StealRange range = ranges[localWorker];
159                                 int index = range.Actual;
160                                 int stopIndex = localWorker + 1 == num ? to : Math.Min (to, index + step);
161                                 TLocal local = init ();
162
163                                 ParallelLoopState state = new ParallelLoopState (infos);
164                                 CancellationToken token = options.CancellationToken;
165
166                                 try {
167                                         for (int i = index; i < stopIndex; range.Actual = ++i) {
168                                                 if (infos.IsStopped)
169                                                         return;
170
171                                                 token.ThrowIfCancellationRequested ();
172
173                                                 if (infos.LowestBreakIteration != null && infos.LowestBreakIteration > i)
174                                                         return;
175
176                                                 state.CurrentIteration = i;
177                                                 local = action (i, state, local);
178                                                 if (i >= stopIndex - range.Stolen)
179                                                         break;
180                                         }
181
182                                         // Try to steal from our right neighbor (cyclic)
183                                         int len = num + localWorker;
184                                         for (int sIndex = localWorker + 1; sIndex < len; ++sIndex) {
185                                                 int extWorker = sIndex % num;
186                                                 range = ranges[extWorker];
187
188                                                 stopIndex = extWorker + 1 == num ? to : Math.Min (to, from + (extWorker + 1) * step);
189
190                                                 int stolen;
191                                                 do {
192                                                         stolen = range.Stolen;
193                                                         if (stopIndex - stolen > range.Actual)
194                                                                 goto next;
195                                                 } while (Interlocked.CompareExchange (ref range.Stolen, stolen + 1, stolen) != stolen);
196
197                                                 stolen = stopIndex - stolen - 1;
198
199                                                 if (stolen > range.Actual)
200                                                         local = action (stolen, state, local);
201
202                                         next:
203                                                 continue;
204                                         }
205                                 } finally {
206                                         destruct (local);
207                                 }
208                         };
209
210                         InitTasks (tasks, num, workerMethod, options);
211
212                         try {
213                                 Task.WaitAll (tasks);
214                         } catch {
215                                 HandleExceptions (tasks, infos);
216                         }
217
218                         return new ParallelLoopResult (infos.LowestBreakIteration, !(infos.IsStopped || infos.IsExceptional));
219                 }
220
221                 class StealRange
222                 {
223                         public int Stolen;
224                         public int Actual;
225
226                         public StealRange (int from, int i, int step)
227                         {
228                                 Actual = from + i * step;
229                         }
230                 }
231
232                 #endregion
233
234                 #region Foreach
235                 static ParallelLoopResult ForEach<TSource, TLocal> (Func<int, IList<IEnumerator<TSource>>> enumerable, ParallelOptions options,
236                                                                     Func<TLocal> init, Func<TSource, ParallelLoopState, TLocal, TLocal> action,
237                                                                     Action<TLocal> destruct)
238                 {
239                         if (enumerable == null)
240                                 throw new ArgumentNullException ("source");
241                         if (options == null)
242                                 throw new ArgumentNullException ("options");
243                         if (action == null)
244                                 throw new ArgumentNullException ("action");
245                         if (init == null)
246                                 throw new ArgumentNullException ("init");
247                         if (destruct == null)
248                                 throw new ArgumentNullException ("destruct");
249
250                         int num = Math.Min (GetBestWorkerNumber (),
251                                             options != null && options.MaxDegreeOfParallelism != -1 ? options.MaxDegreeOfParallelism : int.MaxValue);
252
253                         Task[] tasks = new Task[num];
254                         ParallelLoopState.ExternalInfos infos = new ParallelLoopState.ExternalInfos ();
255
256                         SimpleConcurrentBag<TSource> bag = new SimpleConcurrentBag<TSource> (num);
257                         const int bagCount = 5;
258
259                         IList<IEnumerator<TSource>> slices = enumerable (num);
260
261                         int sliceIndex = -1;
262
263                         Action workerMethod = delegate {
264                                 IEnumerator<TSource> slice = slices[Interlocked.Increment (ref sliceIndex)];
265
266                                 TLocal local = init ();
267                                 ParallelLoopState state = new ParallelLoopState (infos);
268                                 int workIndex = bag.GetNextIndex ();
269                                 CancellationToken token = options.CancellationToken;
270
271                                 try {
272                                         bool cont = true;
273                                         TSource element;
274
275                                         while (cont) {
276                                                 if (infos.IsStopped || infos.IsBroken.Value)
277                                                         return;
278
279                                                 token.ThrowIfCancellationRequested ();
280
281                                                 for (int i = 0; i < bagCount && (cont = slice.MoveNext ()); i++) {
282                                                         bag.Add (workIndex, slice.Current);
283                                                 }
284
285                                                 for (int i = 0; i < bagCount && bag.TryTake (workIndex, out element); i++) {
286                                                         if (infos.IsStopped)
287                                                                 return;
288
289                                                         token.ThrowIfCancellationRequested ();
290
291                                                         local = action (element, state, local);
292                                                 }
293                                         }
294
295                                         while (bag.TrySteal (workIndex, out element)) {
296                                                 token.ThrowIfCancellationRequested ();
297
298                                                 local = action (element, state, local);
299
300                                                 if (infos.IsStopped || infos.IsBroken.Value)
301                                                         return;
302                                         }
303                                 } finally {
304                                         destruct (local);
305                                 }
306                         };
307
308                         InitTasks (tasks, num, workerMethod, options);
309
310                         try {
311                                 Task.WaitAll (tasks);
312                         } catch {
313                                 HandleExceptions (tasks, infos);
314                         }
315
316                         return new ParallelLoopResult (infos.LowestBreakIteration, !(infos.IsStopped || infos.IsExceptional));
317                 }
318
319                 public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> enumerable, Action<TSource> action)
320                 {
321                         if (enumerable == null)
322                                 throw new ArgumentNullException ("source");
323                         if (action == null)
324                                 throw new ArgumentNullException ("action");
325
326                         return ForEach<TSource, object> (Partitioner.Create (enumerable),
327                                                          ParallelOptions.Default,
328                                                          () => null,
329                                                          (e, s, l) => { action (e); return null; },
330                                                          _ => {});
331                 }
332
333                 public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> enumerable, Action<TSource, ParallelLoopState> action)
334                 {
335                         if (enumerable == null)
336                                 throw new ArgumentNullException ("source");
337                         if (action == null)
338                                 throw new ArgumentNullException ("action");
339
340                         return ForEach<TSource, object> (Partitioner.Create (enumerable),
341                                                          ParallelOptions.Default,
342                                                          () => null,
343                                                          (e, s, l) => { action (e, s); return null; },
344                                                          _ => {});
345                 }
346
347                 public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> enumerable,
348                                                                    Action<TSource, ParallelLoopState, long> action)
349                 {
350                         if (enumerable == null)
351                                 throw new ArgumentNullException ("source");
352                         if (action == null)
353                                 throw new ArgumentNullException ("action");
354
355
356                         return ForEach<TSource, object> (Partitioner.Create (enumerable),
357                                                          ParallelOptions.Default,
358                                                          () => null,
359                                                          (e, s, l) => { action (e, s, -1); return null; },
360                                                          _ => {});
361                 }
362
363                 public static ParallelLoopResult ForEach<TSource> (Partitioner<TSource> source,
364                                                                    Action<TSource, ParallelLoopState> body)
365                 {
366                         if (body == null)
367                                 throw new ArgumentNullException ("body");
368
369                         return ForEach<TSource, object> (source,
370                                                          ParallelOptions.Default,
371                                                          () => null,
372                                                          (e, s, l) => { body (e, s); return null; },
373                                                          _ => {});
374                 }
375
376                 public static ParallelLoopResult ForEach<TSource> (OrderablePartitioner<TSource> source,
377                                                                    Action<TSource, ParallelLoopState, long> body)
378
379                 {
380                         if (body == null)
381                                 throw new ArgumentNullException ("body");
382
383                         return ForEach<TSource, object> (source,
384                                                          ParallelOptions.Default,
385                                                          () => null,
386                                                          (e, s, i, l) => { body (e, s, i); return null; },
387                                                          _ => {});
388                 }
389
390                 public static ParallelLoopResult ForEach<TSource> (Partitioner<TSource> source,
391                                                                    Action<TSource> body)
392
393                 {
394                         if (body == null)
395                                 throw new ArgumentNullException ("body");
396
397                         return ForEach<TSource, object> (source,
398                                                          ParallelOptions.Default,
399                                                          () => null,
400                                                          (e, s, l) => { body (e); return null; },
401                                                          _ => {});
402                 }
403
404                 public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source,
405                                                                    ParallelOptions parallelOptions,
406                                                                    Action<TSource> body)
407                 {
408                         if (source == null)
409                                 throw new ArgumentNullException ("source");
410                         if (body == null)
411                                 throw new ArgumentNullException ("body");
412
413                         return ForEach<TSource, object> (Partitioner.Create (source),
414                                                          parallelOptions,
415                                                          () => null,
416                                                          (e, s, l) => { body (e); return null; },
417                                                          _ => {});
418                 }
419
420                 public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source, ParallelOptions parallelOptions,
421                                                                    Action<TSource, ParallelLoopState> body)
422                 {
423                         if (source == null)
424                                 throw new ArgumentNullException ("source");
425                         if (body == null)
426                                 throw new ArgumentNullException ("body");
427
428                         return ForEach<TSource, object> (Partitioner.Create (source),
429                                                          parallelOptions,
430                                                          () => null,
431                                                          (e, s, l) => { body (e, s); return null; },
432                                                          _ => {});
433                 }
434
435                 public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source, ParallelOptions parallelOptions,
436                                                                    Action<TSource, ParallelLoopState, long> body)
437                 {
438                         if (source == null)
439                                 throw new ArgumentNullException ("source");
440                         if (body == null)
441                                 throw new ArgumentNullException ("body");
442
443                         return ForEach<TSource, object> (Partitioner.Create (source),
444                                                          parallelOptions,
445                                                          () => null,
446                                                          (e, s, i, l) => { body (e, s, i); return null; },
447                                                          _ => {});
448                 }
449
450                 public static ParallelLoopResult ForEach<TSource> (OrderablePartitioner<TSource> source, ParallelOptions parallelOptions,
451                                                                    Action<TSource, ParallelLoopState, long> body)
452
453                 {
454                         if (body == null)
455                                 throw new ArgumentNullException ("body");
456
457                         return ForEach<TSource, object> (source,
458                                                          parallelOptions,
459                                                          () => null,
460                                                          (e, s, i, l) => { body (e, s, i); return null; },
461                                                          _ => {});
462                 }
463
464                 public static ParallelLoopResult ForEach<TSource> (Partitioner<TSource> source, ParallelOptions parallelOptions,
465                                                                    Action<TSource> body)
466                 {
467                         if (body == null)
468                                 throw new ArgumentNullException ("body");
469
470                         return ForEach<TSource, object> (source,
471                                                          parallelOptions,
472                                                          () => null,
473                                                          (e, s, l) => { body (e); return null; },
474                                                          _ => {});
475                 }
476
477                 public static ParallelLoopResult ForEach<TSource> (Partitioner<TSource> source, ParallelOptions parallelOptions,
478                                                                    Action<TSource, ParallelLoopState> body)
479                 {
480                         return ForEach<TSource, object> (source,
481                                                          parallelOptions,
482                                                          () => null,
483                                                          (e, s, l) => { body (e, s); return null; },
484                                                          _ => {});
485                 }
486
487                 public static ParallelLoopResult ForEach<TSource, TLocal> (IEnumerable<TSource> source, Func<TLocal> localInit,
488                                                                            Func<TSource, ParallelLoopState, TLocal, TLocal> body,
489                                                                            Action<TLocal> localFinally)
490                 {
491                         if (source == null)
492                                 throw new ArgumentNullException ("source");
493
494                         return ForEach<TSource, TLocal> ((Partitioner<TSource>)Partitioner.Create (source),
495                                                          ParallelOptions.Default,
496                                                          localInit,
497                                                          body,
498                                                          localFinally);
499                 }
500
501                 public static ParallelLoopResult ForEach<TSource, TLocal> (IEnumerable<TSource> source, Func<TLocal> localInit,
502                                                                            Func<TSource, ParallelLoopState, long, TLocal, TLocal> body,
503                                                                            Action<TLocal> localFinally)
504                 {
505                         return ForEach<TSource, TLocal> (Partitioner.Create (source),
506                                                          ParallelOptions.Default,
507                                                          localInit,
508                                                          body,
509                                                          localFinally);
510                 }
511
512                 public static ParallelLoopResult ForEach<TSource, TLocal> (OrderablePartitioner<TSource> source, Func<TLocal> localInit,
513                                                                            Func<TSource, ParallelLoopState, long, TLocal, TLocal> body,
514                                                                            Action<TLocal> localFinally)
515                 {
516                         return ForEach<TSource, TLocal> (source, ParallelOptions.Default, localInit, body, localFinally);
517                 }
518
519                 public static ParallelLoopResult ForEach<TSource, TLocal> (Partitioner<TSource> source, Func<TLocal> localInit,
520                                                                            Func<TSource, ParallelLoopState, TLocal, TLocal> body,
521                                                                            Action<TLocal> localFinally)
522                 {
523                         return ForEach<TSource, TLocal> (source, ParallelOptions.Default, localInit, body, localFinally);
524                 }
525
526                 public static ParallelLoopResult ForEach<TSource, TLocal> (IEnumerable<TSource> source, ParallelOptions parallelOptions,
527                                                                            Func<TLocal> localInit,
528                                                                            Func<TSource, ParallelLoopState, TLocal, TLocal> body,
529                                                                            Action<TLocal> localFinally)
530                 {
531                         if (source == null)
532                                 throw new ArgumentNullException ("source");
533
534                         return ForEach<TSource, TLocal> (Partitioner.Create (source), parallelOptions, localInit, body, localFinally);
535                 }
536
537                 public static ParallelLoopResult ForEach<TSource, TLocal> (IEnumerable<TSource> source, ParallelOptions parallelOptions,
538                                                                            Func<TLocal> localInit,
539                                                                            Func<TSource, ParallelLoopState, long, TLocal, TLocal> body,
540                                                                            Action<TLocal> localFinally)
541                 {
542                         if (source == null)
543                                 throw new ArgumentNullException ("source");
544
545                         return ForEach<TSource, TLocal> (Partitioner.Create (source), parallelOptions, localInit, body, localFinally);
546                 }
547
548                 public static ParallelLoopResult ForEach<TSource, TLocal> (Partitioner<TSource> enumerable, ParallelOptions options,
549                                                                            Func<TLocal> init,
550                                                                            Func<TSource, ParallelLoopState, TLocal, TLocal> action,
551                                                                            Action<TLocal> destruct)
552                 {
553                         if (enumerable == null)
554                                 throw new ArgumentNullException ("source");
555                         if (action == null)
556                                 throw new ArgumentNullException ("action");
557
558                         return ForEach<TSource, TLocal> (enumerable.GetPartitions, options, init, action, destruct);
559                 }
560
561                 public static ParallelLoopResult ForEach<TSource, TLocal> (OrderablePartitioner<TSource> enumerable, ParallelOptions options,
562                                                                            Func<TLocal> init,
563                                                                            Func<TSource, ParallelLoopState, long, TLocal, TLocal> action,
564                                                                            Action<TLocal> destruct)
565                 {
566                         if (enumerable == null)
567                                 throw new ArgumentNullException ("source");
568                         if (action == null)
569                                 throw new ArgumentNullException ("action");
570
571                         return ForEach<KeyValuePair<long, TSource>, TLocal> (enumerable.GetOrderablePartitions,
572                                                                              options,
573                                                                              init,
574                                                                              (e, s, l) => action (e.Value, s, e.Key, l),
575                                                                              destruct);
576                 }
577                 #endregion
578
579                 #region Invoke
580                 public static void Invoke (params Action[] actions)
581                 {
582                         if (actions == null)
583                                 throw new ArgumentNullException ("actions");
584
585                         Invoke (actions, (Action a) => Task.Factory.StartNew (a));
586                 }
587
588                 public static void Invoke (ParallelOptions parallelOptions, params Action[] actions)
589                 {
590                         if (parallelOptions == null)
591                                 throw new ArgumentNullException ("parallelOptions");
592                         if (actions == null)
593                                 throw new ArgumentNullException ("actions");
594
595                         Invoke (actions, (Action a) => Task.Factory.StartNew (a, parallelOptions.CancellationToken, TaskCreationOptions.None, parallelOptions.TaskScheduler));
596                 }
597
598                 static void Invoke (Action[] actions, Func<Action, Task> taskCreator)
599                 {
600                         if (actions.Length == 0)
601                                 throw new ArgumentException ("actions is empty");
602
603                         // Execute it directly
604                         if (actions.Length == 1 && actions[0] != null)
605                                 actions[0] ();
606
607                         bool shouldThrow = false;
608                         Task[] ts = Array.ConvertAll (actions, delegate (Action a) {
609                                 if (a == null) {
610                                         shouldThrow = true;
611                                         return null;
612                                 }
613
614                                 return taskCreator (a);
615                         });
616
617                         if (shouldThrow)
618                                 throw new ArgumentException ("One action in actions is null", "actions");
619
620                         try {
621                                 Task.WaitAll (ts);
622                         } catch {
623                                 HandleExceptions (ts);
624                         }
625                 }
626                 #endregion
627
628                 #region SpawnBestNumber, used by PLinq
629                 internal static Task[] SpawnBestNumber (Action action, Action callback)
630                 {
631                         return SpawnBestNumber (action, -1, callback);
632                 }
633
634                 internal static Task[] SpawnBestNumber (Action action, int dop, Action callback)
635                 {
636                         return SpawnBestNumber (action, dop, false, callback);
637                 }
638
639                 internal static Task[] SpawnBestNumber (Action action, int dop, bool wait, Action callback)
640                 {
641                         // Get the optimum amount of worker to create
642                         int num = dop == -1 ? (wait ? GetBestWorkerNumber () + 1 : GetBestWorkerNumber ()) : dop;
643
644                         // Initialize worker
645                         CountdownEvent evt = new CountdownEvent (num);
646                         Task[] tasks = new Task [num];
647                         for (int i = 0; i < num; i++) {
648                                 tasks [i] = Task.Factory.StartNew (() => {
649                                         action ();
650                                         evt.Signal ();
651                                         if (callback != null && evt.IsSet)
652                                                 callback ();
653                                 });
654                         }
655
656                         // If explicitely told, wait for all workers to complete
657                         // and thus let main thread participate in the processing
658                         if (wait)
659                                 Task.WaitAll (tasks);
660
661                         return tasks;
662                 }
663                 #endregion
664         }
665 }
666 #endif