public static void RunUntilCompletion( this Task task, TimeSpan pollingInterval = default, TaskWaitingStrategy taskWaitingStrategy = TaskWaitingStrategy.Sleep) { task.InternalRunUntilCompletion(pollingInterval, taskWaitingStrategy); }
/// <summary> /// Blocks on a task execution until it's in a <see cref="TaskStatus.Canceled" />, <see cref="TaskStatus.Faulted" />, or <see cref="TaskStatus.RanToCompletion" /> status, will start the task if in <see cref="TaskStatus.Created" />. /// </summary> /// <typeparam name="T">The type of the return value of the specified task.</typeparam> /// <param name="task">Task to wait on.</param> /// <param name="pollingInterval">OPTIONAL time to poll and check status of task; DEFAULT is <see cref="DefaultPollingInterval" />.</param> /// <param name="taskWaitingStrategy">OPTIONAL strategy on how to wait; DEFAULT is <see cref="TaskWaitingStrategy.Sleep" />.</param> /// <returns> /// Return value of specified task. /// </returns> public static T RunUntilCompletion <T>( this Task <T> task, TimeSpan pollingInterval = default, TaskWaitingStrategy taskWaitingStrategy = TaskWaitingStrategy.Sleep) { task.InternalRunUntilCompletion(pollingInterval, taskWaitingStrategy); return(task.Result); }
private static void InternalRunUntilCompletion( this Task task, TimeSpan pollingInterval, TaskWaitingStrategy taskWaitingStrategy) { try { if (task == null) { throw new ArgumentNullException(nameof(task)); } var localPollingTime = pollingInterval == default ? DefaultPollingInterval : pollingInterval; if (task.Status == TaskStatus.Created) { task.Start(); } // running this way because i want to interrogate afterwards to throw if faulted... while (!task.IsCompleted && !task.IsCanceled && !task.IsFaulted) { switch (taskWaitingStrategy) { case TaskWaitingStrategy.YieldAndSleep: Thread.Yield(); Thread.Sleep(pollingInterval); break; case TaskWaitingStrategy.Sleep: Thread.Sleep(pollingInterval); break; default: throw new NotSupportedException(Invariant($"Unsupported {nameof(TaskWaitingStrategy)} - {taskWaitingStrategy}")); } Thread.Sleep(localPollingTime); } task.IfFaultedTaskExtractAndThrowException(); } catch { // This is here intentionally as the TPL will sometimes do dumb things... throw; } }
/// <summary> /// Blocks on a task execution until it's in a <see cref="TaskStatus.Canceled" />, <see cref="TaskStatus.Faulted" />, or <see cref="TaskStatus.RanToCompletion" /> status, will start the task if in <see cref="TaskStatus.Created" />. /// </summary> /// <param name="task">Task to wait on.</param> /// <param name="pollingInterval">Optional time to poll and check status of task; DEFAULT is <see cref="DefaultPollingInterval" />.</param> /// <param name="taskWaitingStrategy">Optional strategy on how to wait; DEFAULT is <see cref="TaskWaitingStrategy.Sleep" />.</param> public static void TaskUntilCompletion(Task task, TimeSpan pollingInterval = default(TimeSpan), TaskWaitingStrategy taskWaitingStrategy = TaskWaitingStrategy.Sleep) { if (task == null) { throw new ArgumentNullException(nameof(task)); } async Task <string> UnnecessaryReturnTaskToReuseReturningObjectCodePath() { try { await task; task.IfNotCompletedThrowException(); task.IfFaultedTaskExtractAndThrowException(); // This result is not necessary but it is asserted to be the case later so those location must be congruent. // This is to allow for reusing a single code path for both returning and void tasks. return(string.Empty); } catch { // This is here intentionally as the TPL will sometimes do dumb things... throw; } } var unnecessaryResult = TaskUntilCompletion(UnnecessaryReturnTaskToReuseReturningObjectCodePath(), pollingInterval, taskWaitingStrategy); if (unnecessaryResult != string.Empty) { throw new InvalidOperationException(Invariant($"Task was run until completion but it was expected to be a void call and thus during the wrapping should have returned {typeof(string).Name}.{nameof(string.Empty)} but instead it returned '{unnecessaryResult}'.")); } }