/// <summary> /// Sets the <paramref name="taskCompletionSource"/> to fault with a <see cref="TimeoutException"/> after /// the given <paramref name="timeout"/>. Returns the input <paramref name="taskCompletionSource"/> /// </summary> public static TaskCompletionSource <TResult> TimeoutAfter <TResult>(this TaskCompletionSource <TResult> taskCompletionSource, TimeSpan timeout) { if (taskCompletionSource == null) { throw new ArgumentNullException(nameof(taskCompletionSource)); } timeout.AssertIsValidTimeout(); if (timeout != Timeout.InfiniteTimeSpan) { var timeoutCanceler = new CancellationTokenSource(); var timeoutTask = Task.Delay(timeout, timeoutCanceler.Token); // timeout cancels the source timeoutTask.SynchronousContinueWith( (task, state) => ((TaskCompletionSource <TResult>)state).TrySetException(new TimeoutException("Timeout of " + timeout + " expired!")), state: taskCompletionSource, continuationOptions: TaskContinuationOptions.OnlyOnRanToCompletion ); // source cleans up the timeout taskCompletionSource.Task.SynchronousContinueWith( (task, state) => ((CancellationTokenSource)state).Cancel(), state: timeoutCanceler ); } return(taskCompletionSource); }
/// <summary> /// Asynchronously waits up to <paramref name="timeout"/> for <paramref name="task"/> /// to complete. The result of the returned <see cref="Task{TResult}"/> indicates whether /// or not the <paramref name="task"/> completed /// </summary> public static Task <bool> WaitAsync(this Task task, TimeSpan timeout) { if (task == null) { throw new ArgumentNullException(nameof(task)); } timeout.AssertIsValidTimeout(); if (task.IsCompleted) { return(TrueTask); // hot path } return(InternalWaitAsync(task, timeout)); }