/// <summary> /// Execute a continuation task when a task completes. The continuation /// task is synchronously created by a continuation function, and then unwrapped to /// form the result of this method. The <paramref name="supportsErrors"/> /// parameter specifies whether the continuation is executed if the antecedent task is faulted. /// </summary> /// <remarks> /// <para>If the antecedent <paramref name="task"/> is cancelled, or faulted with <paramref name="supportsErrors"/> /// set to <see langword="false"/>, the status /// of the antecedent is directly applied to the task returned by this method; it is /// not wrapped in an additional <see cref="AggregateException"/>. /// </para> /// /// <note type="caller"> /// Since the <paramref name="continuationFunction"/> is executed synchronously, this /// method should only be used for lightweight continuation functions. This restriction /// applies only to <paramref name="continuationFunction"/> itself, not to the /// <see cref="Task"/> returned by it. /// </note> /// </remarks> /// <typeparam name="TSource">The type of the result produced by the antecedent <see cref="Task{TResult}"/>.</typeparam> /// <param name="task">The antecedent task.</param> /// <param name="continuationFunction">The continuation function to execute when <paramref name="task"/> completes. The continuation function returns a <see cref="Task"/> which provides the final result of the continuation.</param> /// <param name="supportsErrors"><see langword="true"/> if the <paramref name="continuationFunction"/> properly handles a faulted antecedent task; otherwise, <see langword="false"/>.</param> /// <returns>A <see cref="Task"/> representing the unwrapped asynchronous operation.</returns> /// <exception cref="ArgumentNullException"> /// If <paramref name="task"/> is <see langword="null"/>. /// <para>-or-</para> /// <para>If <paramref name="continuationFunction"/> is <see langword="null"/>.</para> /// </exception> public static Task Then <TSource>(this Task <TSource> task, Func <Task <TSource>, Task> continuationFunction, bool supportsErrors) { if (task == null) { throw new ArgumentNullException("task"); } if (continuationFunction == null) { throw new ArgumentNullException("continuationFunction"); } TaskCompletionSource <VoidResult> completionSource = new TaskCompletionSource <VoidResult>(); TaskContinuationOptions successContinuationOptions = supportsErrors ? TaskContinuationOptions.NotOnCanceled : TaskContinuationOptions.OnlyOnRanToCompletion; task .ContinueWith(continuationFunction, TaskContinuationOptions.ExecuteSynchronously | successContinuationOptions) .Unwrap() .ContinueWith( t => { if (task.Status == TaskStatus.RanToCompletion || supportsErrors && task.Status == TaskStatus.Faulted) { completionSource.SetFromTask(t); } }, TaskContinuationOptions.ExecuteSynchronously); TaskContinuationOptions failedContinuationOptions = supportsErrors ? TaskContinuationOptions.OnlyOnCanceled : TaskContinuationOptions.NotOnRanToCompletion; task .ContinueWith(t => completionSource.SetFromTask(t), TaskContinuationOptions.ExecuteSynchronously | failedContinuationOptions); return(completionSource.Task); }
/// <summary> /// Aa extension method suggesting a possible (but far from real implementation) /// of the "Unwrap" extension method. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="task"></param> /// <returns></returns> public static Task <T> Unwrap2 <T>(this Task <Task <T> > task) { TaskCompletionSource <T> tcs = new TaskCompletionSource <T>(); task.ContinueWith((ant) => { switch (ant.Status) { case TaskStatus.Canceled: tcs.TrySetCanceled(); break; case TaskStatus.Faulted: tcs.TrySetException(ant.Exception.InnerExceptions); break; case TaskStatus.RanToCompletion: Task <T> tr = ant.Result; tr.ContinueWith((ant2) => { tcs.SetFromTask(ant2); }); break; } }); return(tcs.Task); }
/// <summary> /// Switches the GUI into "waiting" mode, then calls <paramref name="taskCreation"/> to create /// the task. /// If another task is started before the previous task finishes running, the previous task is cancelled. /// </summary> public Task<T> RunWithCancellation<T>(Func<CancellationToken, Task<T>> taskCreation) { if (waitAdorner.Visibility != Visibility.Visible) { waitAdorner.Visibility = Visibility.Visible; // Work around a WPF bug by setting IsIndeterminate only while the progress bar is visible. // https://github.com/icsharpcode/ILSpy/issues/593 progressBar.IsIndeterminate = true; waitAdorner.BeginAnimation(OpacityProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(0.5)), FillBehavior.Stop)); var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) { taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Indeterminate; } } CancellationTokenSource previousCancellationTokenSource = currentCancellationTokenSource; var myCancellationTokenSource = new CancellationTokenSource(); currentCancellationTokenSource = myCancellationTokenSource; // cancel the previous only after current was set to the new one (avoid that the old one still finishes successfully) if (previousCancellationTokenSource != null) previousCancellationTokenSource.Cancel(); var tcs = new TaskCompletionSource<T>(); Task<T> task; try { task = taskCreation(myCancellationTokenSource.Token); } catch (OperationCanceledException) { task = TaskHelper.FromCancellation<T>(); } catch (Exception ex) { task = TaskHelper.FromException<T>(ex); } Action continuation = delegate { try { if (currentCancellationTokenSource == myCancellationTokenSource) { currentCancellationTokenSource = null; waitAdorner.Visibility = Visibility.Collapsed; progressBar.IsIndeterminate = false; var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) { taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; } if (task.IsCanceled) { AvalonEditTextOutput output = new AvalonEditTextOutput(); output.WriteLine("The operation was canceled."); ShowOutput(output); } tcs.SetFromTask(task); } else { tcs.SetCanceled(); } } finally { myCancellationTokenSource.Dispose(); } }; task.ContinueWith(delegate { Dispatcher.BeginInvoke(DispatcherPriority.Normal, continuation); }); return tcs.Task; }
static IEnumerable <Task> _getResponseAsync(TaskCompletionSource <WebResponse> tcs, WebRequest request, TimeSpan timeout) { using (var cancellation_token = new Concurrency.TimeoutToken(timeout)) using (var registration_token = cancellation_token.Token.Register(() => { request.Abort(); })) { using (var task_get_response = request.GetResponseAsync()) { yield return(task_get_response); tcs.SetFromTask(task_get_response); yield break; } } }
public static Task ConvertAsync(string sourceFileName, string destinationFileName, CancellationToken cancellationToken) { // Wire a graph together. var source = File.OpenRead(sourceFileName); var decoder = CodecFactory.CreateDecoder(sourceFileName); var encoder = CodecFactory.CreateEncoder(destinationFileName); var destination = File.Create(destinationFileName); const int bufferSize = 16384; decoder.ErrorDataReceived += (sender, e) => { Console.WriteLine("{0}: {1}", sourceFileName, e.Data); }; var decoderTask = decoder.Start(cancellationToken); // Note that the process has to be started before you can get the stream, // otherwise you get InvalidOperationException containing "StandardIn has not been redirected.". var sourceToDecoderTask = source.CopyToAsync(decoder.InputStream, bufferSize, cancellationToken) .ContinueWith(t => decoder.InputStream.Close()); encoder.ErrorDataReceived += (sender, e) => { Console.WriteLine("{0}: {1}", destinationFileName, e.Data); }; var encoderTask = encoder.Start(cancellationToken); var decoderToEncoderTask = decoder.OutputStream .CopyToAsync(encoder.InputStream, bufferSize, cancellationToken) .ContinueWith(t => encoder.InputStream.Close()); var encoderToDestination = encoder.OutputStream .CopyToAsync(destination, bufferSize, cancellationToken) .ContinueWith(t => destination.Close()); // We need to propagate the cancelation/exception; use a TCS. var completion = new TaskCompletionSource<bool>(); Task.WhenAll(sourceToDecoderTask, decoderTask, decoderToEncoderTask, encoderTask, encoderToDestination) .ContinueWith(t => { if (t.IsCanceled || t.IsFaulted) { Console.WriteLine("Deleting '{0}'.", destinationFileName); File.Delete(destinationFileName); } completion.SetFromTask(t); }); return completion.Task; }
/// <summary> /// <paramref name="task"/>가 완료되면, <paramref name="callback"/>을 호출하도록 예약된 새로운 Task를 생성하여, /// <paramref name="task"/>의 수행 결과를 설정한 후 제공합니다. /// </summary> /// <param name="task">실제 수행할 작업</param> /// <param name="callback">작업 후 호출할 callback 함수</param> /// <param name="state">callback 함수 호출 시 인자값</param> /// <returns>작업 완료와 callback 호출까지 모두를 포함한 작업</returns> public static Task <TResult> ToAsync <TResult>(this Task <TResult> task, AsyncCallback callback = null, object state = null) { task.ShouldNotBeNull("task"); var tcs = new TaskCompletionSource <TResult>(state); task.ContinueWith(_ => { tcs.SetFromTask(task); if (callback != null) { callback(tcs.Task); } }); return(tcs.Task); }
public static Task <TResult> ToAsync <TResult>(this Task <TResult> task, AsyncCallback callback, object state) { if (task == null) { throw new ArgumentNullException("task"); } var tcs = new TaskCompletionSource <TResult>(state); task.ContinueWith(delegate { tcs.SetFromTask(task); callback?.Invoke(tcs.Task); }); return(tcs.Task); }
/// <summary> /// <paramref name="millisecondsDelayed"/> 이후에 <paramref name="function"/>을 수행하는 작업을 생성합니다. /// </summary> /// <typeparam name="TResult">작업 결과 수형</typeparam> /// <param name="factory">Task Factory</param> /// <param name="millisecondsDelayed">실행 전에 지연할 시간(밀리초)</param> /// <param name="function">실행할 함수</param> /// <param name="state">함수 인자 값</param> /// <param name="cancellationToken">작업 취소용 토큰</param> /// <param name="creationOptions">작업 생성 옵션</param> /// <param name="scheduler">작업 스케쥴러</param> /// <returns>일정 시간 이후에 함수를 실행하는 작업</returns> public static Task <TResult> StartNewDelayed <TResult>(this TaskFactory <TResult> factory, int millisecondsDelayed, Func <object, TResult> function, object state = null, CancellationToken?cancellationToken = null, TaskCreationOptions?creationOptions = null, TaskScheduler scheduler = null) { factory.ShouldNotBeNull("factory"); millisecondsDelayed.ShouldBePositiveOrZero("millisecondsDelayed"); function.ShouldNotBeNull("function"); scheduler = scheduler ?? factory.GetTargetScheduler(); if (IsDebugEnabled) { log.Debug("일정 시간 이후에 지정한 함수를 수행하는 작업을 생성합니다. 작업지연 시간=[{0}] msecs", millisecondsDelayed); } var tcs = new TaskCompletionSource <TResult>(state); Timer timer = null; // 주어진 함수를 수행할 Task를 정의 var functionTask = new Task <TResult>(function, state, creationOptions ?? factory.CreationOptions); functionTask.ContinueWith(antecedent => { // tcs의 작업 결과를 antecedent.Result로 설정합니다. tcs.SetFromTask(antecedent); if (timer != null) { timer.Dispose(); } }, cancellationToken ?? factory.CancellationToken, ContinuationOptionsFromCreationOptions(creationOptions ?? factory.CreationOptions) | TaskContinuationOptions.ExecuteSynchronously, scheduler); // 이렇게 타이머의 Time Callback 함수에서, 사용자 작업을 수행하도록 한다. timer = new Timer(task => ((Task)task).Start(scheduler), functionTask, millisecondsDelayed, Timeout.Infinite); return(tcs.Task); }
public static Task ToAsync(this Task task, AsyncCallback callback, object state) { if (task == null) { throw new ArgumentNullException("task"); } TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(state); task.ContinueWith(delegate(Task _) { tcs.SetFromTask(task); if (callback != null) { callback(tcs.Task); } }); return(tcs.Task); }
/// <summary> /// Creates a Task that represents the completion of another Task, and /// that schedules an AsyncCallback to run upon completion. /// </summary> /// <param name="task">The antecedent Task.</param> /// <param name="callback">The AsyncCallback to run.</param> /// <param name="state">The object state to use with the AsyncCallback.</param> /// <returns>The new task.</returns> public static Task <TResult> ToAsync <TResult>(this Task <TResult> task, AsyncCallback callback, object state) { if (task == null) { throw new ArgumentNullException(nameof(task)); } TaskCompletionSource <TResult> tcs = new TaskCompletionSource <TResult>(state); task.ContinueWith(_ => { tcs.SetFromTask(task); if (callback == null) { return; } callback(tcs.Task); }); return(tcs.Task); }
/// <summary> /// Creates a Task that represents the completion of another Task, and /// that schedules an AsyncCallback to run upon completion. /// </summary> /// <param name="task">The antecedent Task.</param> /// <param name="callback">The AsyncCallback to run.</param> /// <param name="state">The object state to use with the AsyncCallback.</param> /// <returns>The new task.</returns> public static Task ToAsync(this Task task, AsyncCallback callback, object state) { if (task == null) { throw new ArgumentNullException(nameof(task)); } TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(state); task.ContinueWith((Action <Task>)(_ => { tcs.SetFromTask <object>(task); if (callback == null) { return; } callback((IAsyncResult)tcs.Task); })); return((Task)tcs.Task); }
/// <summary>Creates and schedules a task for execution after the specified time delay.</summary> /// <param name="factory">The factory to use to create the task.</param> /// <param name="millisecondsDelay">The delay after which the task will be scheduled.</param> /// <param name="function">The delegate executed by the task.</param> /// <param name="state">An object provided to the delegate.</param> /// <param name="cancellationToken">The CancellationToken to assign to the Task.</param> /// <param name="creationOptions">Options that control the task's behavior.</param> /// <param name="scheduler">The scheduler to which the Task will be scheduled.</param> /// <returns>The created Task.</returns> public static Task <TResult> StartNewDelayed <TResult>( this TaskFactory <TResult> factory, int millisecondsDelay, Func <object, TResult> function, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) { if (factory == null) { throw new ArgumentNullException("factory"); } if (millisecondsDelay < 0) { throw new ArgumentOutOfRangeException("millisecondsDelay"); } if (function == null) { throw new ArgumentNullException("action"); } if (scheduler == null) { throw new ArgumentNullException("scheduler"); } // Create the task that will be returned var result = new TaskCompletionSource <TResult>(state); Timer timer = null; // Create the task that will run the user's function var functionTask = new Task <TResult>(function, state, creationOptions); // When the function task completes, transfer the results to the returned task functionTask.ContinueWith(t => { result.SetFromTask(t); timer.Dispose(); }, cancellationToken, ContinuationOptionsFromCreationOptions(creationOptions) | TaskContinuationOptions.ExecuteSynchronously, scheduler); // Start the timer for the trigger timer = new Timer(obj => ((Task)obj).Start(scheduler), functionTask, millisecondsDelay, Timeout.Infinite); return(result.Task); }
/// <summary> /// <paramref name="task"/>가 완료되면, <paramref name="callback"/>을 호출하도록 예약된 새로운 Task를 생성하여, /// <paramref name="task"/>의 수행 결과를 설정한 후 제공합니다. /// </summary> /// <param name="task">실제 수행할 작업</param> /// <param name="callback">작업 후 호출할 callback 함수</param> /// <param name="state">callback 함수 호출 시 인자값</param> /// <returns>작업 완료와 callback 호출까지 모두를 포함한 작업</returns> public static Task ToAsync(this Task task, AsyncCallback callback = null, object state = null) { task.ShouldNotBeNull("task"); if (IsDebugEnabled) { log.Debug("지정된 Task가 완료되면, callback을 호출하도록 하는 새로운 Task를 빌드합니다."); } var tcs = new TaskCompletionSource <object>(state); task.ContinueWith(_ => { tcs.SetFromTask(task); if (callback != null) { callback(tcs.Task); } }, TaskContinuationOptions.ExecuteSynchronously); return(tcs.Task); }
/// <summary> /// Creates an <see cref="IAsyncResult"/> object that's suitable to be returned from an APM-style method. /// </summary> /// <typeparam name="T">The type of value being returned by the EndXxx APM method.</typeparam> /// <param name="task">The task that produces the value to be returned.</param> /// <param name="callback">The <see cref="AsyncCallback"/> supplied to the BeginXxx APM method.</param> /// <param name="state">The state object supplied to the BeginXxx APM method.</param> /// <returns>An <see cref="IAsyncResult"/> object that can be returned from an APM-style BeginXxx method.</returns> public static IAsyncResult CreateAsyncResult <T>(this Task <T> task, AsyncCallback callback, object state) { if (task == null) { throw new ArgumentNullException("task"); } // create result object that can hold the asynchronously-computed value TaskCompletionSource <T> result = new TaskCompletionSource <T>(state); // set the result (or failure) when the value is known task.ContinueWith(t => { result.SetFromTask(t); if (callback != null) { callback(result.Task); } }); // the result's task functions as the IAsyncResult APM return value return(result.Task); }
/// <summary> /// A variant of the previous method but now returning a Task without result /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="U"></typeparam> /// <param name="t"></param> /// <param name="cont"></param> /// <returns></returns> public static Task AndThen <T>(this Task <T> t, Func <T, Task> cont) { TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(); t.ContinueWith((ant) => { switch (ant.Status) { case TaskStatus.Canceled: tcs.TrySetCanceled(); break; case TaskStatus.Faulted: tcs.SetException(ant.Exception); break; case TaskStatus.RanToCompletion: cont(ant.Result).ContinueWith((ant2) => { tcs.SetFromTask(ant2); }, TaskContinuationOptions.ExecuteSynchronously); break; } }, TaskContinuationOptions.ExecuteSynchronously); return(tcs.Task); }
/// <summary> /// Get all pages in a paginated collection. /// </summary> /// <remarks> /// If <paramref name="progress"/> is non-<see langword="null"/>, the first call to /// <see cref="IProgress{T}.Report"/> will specify the <paramref name="page"/> /// argument. After each task to obtain to the next page of results completes, /// the <see cref="IProgress{T}.Report"/> method will be called again with the /// new page of results. /// <para> /// This method determines that the end of the collection is reached when either of /// the following conditions is true. /// </para> /// <list type="bullet"> /// <item>The <see cref="ReadOnlyCollectionPage{T}.CanHaveNextPage"/> property returns <see langword="false"/>.</item> /// <item>An empty page is reached.</item> /// </list> /// </remarks> /// <typeparam name="T">The type of elements in the collection.</typeparam> /// <param name="page">The first page in the collection.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param> /// <param name="progress">An optional callback object to receive progress notifications. If this is <see langword="null"/>, no progress notifications are sent.</param> /// <returns> /// A <see cref="Task"/> object representing the asynchronous operation. When the operation /// completes successfully, the <see cref="Task{TResult}.Result"/> property will contain a /// read-only collection containing the complete set of results from the paginated collection. /// </returns> /// <exception cref="ArgumentNullException">If <paramref name="page"/> is <see langword="null"/>.</exception> public static Task <ReadOnlyCollection <T> > GetAllPagesAsync <T>(this ReadOnlyCollectionPage <T> page, CancellationToken cancellationToken, IProgress <ReadOnlyCollectionPage <T> > progress) { if (page == null) { throw new ArgumentNullException("page"); } if (progress != null) { progress.Report(page); } if (!page.CanHaveNextPage || page.Count == 0) { return(InternalTaskExtensions.CompletedTask <ReadOnlyCollection <T> >(page)); } TaskCompletionSource <ReadOnlyCollection <T> > taskCompletionSource = new TaskCompletionSource <ReadOnlyCollection <T> >(); List <T> result = new List <T>(page); ReadOnlyCollectionPage <T> currentPage = page; Func <Task <ReadOnlyCollectionPage <T> > > getNextPage = () => currentPage.GetNextPageAsync(cancellationToken); Task <ReadOnlyCollectionPage <T> > currentTask = getNextPage(); Action <Task <ReadOnlyCollectionPage <T> > > continuation = null; continuation = previousTask => { if (previousTask.Status != TaskStatus.RanToCompletion) { taskCompletionSource.SetFromTask(previousTask); return; } currentPage = previousTask.Result; if (currentPage == null) { // TODO: should we throw an exception instead? taskCompletionSource.SetResult(result.AsReadOnly()); return; } if (progress != null) { progress.Report(currentPage); } result.AddRange(currentPage); if (!currentPage.CanHaveNextPage || currentPage.Count == 0) { taskCompletionSource.SetResult(result.AsReadOnly()); return; } // continue with the next page currentTask = getNextPage(); // use ContinueWith since the continuation handles cancellation and faulted antecedent tasks currentTask.ContinueWith(continuation, TaskContinuationOptions.ExecuteSynchronously); }; // use ContinueWith since the continuation handles cancellation and faulted antecedent tasks currentTask.ContinueWith(continuation, TaskContinuationOptions.ExecuteSynchronously); return(taskCompletionSource.Task); }
public static Task EncodeAsync(string sourceFileName, string destinationFileName, CancellationToken cancellationToken) { // Wire a graph together. var source = File.OpenRead(sourceFileName); var encoder = CodecFactory.CreateEncoder(destinationFileName); var destination = File.Create(destinationFileName); const int bufferSize = 16384; encoder.ErrorDataReceived += (sender, e) => { Console.WriteLine("{0}: {1}", destinationFileName, e.Data); }; var encoderTask = encoder.Start(cancellationToken); var sourceToEncoderTask = source.CopyToAsync(encoder.InputStream, bufferSize, cancellationToken) .ContinueWith(t => encoder.InputStream.Close()); var encoderToDestination = encoder.OutputStream .CopyToAsync(destination, bufferSize, cancellationToken) .ContinueWith(t => destination.Close()); // We need to propagate the cancelation/exception; use a TCS. var completion = new TaskCompletionSource<bool>(); Task.WhenAll(sourceToEncoderTask, encoderTask, encoderToDestination) .ContinueWith(t => { if (t.IsCanceled || t.IsFaulted) { Console.WriteLine("Deleting '{0}'.", destinationFileName); File.Delete(destinationFileName); } completion.SetFromTask(t); }); return completion.Task; }
public static void SetFromTask <TResult>(this TaskCompletionSource <TResult> resultSetter, Task <TResult> task) { resultSetter.SetFromTask(task); }
/// <summary> /// Creates a task that will complete after a time delay. /// </summary> /// <remarks> /// If the cancellation token is signaled before the specified time delay, then the task /// is completed in <see cref="TaskStatus.Canceled"/> state. Otherwise, the task is /// completed in <see cref="TaskStatus.RanToCompletion"/> state once the specified time /// delay has expired. /// <para>This method ignores any fractional milliseconds when evaluating <paramref name="delay"/>.</para> /// </remarks> /// <param name="delay">The time span to wait before completing the returned task</param> /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned task</param> /// <returns>A task that represents the time delay</returns> /// <exception cref="ArgumentOutOfRangeException">If <paramref name="delay"/> represents a negative time interval.</exception> /// <exception cref="TaskCanceledException">If the task has been canceled.</exception> /// <exception cref="ObjectDisposedException">If the provided <paramref name="cancellationToken"/> has already been disposed.</exception> public static Task Delay(TimeSpan delay, CancellationToken cancellationToken) { #if NET45PLUS return(Task.Delay(delay, cancellationToken)); #else long totalMilliseconds = (long)delay.TotalMilliseconds; if (totalMilliseconds < 0) { throw new ArgumentOutOfRangeException("delay"); } if (cancellationToken.IsCancellationRequested) { return(CompletedTask.Canceled()); } if (totalMilliseconds == 0) { return(CompletedTask.Default); } TaskCompletionSource <VoidResult> result = new TaskCompletionSource <VoidResult>(); #if !PORTABLE TaskCompletionSource <VoidResult> intermediateResult = new TaskCompletionSource <VoidResult>(); RegisteredWaitHandle timerRegisteredWaitHandle = null; RegisteredWaitHandle cancellationRegisteredWaitHandle = null; WaitOrTimerCallback timedOutCallback = (object state, bool timedOut) => { if (timedOut) { intermediateResult.TrySetResult(default(VoidResult)); } }; IAsyncResult asyncResult = intermediateResult.Task; timerRegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, timedOutCallback, null, delay, true); if (cancellationToken.CanBeCanceled) { WaitOrTimerCallback cancelledCallback = (object state, bool timedOut) => { if (cancellationToken.IsCancellationRequested) { intermediateResult.TrySetCanceled(); } }; cancellationRegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(cancellationToken.WaitHandle, cancelledCallback, null, Timeout.Infinite, true); } intermediateResult.Task .ContinueWith( _ => { if (cancellationRegisteredWaitHandle != null) { cancellationRegisteredWaitHandle.Unregister(null); } if (timerRegisteredWaitHandle != null) { timerRegisteredWaitHandle.Unregister(null); } }, TaskContinuationOptions.ExecuteSynchronously) .ContinueWith( cleanupTask => { switch (cleanupTask.Status) { case TaskStatus.RanToCompletion: result.SetFromTask(intermediateResult.Task); break; case TaskStatus.Canceled: result.SetCanceled(); break; case TaskStatus.Faulted: result.SetException(cleanupTask.Exception.InnerExceptions); break; default: throw new InvalidOperationException("Unreachable"); } }); #else // Since portable-net40 doesn't provide Task.Delay and also doesn't provide ThreadPool.RegisterWaitForSingleObject, // we need to implement this functionality using timers stored in a ConditionalWeakTable, which are associated with // the actual Task instance that gets returned by this method. CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); Timer timer = null; TimerCallback timerCallback = state => { result.TrySetResult(default(VoidResult)); timer.Dispose(); cancellationTokenRegistration.Dispose(); }; timer = new Timer(timerCallback, null, Timeout.Infinite, Timeout.Infinite); _delayTimers.Add(result.Task, timer); timer.Change(delay, TimeSpan.FromMilliseconds(-1)); if (cancellationToken.CanBeCanceled) { Action cancellationCallback = () => { result.TrySetCanceled(); if (timer != null) { timer.Dispose(); } cancellationTokenRegistration.Dispose(); }; cancellationTokenRegistration = cancellationToken.Register(cancellationCallback); } #endif return(result.Task); #endif }
static IEnumerable<Task> _getResponseAsync(TaskCompletionSource<WebResponse> tcs, WebRequest request, TimeSpan timeout) { using (var cancellation_token = new Concurrency.TimeoutToken(timeout)) using (var registration_token = cancellation_token.Token.Register(() => { request.Abort(); })) { using (var task_get_response = request.GetResponseAsync()) { yield return task_get_response; tcs.SetFromTask(task_get_response); yield break; } } }