#if NET20 || NET30 || NET35 || !NET_4_6 using System.Collections.Generic; using System.Diagnostics.Contracts; namespace System.Threading.Tasks { public partial class Task { /// /// Creates a task that will complete when all of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of all of the supplied tasks. /// /// /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// /// /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// /// /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// /// /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. /// /// /// /// The argument was null. /// /// /// The collection contained a null task. /// public static Task WhenAll(IEnumerable tasks) { // Take a more efficient path if tasks is actually an array var taskArray = tasks as Task[]; if (taskArray != null) { return WhenAll(taskArray); } // Skip a List allocation/copy if tasks is a collection var taskCollection = tasks as ICollection; if (taskCollection != null) { var index = 0; taskArray = new Task[taskCollection.Count]; foreach (var task in tasks) { if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } taskArray[index++] = task; } return InternalWhenAll(taskArray); } // Do some argument checking and convert tasks to a List (and later an array). if (tasks == null) { throw new ArgumentNullException("tasks"); } var taskList = new List(); foreach (var task in tasks) { if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } taskList.Add(task); } // Delegate the rest to InternalWhenAll() return InternalWhenAll(taskList.ToArray()); } /// /// Creates a task that will complete when all of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of all of the supplied tasks. /// /// /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// /// /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// /// /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// /// /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. /// /// /// /// The argument was null. /// /// /// The array contained a null task. /// public static Task WhenAll(params Task[] tasks) { // Do some argument checking and make a defensive copy of the tasks array if (tasks == null) { throw new ArgumentNullException("tasks"); } Contract.EndContractBlock(); var taskCount = tasks.Length; if (taskCount == 0) { return InternalWhenAll(tasks); // Small optimization in the case of an empty array. } var tasksCopy = new Task[taskCount]; for (var i = 0; i < taskCount; i++) { var task = tasks[i]; if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } tasksCopy[i] = task; } // The rest can be delegated to InternalWhenAll() return InternalWhenAll(tasksCopy); } /// /// Creates a task that will complete when all of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of all of the supplied tasks. /// /// /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// /// /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// /// /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// The Result of the returned task will be set to an array containing all of the results of the /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result). /// /// /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements. /// /// /// /// The argument was null. /// /// /// The collection contained a null task. /// public static Task WhenAll(IEnumerable> tasks) { // Take a more efficient route if tasks is actually an array var taskArray = tasks as Task[]; if (taskArray != null) { return WhenAll(taskArray); } // Skip a List allocation/copy if tasks is a collection var taskCollection = tasks as ICollection>; if (taskCollection != null) { var index = 0; taskArray = new Task[taskCollection.Count]; foreach (var task in tasks) { if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } taskArray[index++] = task; } return InternalWhenAll(taskArray); } // Do some argument checking and convert tasks into a List (later an array) if (tasks == null) { throw new ArgumentNullException("tasks"); } var taskList = new List>(); foreach (var task in tasks) { if (task == null) { throw new ArgumentException("Task_MultiTaskContinuation_NullTask", "tasks"); } taskList.Add(task); } // Delegate the rest to InternalWhenAll(). return InternalWhenAll(taskList.ToArray()); } /// /// Creates a task that will complete when all of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of all of the supplied tasks. /// /// /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// /// /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// /// /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// The Result of the returned task will be set to an array containing all of the results of the /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result). /// /// /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements. /// /// /// /// The argument was null. /// /// /// The array contained a null task. /// public static Task WhenAll(params Task[] tasks) { // Do some argument checking and make a defensive copy of the tasks array if (tasks == null) { throw new ArgumentNullException("tasks"); } Contract.EndContractBlock(); var taskCount = tasks.Length; if (taskCount == 0) { return InternalWhenAll(tasks); // small optimization in the case of an empty task array } var tasksCopy = new Task[taskCount]; for (var i = 0; i < taskCount; i++) { var task = tasks[i]; if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } tasksCopy[i] = task; } // Delegate the rest to InternalWhenAll() return InternalWhenAll(tasksCopy); } /// Returns true if any of the supplied tasks require wait notification. /// The tasks to check. /// true if any of the tasks require notification; otherwise, false. internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(IEnumerable tasks) { if (tasks == null) { Contract.Assert(false, "Expected non-null array of tasks"); throw new ArgumentNullException("tasks"); } foreach (var task in tasks) { if ( task != null && task.IsWaitNotificationEnabled && task.ShouldNotifyDebuggerOfWaitCompletion ) // potential recursion { return true; } } return false; } // Some common logic to support WhenAll() methods // tasks should be a defensive copy. private static Task InternalWhenAll(Task[] tasks) { Contract.Requires(tasks != null, "Expected a non-null tasks array"); // take shortcut if there are no tasks upon which to wait if (tasks.Length == 0) { return CompletedTask; } return new WhenAllPromise(tasks); } // Some common logic to support WhenAll methods private static Task InternalWhenAll(Task[] tasks) { Contract.Requires(tasks != null, "Expected a non-null tasks array"); // take shortcut if there are no tasks upon which to wait if (tasks.Length == 0) { return FromResult(new TResult[0]); } return new WhenAllPromise(tasks); } /// /// Creates a task that will complete when any of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. /// /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state. /// /// /// The argument was null. /// /// /// The array contained a null task, or was empty. /// public static Task WhenAny(params Task[] tasks) { if (tasks == null) { throw new ArgumentNullException("tasks"); } if (tasks.Length == 0) { throw new ArgumentException("The tasks argument contains no tasks.", "tasks"); } Contract.EndContractBlock(); // Make a defensive copy, as the user may manipulate the tasks array // after we return but before the WhenAny asynchronously completes. var taskCount = tasks.Length; var tasksCopy = new Task[taskCount]; for (var index = 0; index < taskCount; index++) { var task = tasks[index]; if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } tasksCopy[index] = task; } var signaledTaskIndex = -1; return PrivateWhenAny(tasksCopy, ref signaledTaskIndex); } /// /// Creates a task that will complete when any of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. /// /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state. /// /// /// The argument was null. /// /// /// The collection contained a null task, or was empty. /// public static Task WhenAny(IEnumerable tasks) { if (tasks == null) { throw new ArgumentNullException("tasks"); } Contract.EndContractBlock(); // Make a defensive copy, as the user may manipulate the tasks collection // after we return but before the WhenAny asynchronously completes. var taskList = new List(); foreach (var task in tasks) { if (task == null) { throw new ArgumentException("The tasks argument included a null value.", "tasks"); } taskList.Add(task); } if (taskList.Count == 0) { throw new ArgumentException("The tasks argument contains no tasks.", "tasks"); } var signaledTaskIndex = -1; return PrivateWhenAny(taskList, ref signaledTaskIndex); } /// /// Creates a task that will complete when any of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. /// /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state. /// /// /// The argument was null. /// /// /// The array contained a null task, or was empty. /// public static Task> WhenAny(params Task[] tasks) { // We would just like to do this: // return (Task>) WhenAny( (Task[]) tasks); // but classes are not covariant to enable casting Task to Task>. // Call WhenAny(Task[]) for basic functionality var intermediate = WhenAny((Task[])tasks); // Return a continuation task with the correct result type return intermediate.ContinueWith(Task.ContinuationConvertion, default(CancellationToken), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); } /// /// Creates a task that will complete when any of the supplied tasks have completed. /// /// The tasks to wait on for completion. /// A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. /// /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state. /// /// /// The argument was null. /// /// /// The collection contained a null task, or was empty. /// public static Task> WhenAny(IEnumerable> tasks) { // We would just like to do this: // return (Task>) WhenAny( (IEnumerable) tasks); // but classes are not covariant to enable casting Task to Task>. // Call WhenAny(IEnumerable) for basic functionality var intermediate = WhenAny((IEnumerable)tasks); // Return a continuation task with the correct result type return intermediate.ContinueWith(Task.ContinuationConvertion, default(CancellationToken), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default); } } } #endif